From 674d64996860628ef518c10e8843796a116936d1 Mon Sep 17 00:00:00 2001 From: jheer Date: Fri, 8 Nov 2024 14:16:56 -0800 Subject: [PATCH 01/12] feat!: Rewrite SQL helpers. --- package-lock.json | 9 + packages/core/jsconfig.json | 11 + packages/core/package.json | 2 +- packages/core/src/Coordinator.js | 8 +- packages/core/src/QueryConsolidator.js | 40 +- packages/core/src/Selection.js | 9 +- packages/core/src/SelectionClause.js | 26 +- packages/core/src/index.js | 2 +- .../core/src/{ => preagg}/PreAggregator.js | 92 +-- packages/core/src/preagg/preagg-columns.js | 103 +++ .../core/src/preagg/sufficient-statistics.js | 439 +++++++++++++ packages/core/src/util/field-info.js | 21 +- packages/core/src/util/hash.js | 2 +- packages/core/src/util/preagg-columns.js | 537 ---------------- packages/core/src/util/selection-types.ts | 4 +- packages/core/src/util/throttle.js | 20 +- packages/core/test/coordinator.test.js | 2 +- packages/core/test/preaggregator.test.js | 107 ++-- packages/core/test/query-consolidator.test.js | 3 +- packages/core/test/query-manager.test.js | 4 +- packages/core/test/throttle.test.js | 4 +- packages/inputs/src/Table.js | 2 +- packages/plot/src/index.js | 2 +- packages/plot/src/interactors/Highlight.js | 6 +- packages/plot/src/interactors/util/brush.js | 4 +- .../plot/src/interactors/util/get-field.js | 11 +- packages/plot/src/marks/ConnectedMark.js | 43 +- packages/plot/src/marks/DenseLineMark.js | 92 +-- packages/plot/src/marks/Density1DMark.js | 24 +- packages/plot/src/marks/ErrorBarMark.js | 11 +- packages/plot/src/marks/Grid2DMark.js | 55 +- packages/plot/src/marks/HexbinMark.js | 70 ++- packages/plot/src/marks/Mark.js | 30 +- packages/plot/src/marks/RegressionMark.js | 6 +- packages/plot/src/marks/util/bin-expr.js | 13 +- packages/plot/src/marks/util/extent.js | 20 +- packages/plot/src/plot-renderer.js | 48 +- packages/plot/src/transforms/bin.js | 87 +-- packages/plot/src/transforms/index.js | 3 - packages/spec/src/ast/ExpressionNode.js | 34 +- packages/spec/src/ast/PlotMarkNode.js | 4 +- packages/spec/src/constants.js | 1 - packages/sql/jsconfig.json | 11 + packages/sql/package.json | 4 +- packages/sql/src/Query.js | 593 ------------------ packages/sql/src/aggregates.js | 185 ------ packages/sql/src/ast/aggregate.js | 164 +++++ packages/sql/src/ast/between-op.js | 75 +++ packages/sql/src/ast/binary-op.js | 40 ++ packages/sql/src/ast/case.js | 105 ++++ packages/sql/src/ast/cast.js | 34 + packages/sql/src/ast/column-ref.js | 46 ++ packages/sql/src/ast/fragment.js | 26 + packages/sql/src/ast/from.js | 40 ++ packages/sql/src/ast/function.js | 34 + packages/sql/src/ast/in-op.js | 33 + packages/sql/src/ast/interval.js | 33 + packages/sql/src/ast/literal.js | 55 ++ packages/sql/src/ast/logical-op.js | 67 ++ packages/sql/src/ast/node.js | 29 + packages/sql/src/ast/order-by.js | 48 ++ packages/sql/src/ast/param.js | 35 ++ packages/sql/src/ast/query.js | 578 +++++++++++++++++ packages/sql/src/ast/sample.js | 53 ++ packages/sql/src/ast/select.js | 44 ++ packages/sql/src/ast/table-ref.js | 44 ++ packages/sql/src/ast/unary-op.js | 64 ++ packages/sql/src/ast/verbatim.js | 26 + packages/sql/src/ast/window.js | 290 +++++++++ packages/sql/src/ast/with.js | 30 + packages/sql/src/cast.js | 19 - packages/sql/src/constants.js | 43 ++ packages/sql/src/datetime.js | 31 - packages/sql/src/desc.js | 13 - packages/sql/src/expression.js | 170 ----- packages/sql/src/functions.js | 25 - packages/sql/src/functions/aggregate.js | 335 ++++++++++ packages/sql/src/functions/case.js | 21 + packages/sql/src/functions/cast.js | 39 ++ packages/sql/src/functions/column.js | 13 + packages/sql/src/functions/datetime.js | 65 ++ packages/sql/src/functions/literal.js | 22 + packages/sql/src/functions/numeric.js | 139 ++++ packages/sql/src/functions/operators.js | 298 +++++++++ packages/sql/src/functions/order-by.js | 24 + packages/sql/src/functions/spatial.js | 56 ++ .../sql/src/functions/sql-template-tag.js | 51 ++ packages/sql/src/functions/string.js | 82 +++ packages/sql/src/functions/table-ref.js | 14 + packages/sql/src/functions/window.js | 121 ++++ packages/sql/src/index-types.ts | 2 + packages/sql/src/index.js | 207 ++---- packages/sql/src/literal.js | 6 - packages/sql/src/load/sql-from.js | 13 +- packages/sql/src/operators.js | 59 -- packages/sql/src/ref.js | 109 ---- packages/sql/src/repeat.js | 3 - packages/sql/src/spatial.js | 10 - packages/sql/src/to-sql.js | 52 -- packages/sql/src/transforms/bin-1d.js | 21 + packages/sql/src/transforms/bin-2d.js | 29 + packages/sql/src/transforms/bin-linear-1d.js | 26 + packages/sql/src/transforms/bin-linear-2d.js | 71 +++ packages/sql/src/transforms/line-density.js | 113 ++++ packages/sql/src/transforms/m4.js | 38 ++ packages/sql/src/{ => transforms}/scales.js | 48 +- packages/sql/src/types.ts | 96 +++ packages/sql/src/util/ast.js | 96 +++ packages/sql/src/util/function.js | 78 +++ packages/sql/src/util/string.js | 16 + packages/sql/src/util/type-check.js | 29 + packages/sql/src/visit/recurse.js | 55 ++ packages/sql/src/visit/rewrite.js | 32 + packages/sql/src/visit/visitors.js | 103 +++ packages/sql/src/visit/walk.js | 30 + packages/sql/src/windows.js | 239 ------- packages/sql/test/aggregate.test.js | 241 ++++--- packages/sql/test/cast.test.js | 49 +- packages/sql/test/create.test.js | 2 +- packages/sql/test/desc.test.js | 27 - packages/sql/test/function.test.js | 78 --- packages/sql/test/literal/test.js | 65 -- packages/sql/test/numeric.test.js | 27 + .../{operator.test.js => operators.test.js} | 64 +- packages/sql/test/order-by.test.js | 60 ++ packages/sql/test/query.test.js | 109 ++-- ...xpression.test.js => sql-template.test.js} | 47 +- packages/sql/test/string.test.js | 61 ++ packages/sql/test/util/columns.js | 9 + packages/sql/test/{ => util}/stub-param.js | 2 +- packages/sql/test/visitors.test.js | 43 ++ packages/sql/test/window.test.js | 130 ++-- packages/sql/tsconfig.json | 21 +- packages/vgplot/src/api.js | 6 +- 134 files changed, 5773 insertions(+), 3227 deletions(-) create mode 100644 packages/core/jsconfig.json rename packages/core/src/{ => preagg}/PreAggregator.js (81%) create mode 100644 packages/core/src/preagg/preagg-columns.js create mode 100644 packages/core/src/preagg/sufficient-statistics.js delete mode 100644 packages/core/src/util/preagg-columns.js delete mode 100644 packages/plot/src/transforms/index.js create mode 100644 packages/sql/jsconfig.json delete mode 100644 packages/sql/src/Query.js delete mode 100644 packages/sql/src/aggregates.js create mode 100644 packages/sql/src/ast/aggregate.js create mode 100644 packages/sql/src/ast/between-op.js create mode 100644 packages/sql/src/ast/binary-op.js create mode 100644 packages/sql/src/ast/case.js create mode 100644 packages/sql/src/ast/cast.js create mode 100644 packages/sql/src/ast/column-ref.js create mode 100644 packages/sql/src/ast/fragment.js create mode 100644 packages/sql/src/ast/from.js create mode 100644 packages/sql/src/ast/function.js create mode 100644 packages/sql/src/ast/in-op.js create mode 100644 packages/sql/src/ast/interval.js create mode 100644 packages/sql/src/ast/literal.js create mode 100644 packages/sql/src/ast/logical-op.js create mode 100644 packages/sql/src/ast/node.js create mode 100644 packages/sql/src/ast/order-by.js create mode 100644 packages/sql/src/ast/param.js create mode 100644 packages/sql/src/ast/query.js create mode 100644 packages/sql/src/ast/sample.js create mode 100644 packages/sql/src/ast/select.js create mode 100644 packages/sql/src/ast/table-ref.js create mode 100644 packages/sql/src/ast/unary-op.js create mode 100644 packages/sql/src/ast/verbatim.js create mode 100644 packages/sql/src/ast/window.js create mode 100644 packages/sql/src/ast/with.js delete mode 100644 packages/sql/src/cast.js create mode 100644 packages/sql/src/constants.js delete mode 100644 packages/sql/src/datetime.js delete mode 100644 packages/sql/src/desc.js delete mode 100644 packages/sql/src/expression.js delete mode 100644 packages/sql/src/functions.js create mode 100644 packages/sql/src/functions/aggregate.js create mode 100644 packages/sql/src/functions/case.js create mode 100644 packages/sql/src/functions/cast.js create mode 100644 packages/sql/src/functions/column.js create mode 100644 packages/sql/src/functions/datetime.js create mode 100644 packages/sql/src/functions/literal.js create mode 100644 packages/sql/src/functions/numeric.js create mode 100644 packages/sql/src/functions/operators.js create mode 100644 packages/sql/src/functions/order-by.js create mode 100644 packages/sql/src/functions/spatial.js create mode 100644 packages/sql/src/functions/sql-template-tag.js create mode 100644 packages/sql/src/functions/string.js create mode 100644 packages/sql/src/functions/table-ref.js create mode 100644 packages/sql/src/functions/window.js create mode 100644 packages/sql/src/index-types.ts delete mode 100644 packages/sql/src/literal.js delete mode 100644 packages/sql/src/operators.js delete mode 100644 packages/sql/src/ref.js delete mode 100644 packages/sql/src/repeat.js delete mode 100644 packages/sql/src/spatial.js delete mode 100644 packages/sql/src/to-sql.js create mode 100644 packages/sql/src/transforms/bin-1d.js create mode 100644 packages/sql/src/transforms/bin-2d.js create mode 100644 packages/sql/src/transforms/bin-linear-1d.js create mode 100644 packages/sql/src/transforms/bin-linear-2d.js create mode 100644 packages/sql/src/transforms/line-density.js create mode 100644 packages/sql/src/transforms/m4.js rename packages/sql/src/{ => transforms}/scales.js (54%) create mode 100644 packages/sql/src/types.ts create mode 100644 packages/sql/src/util/ast.js create mode 100644 packages/sql/src/util/function.js create mode 100644 packages/sql/src/util/string.js create mode 100644 packages/sql/src/util/type-check.js create mode 100644 packages/sql/src/visit/recurse.js create mode 100644 packages/sql/src/visit/rewrite.js create mode 100644 packages/sql/src/visit/visitors.js create mode 100644 packages/sql/src/visit/walk.js delete mode 100644 packages/sql/src/windows.js delete mode 100644 packages/sql/test/desc.test.js delete mode 100644 packages/sql/test/function.test.js delete mode 100644 packages/sql/test/literal/test.js create mode 100644 packages/sql/test/numeric.test.js rename packages/sql/test/{operator.test.js => operators.test.js} (63%) create mode 100644 packages/sql/test/order-by.test.js rename packages/sql/test/{expression.test.js => sql-template.test.js} (50%) create mode 100644 packages/sql/test/string.test.js create mode 100644 packages/sql/test/util/columns.js rename packages/sql/test/{ => util}/stub-param.js (89%) create mode 100644 packages/sql/test/visitors.test.js diff --git a/package-lock.json b/package-lock.json index 7e09d9cd..4e22f976 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4184,6 +4184,10 @@ "resolved": "packages/sql", "link": true }, + "node_modules/@uwdata/mosaic-sql-old": { + "resolved": "packages/sql-old", + "link": true + }, "node_modules/@uwdata/vgplot": { "resolved": "packages/vgplot", "link": true @@ -15173,6 +15177,11 @@ "version": "0.11.0", "license": "BSD-3-Clause" }, + "packages/sql-old": { + "name": "@uwdata/mosaic-sql-old", + "version": "0.11.0", + "license": "BSD-3-Clause" + }, "packages/vega-example": { "name": "mosaic-vega-example", "version": "0.11.0", diff --git a/packages/core/jsconfig.json b/packages/core/jsconfig.json new file mode 100644 index 00000000..270e9f5a --- /dev/null +++ b/packages/core/jsconfig.json @@ -0,0 +1,11 @@ +{ + "include": ["src/**/*"], + "compilerOptions": { + "checkJs": true, + "noEmit": true, + "noImplicitAny": false, + "module": "node16", + "skipLibCheck": true, + "types": [] + } +} \ No newline at end of file diff --git a/packages/core/package.json b/packages/core/package.json index 073997b3..989e0efd 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -26,7 +26,7 @@ "build": "npm run types && node ../../esbuild.js mosaic-core", "types": "tsc", "lint": "eslint src test", - "test": "vitest run --dangerouslyIgnoreUnhandledErrors", + "test": "vitest run && tsc -p jsconfig.json", "prepublishOnly": "npm run test && npm run lint && npm run build" }, "dependencies": { diff --git a/packages/core/src/Coordinator.js b/packages/core/src/Coordinator.js index 8cc135dd..524a4237 100644 --- a/packages/core/src/Coordinator.js +++ b/packages/core/src/Coordinator.js @@ -1,10 +1,10 @@ import { socketConnector } from './connectors/socket.js'; -import { PreAggregator } from './PreAggregator.js'; -import { MosaicClient } from './MosaicClient.js'; -import { QueryManager, Priority } from './QueryManager.js'; +import { PreAggregator } from './preagg/PreAggregator.js'; import { queryFieldInfo } from './util/field-info.js'; import { QueryResult } from './util/query-result.js'; import { voidLogger } from './util/void-logger.js'; +import { MosaicClient } from './MosaicClient.js'; +import { QueryManager, Priority } from './QueryManager.js'; /** * The singleton Coordinator instance. @@ -40,7 +40,7 @@ export function coordinator(instance) { * @param {*} [options.manager] The query manager to use. * @param {boolean} [options.cache=true] Boolean flag to enable/disable query caching. * @param {boolean} [options.consolidate=true] Boolean flag to enable/disable query consolidation. - * @param {import('./PreAggregator.js').PreAggregateOptions} [options.preagg] + * @param {import('./preagg/PreAggregator.js').PreAggregateOptions} [options.preagg] * Options for the Pre-aggregator. */ export class Coordinator { diff --git a/packages/core/src/QueryConsolidator.js b/packages/core/src/QueryConsolidator.js index 788c974e..f2d86bef 100644 --- a/packages/core/src/QueryConsolidator.js +++ b/packages/core/src/QueryConsolidator.js @@ -1,4 +1,4 @@ -import { Query, Ref, isDescribeQuery } from '@uwdata/mosaic-sql'; +import { DescribeQuery, isAggregateExpression, isColumnRef, isDescribeQuery, isSelectQuery, Query } from '@uwdata/mosaic-sql'; import { QueryResult } from './util/query-result.js'; function wait(callback) { @@ -75,18 +75,16 @@ function entryGroups(entries, cache) { * Queries with matching keys are conosolidation-compatible. * If a query is found in the cache, it is exempted from consolidation, * which is indicated by returning the precise query SQL as the key. - * @param {*} query The input query. + * @param {Query | DescribeQuery} query The input query. * @param {*} cache The query cache (sql -> data). * @returns a key string */ function consolidationKey(query, cache) { const sql = `${query}`; - if (query instanceof Query && !cache.get(sql)) { + if (isSelectQuery(query) && !cache.get(sql)) { if ( - // @ts-ignore - query.orderby().length || query.where().length || - // @ts-ignore - query.qualify().length || query.having().length + query._orderby.length || query._where.length || + query._qualify.length || query._having.length ) { // do not try to analyze if query includes clauses // that may refer to *derived* columns we can't resolve @@ -94,25 +92,21 @@ function consolidationKey(query, cache) { } // create a derived query stripped of selections - const q = query.clone().$select('*'); + const q = query.clone().setSelect('*'); // check group by criteria for compatibility // queries may refer to *derived* columns as group by criteria // we resolve these against the true grouping expressions - const groupby = query.groupby(); - // @ts-ignore + const groupby = query._groupby; if (groupby.length) { - const map = {}; // expression map (as -> expr) - // @ts-ignore - query.select().forEach(({ as, expr }) => map[as] = expr); - // @ts-ignore - q.$groupby(groupby.map(e => (e instanceof Ref && map[e.column]) || e)); + const map = {}; // expression map (alias -> expr) + query._select.forEach(({ alias, expr }) => map[alias] = expr); + q.setGroupby(groupby.map(e => (isColumnRef(e) && map[e.column]) || e)); } - // @ts-ignore - else if (query.select().some(({ expr }) => expr.aggregate)) { + else if (query._select.some(e => isAggregateExpression(e.expr))) { // if query is an ungrouped aggregate, add an explicit groupby to // prevent improper consolidation with non-aggregate queries - q.$groupby('ALL'); + q.setGroupby('ALL'); } // key is just the transformed query as SQL @@ -182,13 +176,13 @@ function consolidatedQuery(group, record) { const { query } = item.entry.request; const fieldMap = []; maps.push(fieldMap); - for (const { as, expr } of query.select()) { + for (const { alias, expr } of query._select) { const e = `${expr}`; if (!fields.has(e)) { fields.set(e, [`col${fields.size}`, expr]); } const [name] = fields.get(e); - fieldMap.push([name, as]); + fieldMap.push([name, alias]); } record(`${query}`); } @@ -197,15 +191,15 @@ function consolidatedQuery(group, record) { const query = group[0].entry.request.query.clone(); // update group by statement as needed - const groupby = query.groupby(); + const groupby = query._groupby; if (groupby.length) { const map = {}; group.maps[0].forEach(([name, as]) => map[as] = name); - query.$groupby(groupby.map(e => (e instanceof Ref && map[e.column]) || e)); + query.setGroupby(groupby.map(e => (isColumnRef(e) && map[e.column]) || e)); } // update select statement and return - return query.$select(Array.from(fields.values())); + return query.setSelect(Array.from(fields.values())); } /** diff --git a/packages/core/src/Selection.js b/packages/core/src/Selection.js index 7f96f420..bd77746a 100644 --- a/packages/core/src/Selection.js +++ b/packages/core/src/Selection.js @@ -1,5 +1,6 @@ import { literal, or } from '@uwdata/mosaic-sql'; import { Param } from './Param.js'; +import { MosaicClient } from './MosaicClient.js'; /** * Test if a value is a Selection instance. @@ -315,9 +316,11 @@ export class SelectionResolver { /** * Return a selection query predicate for the given client. - * @param {*[]} clauseList An array of selection clauses. - * @param {*} active The current active selection clause. - * @param {*} client The client whose data may be filtered. + * @param {import('./util/selection-types.js').SelectionClause[]} clauseList + * An array of selection clauses. + * @param {import('./util/selection-types.js').SelectionClause} active + * The current active selection clause. + * @param {MosaicClient} client The client whose data may be filtered. * @returns {*} The query predicate for filtering client data, * based on the current state of this selection. */ diff --git a/packages/core/src/SelectionClause.js b/packages/core/src/SelectionClause.js index ecf16f34..034bf101 100644 --- a/packages/core/src/SelectionClause.js +++ b/packages/core/src/SelectionClause.js @@ -1,7 +1,4 @@ -import { - SQLExpression, and, contains, isBetween, isIn, isNotDistinct, literal, - or, prefix, regexp_matches, suffix -} from '@uwdata/mosaic-sql'; +import { ExprNode, and, contains, isBetween, isIn, isNotDistinct, literal, or, prefix, regexp_matches, suffix } from '@uwdata/mosaic-sql'; import { MosaicClient } from './MosaicClient.js'; /** @@ -10,12 +7,11 @@ import { MosaicClient } from './MosaicClient.js'; * @typedef {import('./util/selection-types.js').Extent} Extent * @typedef {import('./util/selection-types.js').MatchMethod} MatchMethod * @typedef {import('./util/selection-types.js').BinMethod} BinMethod - * @typedef {SQLExpression | string} Field */ /** * Generate a selection clause for a single selected point value. - * @param {Field} field The table column or expression to select. + * @param {import('@uwdata/mosaic-sql').ExprValue} field The table column or expression to select. * @param {*} value The selected value. * @param {object} options Additional clause properties. * @param {*} options.source The source component generating this clause. @@ -28,7 +24,7 @@ export function clausePoint(field, value, { source, clients = source ? new Set([source]) : undefined }) { - /** @type {SQLExpression | null} */ + /** @type {ExprNode | null} */ const predicate = value !== undefined ? isNotDistinct(field, literal(value)) : null; @@ -43,7 +39,7 @@ export function clausePoint(field, value, { /** * Generate a selection clause for multiple selected point values. - * @param {Field[]} fields The table columns or expressions to select. + * @param {import('@uwdata/mosaic-sql').ExprValue[]} fields The table columns or expressions to select. * @param {any[][] | undefined} value The selected values, as an array of * arrays. Each subarray contains values for each *fields* entry. * @param {object} options Additional clause properties. @@ -57,7 +53,7 @@ export function clausePoints(fields, value, { source, clients = source ? new Set([source]) : undefined }) { - /** @type {SQLExpression | null} */ + /** @type {ExprNode | null} */ let predicate = null; if (value) { const clauses = value.length && fields.length === 1 @@ -78,7 +74,7 @@ export function clausePoints(fields, value, { /** * Generate a selection clause for a selected 1D interval. - * @param {Field} field The table column or expression to select. + * @param {import('@uwdata/mosaic-sql').ExprValue} field The table column or expression to select. * @param {Extent} value The selected interval as a [lo, hi] array. * @param {object} options Additional clause properties. * @param {*} options.source The source component generating this clause. @@ -97,7 +93,7 @@ export function clauseInterval(field, value, { scale, pixelSize = 1 }) { - /** @type {SQLExpression | null} */ + /** @type {ExprNode | null} */ const predicate = value != null ? isBetween(field, value) : null; /** @type {import('./util/selection-types.js').IntervalMetadata} */ const meta = { type: 'interval', scales: scale && [scale], bin, pixelSize }; @@ -106,7 +102,7 @@ export function clauseInterval(field, value, { /** * Generate a selection clause for multiple selected intervals. - * @param {Field[]} fields The table columns or expressions to select. + * @param {import('@uwdata/mosaic-sql').ExprValue[]} fields The table columns or expressions to select. * @param {Extent[]} value The selected intervals, as an array of extents. * @param {object} options Additional clause properties. * @param {*} options.source The source component generating this clause. @@ -126,7 +122,7 @@ export function clauseIntervals(fields, value, { scales = [], pixelSize = 1 }) { - /** @type {SQLExpression | null} */ + /** @type {ExprNode | null} */ const predicate = value != null ? and(fields.map((f, i) => isBetween(f, value[i]))) : null; @@ -139,7 +135,7 @@ const MATCH_METHODS = { contains, prefix, suffix, regexp: regexp_matches }; /** * Generate a selection clause for text search matching. - * @param {Field} field The table column or expression to select. + * @param {import('@uwdata/mosaic-sql').ExprValue} field The table column or expression to select. * @param {string} value The selected text search query string. * @param {object} options Additional clause properties. * @param {*} options.source The source component generating this clause. @@ -154,7 +150,7 @@ export function clauseMatch(field, value, { source, clients = undefined, method = 'contains' }) { let fn = MATCH_METHODS[method]; - /** @type {SQLExpression | null} */ + /** @type {ExprNode | null} */ const predicate = value ? fn(field, literal(value)) : null; /** @type {import('./util/selection-types.js').MatchMetadata} */ const meta = { type: 'match', method }; diff --git a/packages/core/src/index.js b/packages/core/src/index.js index e40a4aef..3e182ac3 100644 --- a/packages/core/src/index.js +++ b/packages/core/src/index.js @@ -34,4 +34,4 @@ export { toDataColumns } from './util/to-data-columns.js'; * @typedef {import('./util/selection-types.js').BinMethod} BinMethod * @typedef {import('./util/selection-types.js').IntervalMetadata} IntervalMetadata * @typedef {import('./util/selection-types.js').SelectionClause} SelectionClause - */ \ No newline at end of file + */ diff --git a/packages/core/src/PreAggregator.js b/packages/core/src/preagg/PreAggregator.js similarity index 81% rename from packages/core/src/PreAggregator.js rename to packages/core/src/preagg/PreAggregator.js index 1a2b6651..985da608 100644 --- a/packages/core/src/PreAggregator.js +++ b/packages/core/src/preagg/PreAggregator.js @@ -1,8 +1,6 @@ -import { - Query, and, asColumn, createTable, isBetween, scaleTransform, sql -} from '@uwdata/mosaic-sql'; -import { preaggColumns } from './util/preagg-columns.js'; -import { fnv_hash } from './util/hash.js'; +import { Query, and, asNode, ceil, collectColumns, createTable, float64, floor, isBetween, int32, mul, round, scaleTransform, sub, isSelectQuery, ExprNode, SelectQuery } from '@uwdata/mosaic-sql'; +import { preaggColumns } from './preagg-columns.js'; +import { fnv_hash } from '../util/hash.js'; const Skip = { skip: true, result: null }; @@ -36,14 +34,14 @@ const Skip = { skip: true, result: null }; export class PreAggregator { /** * Create a new manager of materialized views of pre-aggregated data. - * @param {import('./Coordinator.js').Coordinator} coordinator A Mosaic coordinator. + * @param {import('../Coordinator.js').Coordinator} coordinator A Mosaic coordinator. * @param {PreAggregateOptions} [options] Pre-aggregation options. */ constructor(coordinator, { schema = 'mosaic', enabled = true } = {}) { - /** @type {Map} */ + /** @type {Map} */ this.entries = new Map(); this.active = null; this.mc = coordinator; @@ -125,10 +123,10 @@ export class PreAggregator { * client-selection pair, or null if the client has unstable filters. * This method has multiple possible side effects, including materialized * view creation and updating internal caches. - * @param {import('./MosaicClient.js').MosaicClient} client A Mosaic client. - * @param {import('./Selection.js').Selection} selection A Mosaic selection + * @param {import('../MosaicClient.js').MosaicClient} client A Mosaic client. + * @param {import('../Selection.js').Selection} selection A Mosaic selection * to filter the client by. - * @param {import('./util/selection-types.js').SelectionClause} activeClause + * @param {import('../util/selection-types.js').SelectionClause} activeClause * A representative active selection clause for which to (possibly) generate * materialized views of pre-aggregates. * @returns {PreAggregateInfo | Skip | null} Information and query generator @@ -203,13 +201,13 @@ export class PreAggregator { * function for the active dimensions of a pre-aggregated materialized view. * If the active clause is not indexable or is missing metadata, this method * returns an object with a null source property. - * @param {import('./util/selection-types.js').SelectionClause} clause + * @param {import('../util/selection-types.js').SelectionClause} clause * The active selection clause to analyze. */ function activeColumns(clause) { const { source, meta } = clause; const clausePred = clause.predicate; - const clauseCols = clausePred?.columns; + const clauseCols = collectColumns(clausePred).map(c => c.column); let predicate; let columns; @@ -223,7 +221,7 @@ function activeColumns(clause) { if (type === 'point') { predicate = x => x; columns = Object.fromEntries( - clauseCols.map(col => [`${col}`, asColumn(col)]) + clauseCols.map(col => [`${col}`, asNode(col)]) ); } else if (type === 'interval' && scales) { // determine pixel-level binning @@ -232,20 +230,22 @@ function activeColumns(clause) { if (bins.some(b => !b)) { // bail if a scale type is unsupported } else if (bins.length === 1) { + // selection clause predicate has type BetweenOpNode // single interval selection - predicate = p => p ? isBetween('active0', p.range.map(bins[0])) : []; + predicate = p => p ? isBetween('active0', p.extent.map(bins[0])) : []; // @ts-ignore - columns = { active0: bins[0](clausePred.field) }; + columns = { active0: bins[0](clausePred.expr) }; } else { + // selection clause predicate has type AndNode // multiple interval selection predicate = p => p - ? and(p.children.map( - ({ range }, i) => isBetween(`active${i}`, range.map(bins[i])) + ? and(p.clauses.map( + (c, i) => isBetween(`active${i}`, c.extent.map(bins[i])) )) : []; columns = Object.fromEntries( // @ts-ignore - clausePred.children.map((p, i) => [`active${i}`, bins[i](p.field)]) + clausePred.clauses.map((p, i) => [`active${i}`, bins[i](p.expr)]) ); } } @@ -253,57 +253,63 @@ function activeColumns(clause) { return { source: columns ? source : null, columns, predicate }; } -const BIN = { ceil: 'CEIL', round: 'ROUND' }; +const BIN = { ceil, round }; /** * Returns a bin function generator to discretize a selection interval domain. - * @param {import('./util/selection-types.js').Scale} scale A scale that maps + * @param {import('../util/selection-types.js').Scale} scale A scale that maps * domain values to the output range (typically pixels). * @param {number} pixelSize The interactive pixel size. This value indicates * the bin step size and may be greater than an actual screen pixel. - * @param {import('./util/selection-types.js').BinMethod} bin The binning + * @param {import('../util/selection-types.js').BinMethod} bin The binning * method to apply, one of `floor`, `ceil', or `round`. - * @returns {(value: any) => import('@uwdata/mosaic-sql').SQLExpression} - * A bin function generator. + * @returns {(value: any) => ExprNode} A bin function generator. */ function binInterval(scale, pixelSize, bin) { const { type, domain, range, apply, sqlApply } = scaleTransform(scale); if (!apply) return; // unsupported scale type - const fn = BIN[`${bin}`.toLowerCase()] || 'FLOOR'; + const binFn = BIN[`${bin}`.toLowerCase()] || floor; const lo = apply(Math.min(...domain)); const hi = apply(Math.max(...domain)); - const a = type === 'identity' ? 1 : Math.abs(range[1] - range[0]) / (hi - lo); - const s = a / pixelSize === 1 ? '' : `${a / pixelSize}::DOUBLE * `; - const d = lo === 0 ? '' : ` - ${lo}::DOUBLE`; - return value => sql`${fn}(${s}(${sqlApply(value)}${d}))::INTEGER`; + const s = (type === 'identity' + ? 1 + : Math.abs(range[1] - range[0]) / (hi - lo)) / pixelSize; + const scalar = s === 1 + ? x => x + : x => mul(float64(s), x); + const diff = lo === 0 + ? x => x + : x => sub(x, float64(lo)); + return value => int32(binFn(scalar(diff(sqlApply(value))))); } /** * Generate pre-aggregate query information. - * @param {Query} clientQuery The original client query. - * @param {*} active Active (selected) column definitions. - * @param {*} preaggCols Pre-aggregate column definitions. + * @param {SelectQuery} clientQuery The original client query. + * @param {ReturnType} active Active (selected) columns. + * @param {ReturnType} preaggCols Pre-aggregation columns. * @returns {PreAggregateInfo} */ function preaggregateInfo(clientQuery, active, preaggCols, schema) { - const { dims, aggr, aux } = preaggCols; + const { group, output, preagg } = preaggCols; const { columns } = active; // build materialized view construction query const query = clientQuery - .select({ ...columns, ...aux }) + .setSelect({ ...preagg, ...columns }) .groupby(Object.keys(columns)); // ensure active clause columns are selected by subqueries const [subq] = query.subqueries; if (subq) { - const cols = Object.values(columns).flatMap(c => c.columns); + const cols = Object.values(columns) + .flatMap(c => collectColumns(c).map(c => c.column)); subqueryPushdown(subq, cols); } // push orderby criteria to later queries - const order = query.orderby(); - query.query.orderby = []; + const order = query._orderby; + query._orderby = []; // generate creation query string and hash id const create = query.toString(); @@ -312,9 +318,9 @@ function preaggregateInfo(clientQuery, active, preaggCols, schema) { // generate preaggregate select query const select = Query - .select(dims, aggr) + .select(group, output) .from(table) - .groupby(dims) + .groupby(group) .orderby(order); return new PreAggregateInfo({ table, create, active, select }); @@ -328,7 +334,7 @@ function subqueryPushdown(query, cols) { const pushdown = q => { if (memo.has(q)) return; memo.add(q); - if (q.select && q.from().length) { + if (isSelectQuery(q) && q._from.length) { q.select(cols); } q.subqueries.forEach(pushdown); @@ -349,7 +355,7 @@ export class PreAggregateInfo { * @param {string} options.table The materialized view table name. * @param {string} options.create The table creation query. * @param {*} options.active Active column information. - * @param {Query} options.select Base query for requesting updates + * @param {SelectQuery} options.select Base query for requesting updates * using a pre-aggregated materialized view. */ constructor({ table, create, active, select }) { @@ -375,7 +381,7 @@ export class PreAggregateInfo { this.active = active; /** * Select query (sans where clause) for materialized views. - * @type {Query} + * @type {SelectQuery} */ this.select = select; /** @@ -388,9 +394,9 @@ export class PreAggregateInfo { /** * Generate a materialized view query for the given predicate. - * @param {import('@uwdata/mosaic-sql').SQLExpression} predicate The current + * @param {import('@uwdata/mosaic-sql').ExprNode} predicate The current * active clause predicate. - * @returns {Query} A materialized view query. + * @returns {SelectQuery} A materialized view query. */ query(predicate) { return this.select.clone().where(this.active.predicate(predicate)); diff --git a/packages/core/src/preagg/preagg-columns.js b/packages/core/src/preagg/preagg-columns.js new file mode 100644 index 00000000..0ac6cfa9 --- /dev/null +++ b/packages/core/src/preagg/preagg-columns.js @@ -0,0 +1,103 @@ +import { AggregateNode, ExprNode, Query, SelectQuery, collectAggregates, isAggregateExpression, isSelectQuery, isTableRef, rewrite, sql } from '@uwdata/mosaic-sql'; +import { MosaicClient } from '../MosaicClient.js'; +import { sufficientStatistics } from './sufficient-statistics.js'; + +/** + * Determine pre-aggregation columns for a given Mosaic client. + * @param {MosaicClient} client The Mosaic client. + * @returns An object with necessary column data to generate pre-aggregated + * columns, or null if the client can't be optimized or the client query + * contains an invalid or unsupported expression. + */ +export function preaggColumns(client) { + if (!client.filterStable) return null; + const q = client.query(); + + // bail if query is not analyzable + if (!isSelectQuery(q)) return null; + + // bail if no base table + const from = getBase(q, q => { + const ref = q._from[0]?.expr; + return isTableRef(ref) ? ref.name : ref; + }); + if (typeof from !== 'string') return null; + + /** @type {Map} */ + const aggrs = new Map; + /** @type {Record} */ + const preagg = {}; + /** @type {Record} */ + const output = {}; + /** @type {string[]} */ + const group = []; // list of grouping dimension columns + + // generate a scalar subquery for a global average + const avg = ref => { + const name = ref.column; + const expr = getBase(q, q => q._select.find(c => c.alias === name)?.expr); + return sql`(SELECT avg(${expr ?? ref}) FROM "${from}")`; + }; + + // iterate over select clauses and analyze expressions + for (const { alias, expr } of q._select) { + // bail if there is an aggregate we can't analyze + // a value > 1 indicates an aggregate in verbatim text + if (isAggregateExpression(expr) > 1) return null; + + const nodes = collectAggregates(expr); + if (nodes.length === 0) { + // if no aggregates, expr is a groupby dimension + group.push(alias); + preagg[alias] = expr; + } else { + for (const node of nodes) { + // bail if distinct aggregate + if (node.isDistinct) return null; + + // bail if aggregate function is unsupported + // otherwise add output aggregate to rewrite map + const agg = sufficientStatistics(node, preagg, avg); + if (!agg) return null; + aggrs.set(node, agg); + } + + // rewrite original select clause to use preaggregates + output[alias] = rewrite(expr, aggrs); + } + } + + // bail if the query has no aggregates + if (!aggrs.size) return null; + + return { group, preagg, output }; +} + +/** + * Identify a shared base (source) query and extract a value from it. + * This method is used to find a shared base table name or extract + * the original column name within a base table. + * @param {Query} query The input query. + * @param {(q: SelectQuery) => any} get A getter function to extract + * a value from a base query. + * @returns {string | undefined | NaN} the base query value, or + * `undefined` if there is no source table, or `NaN` if the + * query operates over multiple source tables. + */ +function getBase(query, get) { + const subq = query.subqueries; + + // select query + if (isSelectQuery(query) && subq.length === 0) { + return get(query); + } + + // handle set operations / subqueries + const base = getBase(subq[0], get); + for (let i = 1; i < subq.length; ++i) { + const value = getBase(subq[i], get); + if (value === undefined) continue; + if (value !== base) return NaN; + } + return base; +} diff --git a/packages/core/src/preagg/sufficient-statistics.js b/packages/core/src/preagg/sufficient-statistics.js new file mode 100644 index 00000000..25f167ee --- /dev/null +++ b/packages/core/src/preagg/sufficient-statistics.js @@ -0,0 +1,439 @@ +import { AggregateNode, and, argmax, argmin, count, div, ExprNode, isNotNull, max, min, mul, pow, regrAvgX, regrAvgY, regrCount, sql, sqrt, sub, sum } from '@uwdata/mosaic-sql'; +import { fnv_hash } from '../util/hash.js'; + +/** + * Determine sufficient statistics to preaggregate the given node. This + * method populates the *preagg* and *aggrs* arguments with necessary + * information for preaggregation optimization. + * @param {AggregateNode} node An aggregate function. + * @param {Record} preagg Map of column names to + * expressions to include in the preaggregation table. + * @returns {ExprNode} Output aggregate expression that uses preaggregated + * sufficient statistics to service updates. + */ +export function sufficientStatistics(node, preagg, avg) { + switch (node.name) { + case 'count': + case 'sum': + return sumExpr(preagg, node); + case 'avg': + return avgExpr(preagg, node); + case 'arg_max': + return argmaxExpr(preagg, node); + case 'arg_min': + return argminExpr(preagg, node); + + // variance statistics drop the original aggregate operation + // in favor of tracking sufficient statistics + case 'variance': + case 'var_samp': + return varianceExpr(preagg, node, avg); + case 'var_pop': + return varianceExpr(preagg, node, avg, false); + case 'stddev': + case 'stddev_samp': + return sqrt(varianceExpr(preagg, node, avg)); + case 'stddev_pop': + return sqrt(varianceExpr(preagg, node, avg, false)); + case 'covar_samp': + return covarianceExpr(preagg, node, avg); + case 'covar_pop': + return covarianceExpr(preagg, node, avg, false); + case 'corr': + return corrExpr(preagg, node, avg); + + // regression statistics + case 'regr_count': + return regrCountExpr(preagg, node).expr; + case 'regr_avgx': + return regrAvgXExpr(preagg, node); + case 'regr_avgy': + return regrAvgYExpr(preagg, node); + case 'regr_syy': + return regrVarExpr(preagg, 0, node, avg); + case 'regr_sxx': + return regrVarExpr(preagg, 1, node, avg); + case 'regr_sxy': + return covarianceExpr(preagg, node, avg, null); + case 'regr_slope': + return regrSlopeExpr(preagg, node, avg); + case 'regr_intercept': + return regrInterceptExpr(preagg, node, avg); + case 'regr_r2': + return pow(corrExpr(preagg, node, avg), 2); + + // aggregates that commute directly + case 'max': + case 'min': + case 'bit_and': + case 'bit_or': + case 'bit_xor': + case 'bool_and': + case 'bool_or': + case 'product': { + const name = colName(node); + preagg[name] = node; + return sql`${node.name}("${name}")`; + } + + // unsupported aggregate, return null to indicate failure + default: return null; + } +} + +/** + * Generate a column name for the given aggregate node. The name is + * made from a hash of the string-serialized SQL expression. + * @param {AggregateNode} node The aggregate node to name. + * @returns {string} The generated column name. + */ +function colName(node) { + return 'pre_' + fnv_hash(`${node}`).toString(16); +} + +/** + * Add a sufficient statistic to the preaggregation column set. + * Generates a unique column name for the statistic and propagates + * a FILTER clause if one exists on the original aggregate node. + * @param {Record} preagg A map of columns (such as + * sufficient statistics) to pre-aggregate. + * @param {AggregateNode} expr The aggregate statistic to add. + * @param {AggregateNode} [node] The originating aggregate function call. + * @returns {string} The name of the statistic column. + */ +function addStat(preagg, expr, node) { + const filter = node?.filter; + if (filter) { + // push filter clause to preaggregate expr + expr = expr.filter + ? expr.where(and(filter, expr.filter)) + : expr.where(filter); + } + const name = colName(expr); + preagg[name] = expr; + return name; +} + +/** + * Generate an expression for calculating counts over data dimensions. + * As a side effect, this method adds a column to the input *preagg* object + * to track the count of non-null values per-partition. + * @param {Record} preagg A map of columns (such as + * sufficient statistics) to pre-aggregate. + * @param {AggregateNode} node The originating aggregate function call. + * @returns {{ expr: ExprNode, name: string }} An aggregate expression over + * pre-aggregated dimensions and associated column name. + */ +function countExpr(preagg, node) { + const name = addStat(preagg, count(node.args[0]), node); + return { expr: sum(name), name }; +} + +/** + * Generate an expression for calculating counts or sums over data dimensions. + * @param {Record} preagg A map of columns (such as + * sufficient statistics) to pre-aggregate. + * @param {AggregateNode} node The originating aggregate function call. + * @returns {ExprNode} An aggregate expression over pre-aggregated dimensions. + */ +function sumExpr(preagg, node) { + return sum(addStat(preagg, node)); +} + +/** + * Generate an expression for calculating averages over data dimensions. + * As a side effect, this method adds a column to the input *preagg* object + * to track the count of non-null values per-partition. + * @param {Record} preagg A map of columns (such as + * sufficient statistics) to pre-aggregate. + * @param {AggregateNode} [node] The originating aggregate function call. + * @returns {ExprNode} An aggregate expression over pre-aggregated dimensions. + */ +function avgExpr(preagg, node) { + const as = addStat(preagg, node); + const { expr, name } = countExpr(preagg, node); + return div(sum(mul(as, name)), expr); +} + +/** + * Generate an expression for calculating argmax over data dimensions. + * As a side effect, this method adds a column to the input *preagg* object + * to track a maximum value per-partition. + * @param {Record} preagg A map of columns (such as + * sufficient statistics) to pre-aggregate. + * @param {AggregateNode} node The originating aggregate function call. + * @returns {ExprNode} An aggregate expression over pre-aggregated dimensions. + */ +function argmaxExpr(preagg, node) { + const expr = addStat(preagg, node); + const maxy = addStat(preagg, max(node.args[1]), node); + return argmax(expr, maxy); +} + +/** + * Generate an expression for calculating argmin over data dimensions. + * As a side effect, this method adds a column to the input *preagg* object + * to track a minimum value per-partition. + * @param {object} preagg An object for columns (such as + * sufficient statistics) to include in the pre-aggregation. + * @param {AggregateNode} node Source data table columns. The entries may be strings, + * column references, SQL expressions, or other string-coercible values. + * @returns {ExprNode} An aggregate expression over pre-aggregated dimensions. + */ +function argminExpr(preagg, node) { + const expr = addStat(preagg, node); + const miny = addStat(preagg, min(node.args[1]), node); + return argmin(expr, miny); +} + +/** + * Generate an expression for calculating variance over data dimensions. + * This method uses the "textbook" definition of variance (E[X^2] - E[X]^2), + * but on mean-centered data to reduce floating point error. The variance + * calculation uses three sufficient statistics: the count of non-null values, + * the residual sum of squares and the sum of residual (mean-centered) values. + * As a side effect, this method adds columns for these statistics to the + * input *preagg* object. + * @param {Record} preagg A map of columns (such as + * sufficient statistics) to pre-aggregate. + * @param {AggregateNode} node The originating aggregate function call. + * @param {(field: any) => ExprNode} avg Global average query generator. + * @param {boolean} [correction=true] A flag for whether a Bessel + * correction should be applied to compute the sample variance + * rather than the populatation variance. + * @returns {ExprNode} An aggregate expression over pre-aggregated dimensions. + */ +function varianceExpr(preagg, node, avg, correction = true) { + const x = node.args[0]; + const { expr: n } = countExpr(preagg, node); + const delta = sub(x, avg(x)); + const rssq = addStat(preagg, sum(pow(delta, 2)), node); // residual sum of squares + const rsum = addStat(preagg, sum(delta), node); // residual sum + const denom = correction ? sub(n, 1) : n; // Bessel correction + return div(sub(sum(rssq), div(pow(sum(rsum), 2), n)), denom); +} + +/** + * Generate an expression for calculating covariance over data dimensions. + * This method uses mean-centered data to reduce floating point error. The + * covariance calculation uses four sufficient statistics: the count of + * non-null value pairs, the sum of residual products, and residual sums + * (of mean-centered values) for x and y. As a side effect, this method + * adds columns for these statistics to the input *preagg* object. + * @param {Record} preagg A map of columns (such as + * sufficient statistics) to pre-aggregate. + * @param {AggregateNode} node The originating aggregate function call. + * @param {(field: any) => ExprNode} avg Global average query generator. + * @param {boolean|null} [correction=true] A flag for whether a Bessel + * correction should be applied to compute the sample covariance rather + * than the populatation covariance. If null, an expression for the + * unnormalized covariance (no division by sample count) is returned. + * @returns {ExprNode} An aggregate expression over pre-aggregated dimensions. + */ +function covarianceExpr(preagg, node, avg, correction = true) { + const { expr: n } = regrCountExpr(preagg, node); + const sxy = regrSumXYExpr(preagg, node, avg); + const sx = regrSumExpr(preagg, 1, node, avg); + const sy = regrSumExpr(preagg, 0, node, avg); + const num = sub(sxy, div(mul(sx, sy), n)); + return correction === null ? num // do not divide by count + : correction ? div(num, sub(n, 1)) // Bessel correction (sample) + : div(num, n); // no correction (population) +} + +/** + * Generate an expression for calculating Pearson product-moment correlation + * coefficients over data dimensions. This method uses mean-centered data + * to reduce floating point error. The correlation calculation uses six + * sufficient statistics: the count of non-null value pairs, the sum of + * residual products, and both residual sums and sums of squares for x and y. + * As a side effect, this method adds columns for these statistics to the + * input *preagg* object. + * @param {Record} preagg A map of columns (such as + * sufficient statistics) to pre-aggregate. + * @param {AggregateNode} node The originating aggregate function call. + * @param {(field: any) => ExprNode} avg Global average query generator. + * @returns {ExprNode} An aggregate expression over pre-aggregated dimensions. + */ +function corrExpr(preagg, node, avg) { + const { expr: n } = regrCountExpr(preagg, node); + const sxy = regrSumXYExpr(preagg, node, avg); + const sxx = regrSumSqExpr(preagg, 1, node, avg); + const syy = regrSumSqExpr(preagg, 0, node, avg); + const sx = regrSumExpr(preagg, 1, node, avg); + const sy = regrSumExpr(preagg, 0, node, avg); + const vx = sub(sxx, div(pow(sx, 2), n)); + const vy = sub(syy, div(pow(sy, 2), n)); + return div( + sub(sxy, div(mul(sx, sy), n)), + sqrt(mul(vx, vy)) + ); +} + +/** + * Generate an expression for the count of non-null (x, y) pairs. As a side + * effect, this method adds columns to the input *preagg* object to the + * partition-level count of non-null pairs. + * @param {Record} preagg A map of columns (such as + * sufficient statistics) to pre-aggregate. + * @param {AggregateNode} node The originating aggregate function call. + * @returns {{ expr: ExprNode, name: string }} An aggregate expression over + * pre-aggregated dimensions and associated column name. + */ +function regrCountExpr(preagg, node) { + const [x, y] = node.args; + const n = addStat(preagg, regrCount(x, y), node); + return { expr: sum(n), name: n }; +} + +/** + * Generate an expression for calculating sums of residual values for use in + * covariance and regression queries. Only values corresponding to non-null + * (x, y) pairs are included. This method uses mean-centered data to reduce + * floating point error. As a side effect, this method adds a column for + * partition-level sums to the input *preagg* object. + * @param {Record} preagg A map of columns (such as + * sufficient statistics) to pre-aggregate. + * @param {number} i An index indicating which argument column to sum. + * @param {AggregateNode} node The originating aggregate function call. + * @param {(field: any) => ExprNode} avg Global average query generator. + * @returns {ExprNode} An aggregate expression over pre-aggregated dimensions. + */ +function regrSumExpr(preagg, i, node, avg) { + const args = node.args; + const v = args[i]; + const o = args[1 - i]; + const rsum = sum(sub(v, avg(v))).where(isNotNull(o)); + return sum(addStat(preagg, rsum, node)); +} + +/** + * Generate an expressios for calculating sums of squared residual values for + * use in covariance and regression queries. Only values corresponding to + * non-null (x, y) pairs are included. This method uses mean-centered data to + * reduce floating point error. As a side effect, this method adds a column + * for partition-level sums to the input *preagg* object. + * @param {Record} preagg A map of columns (such as + * sufficient statistics) to pre-aggregate. + * @param {number} i An index indicating which argument column to sum. + * @param {AggregateNode} node The originating aggregate function call. + * @param {(field: any) => ExprNode} avg Global average query generator. + * @returns {ExprNode} An aggregate expression over pre-aggregated dimensions. + */ +function regrSumSqExpr(preagg, i, node, avg) { + const args = node.args; + const v = args[i]; + const u = args[1 - i]; + const ssq = sum(pow(sub(v, avg(v)), 2)).where(isNotNull(u)); + return sum(addStat(preagg, ssq, node)); +} + +/** + * Generate an expression for calculating sums of residual product values for + * use in covariance and regression queries. Only values corresponding to + * non-null (x, y) pairs are included. This method uses mean-centered data to + * reduce floating point error. As a side effect, this method adds a column + * for partition-level sums to the input *preagg* object. + * @param {Record} preagg A map of columns (such as + * sufficient statistics) to pre-aggregate. + * @param {AggregateNode} node The originating aggregate function call. + * @param {(field: any) => ExprNode} avg Global average query generator. + * @returns {ExprNode} An aggregate expression over pre-aggregated dimensions. + */ +function regrSumXYExpr(preagg, node, avg) { + const [y, x] = node.args; + const sxy = sum(mul(sub(x, avg(x)), sub(y, avg(y)))); + return sum(addStat(preagg, sxy, node)); +} + +/** + * Generate an expression for the average x value in a regression context. + * Only values corresponding to non-null (x, y) pairs are included. As a side + * effect, this method adds columns to the input *preagg* object to track both + * the count of non-null pairs and partition-level averages. + * @param {Record} preagg A map of columns (such as + * sufficient statistics) to pre-aggregate. + * @param {AggregateNode} node The originating aggregate function call. + * @returns {ExprNode} An aggregate expression over pre-aggregated dimensions. + */ +function regrAvgXExpr(preagg, node) { + const [y, x] = node.args; + const { expr: n, name } = regrCountExpr(preagg, node); + const a = addStat(preagg, regrAvgX(y, x), node); + return div(sum(mul(a, name)), n); +} + +/** + * Generate an expression for the average y value in a regression context. + * Only values corresponding to non-null (x, y) pairs are included. As a side + * effect, this method adds columns to the input *preagg* object to track both + * the count of non-null pairs and partition-level averages. + * @param {Record} preagg A map of columns (such as + * sufficient statistics) to pre-aggregate. + * @param {AggregateNode} node The originating aggregate function call. + * @returns {ExprNode} An aggregate expression over pre-aggregated dimensions. + */ +function regrAvgYExpr(preagg, node) { + const [y, x] = node.args; + const { expr: n, name } = regrCountExpr(preagg, node); + const a = addStat(preagg, regrAvgY(y, x), node); + return div(sum(mul(a, name)), n); +} + +/** + * Generate an expression for calculating variance over data dimensions for + * use in covariance and regression queries. Only values corresponding to + * non-null (x, y) pairs are included. This method uses mean-centered data to + * reduce floating point error. As a side effect, this method adds columns + * for partition-level count and sums to the input *preagg* object. + * @param {Record} preagg A map of columns (such as + * sufficient statistics) to pre-aggregate. + * @param {number} i The index of the argument to compute the variance for. + * @param {AggregateNode} node The originating aggregate function call. + * @param {(field: any) => ExprNode} avg Global average query generator. + * @returns {ExprNode} An aggregate expression for calculating variance + * over pre-aggregated data dimensions. + */ +function regrVarExpr(preagg, i, node, avg) { + const { expr: n } = regrCountExpr(preagg, node); + const sum = regrSumExpr(preagg, i, node, avg); + const ssq = regrSumSqExpr(preagg, i, node, avg); + return sub(ssq, div(pow(sum, 2), n)); +} + +/** + * Generate an expression for calculating a regression slope. The slope is + * computed as the covariance divided by the variance of the x variable. As a + * side effect, this method adds columns for sufficient statistics to the + * input *preagg* object. + * @param {Record} preagg A map of columns (such as + * sufficient statistics) to pre-aggregate. + * @param {AggregateNode} node The originating aggregate function call. + * @param {(field: any) => ExprNode} avg Global average query generator. + * @returns {ExprNode} An aggregate expression for calculating regression + * slopes over pre-aggregated data dimensions. + */ +function regrSlopeExpr(preagg, node, avg) { + const cov = covarianceExpr(preagg, node, avg, null); + const varx = regrVarExpr(preagg, 1, node, avg); + return div(cov, varx); +} + +/** + * Generate an expression for calculating a regression intercept. The intercept + * is derived from the regression slope and average x and y values. As a + * side effect, this method adds columns for sufficient statistics to the + * input *preagg* object. + * @param {Record} preagg A map of columns (such as + * sufficient statistics) to pre-aggregate. + * @param {AggregateNode} node The originating aggregate function call. + * @param {(field: any) => ExprNode} avg Global average query generator. + * @returns {ExprNode} An aggregate expression for calculating regression + * intercepts over pre-aggregated data dimensions. + */ +function regrInterceptExpr(preagg, node, avg) { + const ax = regrAvgXExpr(preagg, node); + const ay = regrAvgYExpr(preagg, node); + const m = regrSlopeExpr(preagg, node, avg); + return sub(ay, mul(m, ax)); +} diff --git a/packages/core/src/util/field-info.js b/packages/core/src/util/field-info.js index b9ab3df1..f2009d84 100644 --- a/packages/core/src/util/field-info.js +++ b/packages/core/src/util/field-info.js @@ -1,4 +1,4 @@ -import { Query, asRelation, count, isNull, max, min, sql } from '@uwdata/mosaic-sql'; +import { AggregateNode, Query, asTableRef, count, isNull, max, min, sql } from '@uwdata/mosaic-sql'; import { jsType } from './js-type.js'; export const Count = 'count'; @@ -8,6 +8,9 @@ export const Min = 'min'; export const Distinct = 'distinct'; export const Stats = { Count, Nulls, Max, Min, Distinct }; +/** + * @type {Record AggregateNode>} + */ const statMap = { [Count]: count, [Distinct]: column => count(column).distinct(), @@ -16,14 +19,21 @@ const statMap = { [Nulls]: column => count().where(isNull(column)) }; +/** + * + * @param {string} table + * @param {string} column + * @param {string[]|Set} stats + * @returns + */ function summarize(table, column, stats) { return Query .from(table) - .select(Array.from(stats, s => [s, statMap[s](column)])); + .select(Array.from(stats, s => ({[s]: statMap[s](column)}))); } export async function queryFieldInfo(mc, fields) { - if (fields.length === 1 && `${fields[0].column}` === '*') { + if (fields.length === 1 && fields[0].column === '*') { return getTableInfo(mc, fields[0].table); } else { return (await Promise @@ -35,7 +45,8 @@ export async function queryFieldInfo(mc, fields) { async function getFieldInfo(mc, { table, column, stats }) { // generate and issue a query for field metadata info // use GROUP BY ALL to differentiate & consolidate aggregates - const q = Query.from({ source: table }) + const q = Query + .from({ source: table }) .select({ column }) .groupby(column.aggregate ? sql`ALL` : []); const [desc] = Array.from(await mc.query(Query.describe(q))); @@ -61,7 +72,7 @@ async function getFieldInfo(mc, { table, column, stats }) { } async function getTableInfo(mc, table) { - const result = await mc.query(`DESCRIBE ${asRelation(table)}`); + const result = await mc.query(`DESCRIBE ${asTableRef(table)}`); return Array.from(result).map(desc => ({ table, column: desc.column_name, diff --git a/packages/core/src/util/hash.js b/packages/core/src/util/hash.js index 512cf6a7..50b3847d 100644 --- a/packages/core/src/util/hash.js +++ b/packages/core/src/util/hash.js @@ -7,7 +7,7 @@ export function fnv_hash(v) { if (d) a = fnv_multiply(a ^ d >> 8); a = fnv_multiply(a ^ c & 0xff); } - return fnv_mix(a); + return fnv_mix(a) >>> 0; // ensure non-zero value } // a * 16777619 mod 2**32 diff --git a/packages/core/src/util/preagg-columns.js b/packages/core/src/util/preagg-columns.js deleted file mode 100644 index e53331ee..00000000 --- a/packages/core/src/util/preagg-columns.js +++ /dev/null @@ -1,537 +0,0 @@ -import { Query, agg, sql } from '@uwdata/mosaic-sql'; -import { MosaicClient } from '../MosaicClient.js'; - -/** - * Determine pre-aggregation columns for a given Mosaic client. - * @param {MosaicClient} client The Mosaic client. - * @returns An object with necessary column data to generate pre-aggregated - * columns, or null if the client can be optimized or the client query - * contains an invalid or unsupported expression. - */ -export function preaggColumns(client) { - if (!client.filterStable) return null; - const q = client.query(); - const from = getBase(q, q => q.from()?.[0].from.table); - - // bail if no base table or the query is not analyzable - if (typeof from !== 'string' || !q.select) return null; - - const aggr = []; // list of output aggregate columns - const dims = []; // list of grouping dimension columns - const aux = {}; // auxiliary columns needed by aggregates - - const avg = ref => { - const name = ref.column; - // @ts-ignore - const expr = getBase(q, q => q.select().find(c => c.as === name)?.expr); - return `(SELECT AVG(${expr ?? ref}) FROM "${from}")`; - }; - - for (const entry of q.select()) { - const { as, expr: { aggregate, args } } = entry; - const op = aggregate?.toUpperCase?.(); - switch (op) { - case 'COUNT': - case 'SUM': - // TODO: revisit this DOUBLE cast in the future - // for now, this sidesteps client-side conversions - // of bignum and fixed decimal types to JS numbers - aggr.push({ [as]: agg`SUM("${as}")::DOUBLE` }); - break; - case 'AVG': - aggr.push({ [as]: avgExpr(aux, as, args[0]) }); - break; - case 'ARG_MAX': - aggr.push({ [as]: argmaxExpr(aux, as, args) }); - break; - case 'ARG_MIN': - aggr.push({ [as]: argminExpr(aux, as, args) }); - break; - - // variance statistics drop the original aggregate operation - // in favor of tracking auxiliary sufficient statistics - case 'VARIANCE': - case 'VAR_SAMP': - aux[as] = null; - aggr.push({ [as]: varianceExpr(aux, args[0], avg) }); - break; - case 'VAR_POP': - aux[as] = null; - aggr.push({ [as]: varianceExpr(aux, args[0], avg, false) }); - break; - case 'STDDEV': - case 'STDDEV_SAMP': - aux[as] = null; - aggr.push({ [as]: agg`SQRT(${varianceExpr(aux, args[0], avg)})` }); - break; - case 'STDDEV_POP': - aux[as] = null; - aggr.push({ [as]: agg`SQRT(${varianceExpr(aux, args[0], avg, false)})` }); - break; - case 'COVAR_SAMP': - aux[as] = null; - aggr.push({ [as]: covarianceExpr(aux, args, avg) }); - break; - case 'COVAR_POP': - aux[as] = null; - aggr.push({ [as]: covarianceExpr(aux, args, avg, false) }); - break; - case 'CORR': - aux[as] = null; - aggr.push({ [as]: corrExpr(aux, args, avg) }); - break; - - // regression statistics - case 'REGR_COUNT': - aux[as] = null; - aggr.push({ [as]: agg`${regrCountExpr(aux, args)}::DOUBLE` }); - break; - case 'REGR_AVGX': - aux[as] = null; - aggr.push({ [as]: regrAvgXExpr(aux, args) }); - break; - case 'REGR_AVGY': - aux[as] = null; - aggr.push({ [as]: regrAvgYExpr(aux, args) }); - break; - case 'REGR_SYY': - aux[as] = null; - aggr.push({ [as]: regrVarExpr(aux, 0, args, avg) }); - break; - case 'REGR_SXX': - aux[as] = null; - aggr.push({ [as]: regrVarExpr(aux, 1, args, avg) }); - break; - case 'REGR_SXY': - aux[as] = null; - aggr.push({ [as]: covarianceExpr(aux, args, avg, null) }); - break; - case 'REGR_SLOPE': - aux[as] = null; - aggr.push({ [as]: regrSlopeExpr(aux, args, avg) }); - break; - case 'REGR_INTERCEPT': - aux[as] = null; - aggr.push({ [as]: regrInterceptExpr(aux, args, avg) }); - break; - case 'REGR_R2': - aux[as] = null; - aggr.push({ [as]: agg`(${corrExpr(aux, args, avg)}) ** 2` }); - break; - - // aggregates that commute directly - case 'MAX': - case 'MIN': - case 'BIT_AND': - case 'BIT_OR': - case 'BIT_XOR': - case 'BOOL_AND': - case 'BOOL_OR': - case 'PRODUCT': - aggr.push({ [as]: agg`${op}("${as}")` }); - break; - - // otherwise, check if dimension - default: - if (!aggregate) dims.push(as); - else return null; // unsupported aggregate - } - } - - // bail if the query has no aggregates - if (!aggr.length) return null; - - return { from, dims, aggr, aux }; -} - -/** - * Generate an output column name for use as an auxiliary column - * (e.g., for sufficient statistics) within a preaggregated table. - * @param {string} type The operation type. - * @param {...any} args The input column arguments. - * @returns {string} A sanitized auxiliary column name. - */ -function auxName(type, ...args) { - const cols = args.length ? '_' + args.map(sanitize).join('_') : ''; - return `__${type}${cols}__`; -} - -/** - * Sanitize a table column reference as a "safe" string value to - * use as part of derived column names. - * @param {*} col The source data table column. This may be a string, - * column reference, SQL expression, or other string-coercible value. - * @returns {string} The sanitized column name. - */ -function sanitize(col) { - return `${col}` - .replaceAll('"', '') - .replaceAll(' ', '_'); -} - -/** - * Identify a shared base (source) query and extract a value from it. - * This method is used to find a shared base table name or extract - * the original column name within a base table. - * @param {Query} query The input query. - * @param {(q: Query) => any} get A getter function to extract - * a value from a base query. - * @returns {string | undefined | NaN} the base query value, or - * `undefined` if there is no source table, or `NaN` if the - * query operates over multiple source tables. - */ -function getBase(query, get) { - const subq = query.subqueries; - - // select query - if (query.select && subq.length === 0) { - return get(query); - } - - // handle set operations / subqueries - const base = getBase(subq[0], get); - for (let i = 1; i < subq.length; ++i) { - const value = getBase(subq[i], get); - if (value === undefined) continue; - if (value !== base) return NaN; - } - return base; -} - -/** - * Generate an expression for calculating counts over data partitions. - * As a side effect, this method adds a column to the input *aux* object - * to track the count of non-null values per-partition. - * @param {object} aux An object for auxiliary columns (such as - * sufficient statistics) to include in the pre-aggregation. - * @param {any} arg Source data table column. This value may be a string, - * column reference, SQL expression, or other string-coercible value. - * @returns An aggregate expression for calculating counts over - * pre-aggregated data partitions. - */ -function countExpr(aux, arg) { - const n = auxName('count', arg); - aux[n] = agg`COUNT(${arg})`; - return agg`SUM(${n})`.annotate({ name: n }); -} - -/** - * Generate an expression for calculating averages over data partitions. - * As a side effect, this method adds a column to the input *aux* object - * to track the count of non-null values per-partition. - * @param {object} aux An object for auxiliary columns (such as - * sufficient statistics) to include in the pre-aggregation. - * @param {string} as The output column for the original aggregate. - * @param {any} arg Source data table column. This value may be a string, - * column reference, SQL expression, or other string-coercible value. - * @returns An aggregate expression for calculating averages over - * pre-aggregated data partitions. - */ -function avgExpr(aux, as, arg) { - const n = countExpr(aux, arg); - return agg`(SUM("${as}" * ${n.name}) / ${n})`; -} - -/** - * Generate an expression for calculating argmax over data partitions. - * As a side effect, this method adds a column to the input *aux* object - * to track a maximum value per-partition. - * @param {object} aux An object for auxiliary columns (such as - * sufficient statistics) to include in the pre-aggregation. - * @param {string} as The output column for the original aggregate. - * @param {any[]} args Source data table columns. The entries may be strings, - * column references, SQL expressions, or other string-coercible values. - * @returns An aggregate expression for calculating argmax over - * pre-aggregated data partitions. - */ -function argmaxExpr(aux, as, [, y]) { - const max = auxName('max', y); - aux[max] = agg`MAX(${y})`; - return agg`ARG_MAX("${as}", ${max})`; -} - -/** - * Generate an expression for calculating argmin over data partitions. - * As a side effect, this method adds a column to the input *aux* object - * to track a minimum value per-partition. - * @param {object} aux An object for auxiliary columns (such as - * sufficient statistics) to include in the pre-aggregation. - * @param {string} as The output column for the original aggregate. - * @param {any[]} args Source data table columns. The entries may be strings, - * column references, SQL expressions, or other string-coercible values. - * @returns An aggregate expression for calculating argmin over - * pre-aggregated data partitions. - */ -function argminExpr(aux, as, [, y]) { - const min = auxName('min', y); - aux[min] = agg`MIN(${y})`; - return agg`ARG_MIN("${as}", ${min})`; -} - -/** - * Generate an expression for calculating variance over data partitions. - * This method uses the "textbook" definition of variance (E[X^2] - E[X]^2), - * but on mean-centered data to reduce floating point error. The variance - * calculation uses three sufficient statistics: the count of non-null values, - * the residual sum of squares and the sum of residual (mean-centered) values. - * As a side effect, this method adds columns for these statistics to the - * input *aux* object. - * @param {object} aux An object for auxiliary columns (such as - * sufficient statistics) to include in the pre-aggregation. - * @param {*} x The source data table column. This may be a string, - * column reference, SQL expression, or other string-coercible value. - * @param {(field: any) => string} avg Global average query generator. - * @param {boolean} [correction=true] A flag for whether a Bessel - * correction should be applied to compute the sample variance - * rather than the populatation variance. - * @returns An aggregate expression for calculating variance over - * pre-aggregated data partitions. - */ -function varianceExpr(aux, x, avg, correction = true) { - const n = countExpr(aux, x); - const ssq = auxName('rssq', x); // residual sum of squares - const sum = auxName('rsum', x); // residual sum - const delta = sql`${x} - ${avg(x)}`; - aux[ssq] = agg`SUM((${delta}) ** 2)`; - aux[sum] = agg`SUM(${delta})`; - const adj = correction ? ` - 1` : ''; // Bessel correction - return agg`(SUM(${ssq}) - (SUM(${sum}) ** 2 / ${n})) / (${n}${adj})`; -} - -/** - * Generate an expression for calculating covariance over data partitions. - * This method uses mean-centered data to reduce floating point error. The - * covariance calculation uses four sufficient statistics: the count of - * non-null value pairs, the sum of residual products, and residual sums - * (of mean-centered values) for x and y. As a side effect, this method - * adds columns for these statistics to the input *aux* object. - * @param {object} aux An object for auxiliary columns (such as - * sufficient statistics) to include in the pre-aggregation. - * @param {any[]} args Source data table columns. The entries may be strings, - * column references, SQL expressions, or other string-coercible values. - * @param {(field: any) => string} avg Global average query generator. - * @param {boolean|null} [correction=true] A flag for whether a Bessel - * correction should be applied to compute the sample covariance rather - * than the populatation covariance. If null, an expression for the - * unnormalized covariance (no division by sample count) is returned. - * @returns An aggregate expression for calculating covariance over - * pre-aggregated data partitions. - */ -function covarianceExpr(aux, args, avg, correction = true) { - const n = regrCountExpr(aux, args); - const sxy = regrSumXYExpr(aux, args, avg); - const sx = regrSumExpr(aux, 1, args, avg); - const sy = regrSumExpr(aux, 0, args, avg); - const adj = correction === null ? '' // do not divide by count - : correction ? ` / (${n} - 1)` // Bessel correction (sample) - : ` / ${n}`; // no correction (population) - return agg`(${sxy} - ${sx} * ${sy} / ${n})${adj}`; -} - -/** - * Generate an expression for calculating Pearson product-moment correlation - * coefficients over data partitions. This method uses mean-centered data - * to reduce floating point error. The correlation calculation uses six - * sufficient statistics: the count of non-null value pairs, the sum of - * residual products, and both residual sums and sums of squares for x and y. - * As a side effect, this method adds columns for these statistics to the - * input *aux* object. - * @param {object} aux An object for auxiliary columns (such as - * sufficient statistics) to include in the pre-aggregation. - * @param {any[]} args Source data table columns. The entries may be strings, - * column references, SQL expressions, or other string-coercible values. - * @param {(field: any) => string} avg Global average query generator. - * @returns An aggregate expression for calculating correlation over - * pre-aggregated data partitions. - */ -function corrExpr(aux, args, avg) { - const n = regrCountExpr(aux, args); - const sxy = regrSumXYExpr(aux, args, avg); - const sxx = regrSumSqExpr(aux, 1, args, avg); - const syy = regrSumSqExpr(aux, 0, args, avg); - const sx = regrSumExpr(aux, 1, args, avg); - const sy = regrSumExpr(aux, 0, args, avg); - const vx = agg`(${sxx} - (${sx} ** 2) / ${n})`; - const vy = agg`(${syy} - (${sy} ** 2) / ${n})`; - return agg`(${sxy} - ${sx} * ${sy} / ${n}) / SQRT(${vx} * ${vy})`; -} - -/** - * Generate an expression for the count of non-null (x, y) pairs. As a side - * effect, this method adds columns to the input *aux* object to the - * partition-level count of non-null pairs. - * @param {object} aux An object for auxiliary columns (such as - * sufficient statistics) to include in the pre-aggregation. - * @param {any[]} args Source data table columns. The entries may be strings, - * column references, SQL expressions, or other string-coercible values. - * @returns An aggregate expression for calculating regression pair counts - * over pre-aggregated data partitions. - */ -function regrCountExpr(aux, [y, x]) { - const n = auxName('count', y, x); - aux[n] = agg`REGR_COUNT(${y}, ${x})`; - return agg`SUM(${n})`.annotate({ name: n }); -} - -/** - * Generate an expression for calculating sums of residual values for use in - * covariance and regression queries. Only values corresponding to non-null - * (x, y) pairs are included. This method uses mean-centered data to reduce - * floating point error. As a side effect, this method adds a column for - * partition-level sums to the input *aux* object. - * @param {object} aux An object for auxiliary columns (such as - * sufficient statistics) to include in the pre-aggregation. - * @param {number} i An index indicating which argument column to sum. - * @param {any[]} args Source data table columns. The entries may be strings, - * column references, SQL expressions, or other string-coercible values. - * @param {(field: any) => string} avg Global average query generator. - * @returns An aggregate expression over pre-aggregated data partitions. - */ -function regrSumExpr(aux, i, args, avg) { - const v = args[i]; - const o = args[1 - i]; - const sum = auxName('rs', v); - aux[sum] = agg`SUM(${v} - ${avg(v)}) FILTER (${o} IS NOT NULL)`; - return agg`SUM(${sum})` -} - -/** - * Generate an expressios for calculating sums of squared residual values for - * use in covariance and regression queries. Only values corresponding to - * non-null (x, y) pairs are included. This method uses mean-centered data to - * reduce floating point error. As a side effect, this method adds a column - * for partition-level sums to the input *aux* object. - * @param {object} aux An object for auxiliary columns (such as - * sufficient statistics) to include in the pre-aggregation. - * @param {number} i An index indicating which argument column to sum. - * @param {any[]} args Source data table columns. The entries may be strings, - * column references, SQL expressions, or other string-coercible values. - * @param {(field: any) => string} avg Global average query generator. - * @returns An aggregate expression over pre-aggregated data partitions. - */ -function regrSumSqExpr(aux, i, args, avg) { - const v = args[i]; - const u = args[1 - i]; - const ssq = auxName('rss', v); - aux[ssq] = agg`SUM((${v} - ${avg(v)}) ** 2) FILTER (${u} IS NOT NULL)`; - return agg`SUM(${ssq})` -} - -/** - * Generate an expression for calculating sums of residual product values for - * use in covariance and regression queries. Only values corresponding to - * non-null (x, y) pairs are included. This method uses mean-centered data to - * reduce floating point error. As a side effect, this method adds a column - * for partition-level sums to the input *aux* object. - * @param {object} aux An object for auxiliary columns (such as - * sufficient statistics) to include in the pre-aggregation. - * @param {any[]} args Source data table columns. The entries may be strings, - * column references, SQL expressions, or other string-coercible values. - * @param {(field: any) => string} avg Global average query generator. - * @returns An aggregate expression over pre-aggregated data partitions. - */ -function regrSumXYExpr(aux, args, avg) { - const [y, x] = args; - const sxy = auxName('sxy', y, x); - aux[sxy] = agg`SUM((${x} - ${avg(x)}) * (${y} - ${avg(y)}))`; - return agg`SUM(${sxy})`; -} - -/** - * Generate an expression for the average x value in a regression context. - * Only values corresponding to non-null (x, y) pairs are included. As a side - * effect, this method adds columns to the input *aux* object to track both - * the count of non-null pairs and partition-level averages. - * @param {object} aux An object for auxiliary columns (such as - * sufficient statistics) to include in the pre-aggregation. - * @param {any[]} args Source data table columns. The entries may be strings, - * column references, SQL expressions, or other string-coercible values. - * @returns An aggregate expression over pre-aggregated data partitions. - */ -function regrAvgXExpr(aux, args) { - const [y, x] = args; - const n = regrCountExpr(aux, args); - const a = auxName('avg', x, y); - aux[a] = agg`REGR_AVGX(${y}, ${x})`; - return agg`(SUM(${a} * ${n.name}) / ${n})`; -} - -/** - * Generate an expression for the average y value in a regression context. - * Only values corresponding to non-null (x, y) pairs are included. As a side - * effect, this method adds columns to the input *aux* object to track both - * the count of non-null pairs and partition-level averages. - * @param {object} aux An object for auxiliary columns (such as - * sufficient statistics) to include in the pre-aggregation. - * @param {any[]} args Source data table columns. The entries may be strings, - * column references, SQL expressions, or other string-coercible values. - * @returns An aggregate expression over pre-aggregated data partitions. - */ -function regrAvgYExpr(aux, args) { - const [y, x] = args; - const n = regrCountExpr(aux, args); - const a = auxName('avg', y, x); - aux[a] = agg`REGR_AVGY(${y}, ${x})`; - return agg`(SUM(${a} * ${n.name}) / ${n})`; -} - -/** - * Generate an expression for calculating variance over data partitions for - * use in covariance and regression queries. Only values corresponding to - * non-null (x, y) pairs are included. This method uses mean-centered data to - * reduce floating point error. As a side effect, this method adds columns - * for partition-level count and sums to the input *aux* object. - * @param {object} aux An object for auxiliary columns (such as - * sufficient statistics) to include in the pre-aggregation. - * @param {number} i The index of the argument to compute the variance for. - * @param {any[]} args Source data table columns. The entries may be strings, - * column references, SQL expressions, or other string-coercible values. - * @param {(field: any) => string} avg Global average query generator. - * @returns An aggregate expression for calculating variance over - * pre-aggregated data partitions. - */ -function regrVarExpr(aux, i, args, avg) { - const n = regrCountExpr(aux, args); - const sum = regrSumExpr(aux, i, args, avg); - const ssq = regrSumSqExpr(aux, i, args, avg); - return agg`(${ssq} - (${sum} ** 2 / ${n}))`; -} - -/** - * Generate an expression for calculating a regression slope. The slope is - * computed as the covariance divided by the variance of the x variable. As a - * side effect, this method adds columns for sufficient statistics to the - * input *aux* object. - * @param {object} aux An object for auxiliary columns (such as - * sufficient statistics) to include in the pre-aggregation. - * @param {any[]} args Source data table columns. The entries may be strings, - * column references, SQL expressions, or other string-coercible values. - * @param {(field: any) => string} avg Global average query generator. - * @returns An aggregate expression for calculating regression slopes over - * pre-aggregated data partitions. - */ -function regrSlopeExpr(aux, args, avg) { - const cov = covarianceExpr(aux, args, avg, null); - const varx = regrVarExpr(aux, 1, args, avg); - return agg`(${cov}) / ${varx}`; -} - -/** - * Generate an expression for calculating a regression intercept. The intercept - * is derived from the regression slope and average x and y values. As a - * side effect, this method adds columns for sufficient statistics to the - * input *aux* object. - * @param {object} aux An object for auxiliary columns (such as - * sufficient statistics) to include in the pre-aggregation. - * @param {any[]} args Source data table columns. The entries may be strings, - * column references, SQL expressions, or other string-coercible values. - * @param {(field: any) => string} avg Global average query generator. - * @returns An aggregate expression for calculating regression intercepts over - * pre-aggregated data partitions. - */ -function regrInterceptExpr(aux, args, avg) { - const ax = regrAvgXExpr(aux, args); - const ay = regrAvgYExpr(aux, args); - const m = regrSlopeExpr(aux, args, avg); - return agg`${ay} - (${m}) * ${ax}`; -} diff --git a/packages/core/src/util/selection-types.ts b/packages/core/src/util/selection-types.ts index 56f6f071..d2caf9ea 100644 --- a/packages/core/src/util/selection-types.ts +++ b/packages/core/src/util/selection-types.ts @@ -1,4 +1,4 @@ -import { SQLExpression } from '@uwdata/mosaic-sql'; +import { ExprNode } from '@uwdata/mosaic-sql'; import { MosaicClient } from '../MosaicClient.js'; /** @@ -127,7 +127,7 @@ export interface SelectionClause { * The predicate should apply filtering criteria consistent with this * clause's *value* property. */ - predicate: SQLExpression | null; + predicate: ExprNode | null; /** * Optional clause metadata that varies based on the selection type. * The metadata can be used to optimize selection queries, for example diff --git a/packages/core/src/util/throttle.js b/packages/core/src/util/throttle.js index a3493c33..dd33755d 100644 --- a/packages/core/src/util/throttle.js +++ b/packages/core/src/util/throttle.js @@ -16,15 +16,17 @@ export function throttle(callback, debounce = false) { let pending = NIL; function invoke(event) { - curr = callback(event).finally(() => { - if (next) { - const { value } = next; - next = null; - invoke(value); - } else { - curr = null; - } - }); + curr = callback(event) + .catch(() => {}) + .finally(() => { + if (next) { + const { value } = next; + next = null; + invoke(value); + } else { + curr = null; + } + }); } function enqueue(event) { diff --git a/packages/core/test/coordinator.test.js b/packages/core/test/coordinator.test.js index fd05c709..56227515 100644 --- a/packages/core/test/coordinator.test.js +++ b/packages/core/test/coordinator.test.js @@ -29,7 +29,7 @@ describe('coordinator', () => { }, }; - const coord = new Coordinator(connector); + const coord = new Coordinator(connector, { logger: null }); const r0 = coord.query('SELECT 0'); const r1 = coord.query('SELECT 1'); diff --git a/packages/core/test/preaggregator.test.js b/packages/core/test/preaggregator.test.js index e382f188..6f0704f5 100644 --- a/packages/core/test/preaggregator.test.js +++ b/packages/core/test/preaggregator.test.js @@ -1,10 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { - Query, argmax, argmin, avg, corr, count, covarPop, covariance, - isNotDistinct, literal, loadObjects, max, min, product, regrAvgX, - regrAvgY, regrCount, regrIntercept, regrR2, regrSXX, regrSXY, - regrSYY, regrSlope, stddev, stddevPop, sum, varPop, variance -} from '@uwdata/mosaic-sql'; +import { Query, add, argmax, argmin, avg, corr, count, covarPop, covariance, gt, isNotDistinct, literal, loadObjects, max, min, product, regrAvgX, regrAvgY, regrCount, regrIntercept, regrR2, regrSXX, regrSXY, regrSYY, regrSlope, stddev, stddevPop, sum, varPop, variance } from '@uwdata/mosaic-sql'; import { Coordinator, Selection } from '../src/index.js'; import { nodeConnector } from './util/node-connector.js'; import { TestClient } from './util/test-client.js'; @@ -34,13 +29,19 @@ async function run(measure) { return new Promise((resolve) => { let iter = 0; mc.connect(new TestClient(q, sel, { - queryResult: data => iter - ? resolve(Array.from(data)[0].measure) - : ++iter + queryResult(data) { + if (iter) { + resolve([ + Array.from(data)[0].measure, // query result + !!mc.preaggregator.entries.get(this) // optimized? + ]); + } + ++iter; + } })); sel.update({ source: 'test', - schema: { type: 'point' }, + meta: { type: 'point' }, predicate: isNotDistinct('dim', literal('b')) }); }); @@ -48,63 +49,93 @@ async function run(measure) { describe('PreAggregator', () => { it('supports count aggregate', async () => { - expect(await run(count())).toBe(3); - expect(await run(count('x'))).toBe(2); + expect(await run(count())).toStrictEqual([3, true]); + expect(await run(count('x'))).toStrictEqual([2, true]); }); + it('supports sum aggregate', async () => { - expect(await run(sum('x'))).toBe(7); + expect(await run(sum('x'))).toStrictEqual([7, true]); }); + it('supports avg aggregate', async () => { - expect(await run(avg('x'))).toBe(3.5); + expect(await run(avg('x'))).toStrictEqual([3.5, true]); }); + it('supports min aggregate', async () => { - expect(await run(min('x'))).toBe(3); + expect(await run(min('x'))).toStrictEqual([3, true]); }); + it('supports max aggregate', async () => { - expect(await run(max('x'))).toBe(4); + expect(await run(max('x'))).toStrictEqual([4, true]); }); + it('supports product aggregate', async () => { - expect(await run(product('x'))).toBe(12); + expect(await run(product('x'))).toStrictEqual([12, true]); }); + it('supports argmax aggregate', async () => { - expect(await run(argmax('dim', 'x'))).toBe('b'); + expect(await run(argmax('dim', 'x'))).toStrictEqual(['b', true]); }); + it('supports argmin aggregate', async () => { - expect(await run(argmin('dim', 'x'))).toBe('b'); + expect(await run(argmin('dim', 'x'))).toStrictEqual(['b', true]); }); + it('supports variance aggregate', async () => { - expect(await run(variance('x'))).toBe(0.5); + expect(await run(variance('x'))).toStrictEqual([0.5, true]); }); + it('supports varPop aggregate', async () => { - expect(await run(varPop('x'))).toBe(0.25); + expect(await run(varPop('x'))).toStrictEqual([0.25, true]); }); + it('supports stddev aggregate', async () => { - expect(await run(stddev('x'))).toBe(Math.sqrt(0.5)); + expect(await run(stddev('x'))).toStrictEqual([Math.sqrt(0.5), true]); }); + it('supports stddevPop aggregate', async () => { - expect(await run(stddevPop('x'))).toBe(Math.sqrt(0.25)); + expect(await run(stddevPop('x'))).toStrictEqual([Math.sqrt(0.25), true]); }); + it('supports covariance aggregate', async () => { - expect(await run(covariance('x', 'y'))).toBe(-0.5); - expect(await run(covariance('y', 'x'))).toBe(-0.5); + expect(await run(covariance('x', 'y'))).toStrictEqual([-0.5, true]); + expect(await run(covariance('y', 'x'))).toStrictEqual([-0.5, true]); }); + it('supports covarPop aggregate', async () => { - expect(await run(covarPop('x', 'y'))).toBe(-0.25); - expect(await run(covarPop('y', 'x'))).toBe(-0.25); + expect(await run(covarPop('x', 'y'))).toStrictEqual([-0.25, true]); + expect(await run(covarPop('y', 'x'))).toStrictEqual([-0.25, true]); }); + it('supports corr aggregate', async () => { - expect(await run(corr('x', 'y'))).toBe(-1); - expect(await run(corr('y', 'x'))).toBe(-1); + expect(await run(corr('x', 'y'))).toStrictEqual([-1, true]); + expect(await run(corr('y', 'x'))).toStrictEqual([-1, true]); }); + it('supports regression aggregates', async () => { - expect(await run(regrCount('y', 'x'))).toBe(2); - expect(await run(regrAvgX('y', 'x'))).toBe(3.5); - expect(await run(regrAvgY('y', 'x'))).toBe(6.5); - expect(await run(regrSXX('y', 'x'))).toBe(0.5); - expect(await run(regrSYY('y', 'x'))).toBe(0.5); - expect(await run(regrSXY('y', 'x'))).toBe(-0.5); - expect(await run(regrSlope('y', 'x'))).toBe(-1); - expect(await run(regrIntercept('y', 'x'))).toBe(10); - expect(await run(regrR2('y', 'x'))).toBe(1); + expect(await run(regrCount('y', 'x'))).toStrictEqual([2, true]); + expect(await run(regrAvgX('y', 'x'))).toStrictEqual([3.5, true]); + expect(await run(regrAvgY('y', 'x'))).toStrictEqual([6.5, true]); + expect(await run(regrSXX('y', 'x'))).toStrictEqual([0.5, true]); + expect(await run(regrSYY('y', 'x'))).toStrictEqual([0.5, true]); + expect(await run(regrSXY('y', 'x'))).toStrictEqual([-0.5, true]); + expect(await run(regrSlope('y', 'x'))).toStrictEqual([-1, true]); + expect(await run(regrIntercept('y', 'x'))).toStrictEqual([10, true]); + expect(await run(regrR2('y', 'x'))).toStrictEqual([1, true]); + }); + + it('supports multi-aggregate expressions', async () => { + expect(await run(add(sum('x'), product('x')))).toStrictEqual([19, true]); + }); + + it('supports aggregate filter clause', async () => { + expect(await run(sum('x').where(gt('x', 2)))).toStrictEqual([7, true]); + expect(await run(sum('x').where(gt('x', 3)))).toStrictEqual([4, true]); + expect(await run(sum('x').where(gt('x', 4)))).toStrictEqual([null, true]); + }); + + it('does not support distinct aggregates', async () => { + // should handle query, but through non-optimized route + expect(await run(count('x').distinct())).toStrictEqual([2, false]); }); }); diff --git a/packages/core/test/query-consolidator.test.js b/packages/core/test/query-consolidator.test.js index 4149e0be..0c406dbd 100644 --- a/packages/core/test/query-consolidator.test.js +++ b/packages/core/test/query-consolidator.test.js @@ -1,6 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { Query } from '@uwdata/mosaic-sql/src/Query.js'; -import { count, sum } from '@uwdata/mosaic-sql/src/aggregates.js'; +import { Query, count, sum } from '@uwdata/mosaic-sql'; import { consolidator } from '../src/QueryConsolidator.js'; import { Priority } from '../src/QueryManager.js'; import { voidCache } from '../src/util/cache.js'; diff --git a/packages/core/test/query-manager.test.js b/packages/core/test/query-manager.test.js index 40a142ac..2a55105f 100644 --- a/packages/core/test/query-manager.test.js +++ b/packages/core/test/query-manager.test.js @@ -31,9 +31,9 @@ describe('QueryManager', () => { // Mock the connector queryManager.connector({ - query: async ({ sql }) => { + query: ({ sql }) => { expect(sql).toBe('CREATE TABLE test (id INT)'); - return undefined; + return new Promise(() => {}); } }); diff --git a/packages/core/test/throttle.test.js b/packages/core/test/throttle.test.js index af1e71ac..aba07256 100644 --- a/packages/core/test/throttle.test.js +++ b/packages/core/test/throttle.test.js @@ -23,7 +23,7 @@ describe('throttle', () => { await wait(); expect(requests.length).toBe(1); - requests[0].fulfill('done'); + requests[0].fulfill('fulfilled'); await wait(); expect(requests.length).toBe(2); @@ -45,7 +45,7 @@ describe('throttle', () => { await wait(); expect(requests.length).toBe(1); - requests[0].reject('done'); + requests[0].reject('rejected'); await wait(); expect(requests.length).toBe(2); diff --git a/packages/inputs/src/Table.js b/packages/inputs/src/Table.js index ab7c97af..78c647f4 100644 --- a/packages/inputs/src/Table.js +++ b/packages/inputs/src/Table.js @@ -116,7 +116,7 @@ export class Table extends MosaicClient { } fields() { - return this.columns.map(name => column(this.from, name)); + return this.columns.map(name => column(name, this.from)); } fieldInfo(info) { diff --git a/packages/plot/src/index.js b/packages/plot/src/index.js index b6641eab..9ccadcad 100644 --- a/packages/plot/src/index.js +++ b/packages/plot/src/index.js @@ -29,4 +29,4 @@ export { Toggle } from './interactors/Toggle.js'; export { Legend } from './legend.js'; // transforms -export { bin } from './transforms/index.js'; +export { bin } from './transforms/bin.js'; diff --git a/packages/plot/src/interactors/Highlight.js b/packages/plot/src/interactors/Highlight.js index 35aa22e5..be10c26f 100644 --- a/packages/plot/src/interactors/Highlight.js +++ b/packages/plot/src/interactors/Highlight.js @@ -1,5 +1,5 @@ import { throttle } from '@uwdata/mosaic-core'; -import { and } from '@uwdata/mosaic-sql'; +import { and, isAggregateExpression } from '@uwdata/mosaic-sql'; import { getDatum } from './util/get-datum.js'; import { sanitizeStyles } from './util/sanitize-styles.js'; @@ -14,7 +14,7 @@ function configureMark(mark) { if (channel === 'orderby') { ordered = true; } else if (field) { - if (field.aggregate) { + if (isAggregateExpression(field)) { aggregate = true; } else { if (dims.has(as)) continue; @@ -95,7 +95,7 @@ async function predicateFunction(mark, selection) { const s = { __: and(pred) }; const q = mark.query(filter); (q.queries || [q]).forEach(q => { - q.groupby().length ? q.select(s) : q.$select(s); + q._groupby.length ? q.select(s) : q.setSelect(s); }); const data = await mark.coordinator.query(q); diff --git a/packages/plot/src/interactors/util/brush.js b/packages/plot/src/interactors/util/brush.js index a0b5ada8..8638b135 100644 --- a/packages/plot/src/interactors/util/brush.js +++ b/packages/plot/src/interactors/util/brush.js @@ -1,6 +1,4 @@ -import { - brush as d3_brush, brushX as d3_brushX, brushY as d3_brushY, select -} from 'd3'; +import { brush as d3_brush, brushX as d3_brushX, brushY as d3_brushY, select } from 'd3'; import { patchScreenCTM } from './patchScreenCTM.js'; function wrap(brush) { diff --git a/packages/plot/src/interactors/util/get-field.js b/packages/plot/src/interactors/util/get-field.js index d2e3aa4b..c6b37c1f 100644 --- a/packages/plot/src/interactors/util/get-field.js +++ b/packages/plot/src/interactors/util/get-field.js @@ -1,5 +1,14 @@ +import { isNode } from '@uwdata/mosaic-sql'; + function extractField(field) { - return field?.basis || field; + if (isNode(field)) { + if (field.type === 'COLUMN_REF') { + return field.column; + } else if (field.type === 'AGGREGATE') { + return field.args[0] ?? field; + } + } + return field; } export function getField(mark, channel) { diff --git a/packages/plot/src/marks/ConnectedMark.js b/packages/plot/src/marks/ConnectedMark.js index b9cb427f..0ab7d4df 100644 --- a/packages/plot/src/marks/ConnectedMark.js +++ b/packages/plot/src/marks/ConnectedMark.js @@ -1,4 +1,4 @@ -import { Query, argmax, argmin, max, min, sql } from '@uwdata/mosaic-sql'; +import { m4 } from '@uwdata/mosaic-sql'; import { binExpr } from './util/bin-expr.js'; import { filteredExtent } from './util/extent.js'; import { Mark } from './Mark.js'; @@ -6,7 +6,7 @@ import { Mark } from './Mark.js'; export class ConnectedMark extends Mark { constructor(type, source, encodings) { const dim = type.endsWith('X') ? 'y' : type.endsWith('Y') ? 'x' : null; - const req = dim ? { [dim]: ['min', 'max'] } : undefined; + const req = dim ? { [dim]: ['count', 'min', 'max'] } : undefined; super(type, source, encodings, req); this.dim = dim; } @@ -18,23 +18,24 @@ export class ConnectedMark extends Mark { */ query(filter = []) { const { plot, dim, source } = this; - const { optimize = true } = source.options || {}; + let optimize = source.options?.optimize; const q = super.query(filter); if (!dim) return q; const ortho = dim === 'x' ? 'y' : 'x'; const value = this.channelField(ortho, { exact: true })?.as; - const { field, as, type, min, max } = this.channelField(dim); + const { field, as, type, count, min, max } = this.channelField(dim); const isContinuous = type === 'date' || type === 'number'; + const size = dim === 'x' ? plot.innerWidth() : plot.innerHeight(); + optimize ??= (count / size) > 10; // threshold for applying M4 + if (optimize && isContinuous && value) { - // TODO: handle stacked data! - const size = dim === 'x' ? plot.innerWidth() : plot.innerHeight(); + // TODO: handle stacked data const [lo, hi] = filteredExtent(filter, field) || [min, max]; const [expr] = binExpr(this, dim, size, [lo, hi], 1, as); - const cols = q.select() - // @ts-ignore - .map(c => c.as) + const cols = q._select + .map(c => c.alias) .filter(c => c !== as && c !== value); return m4(q, expr, as, value, cols); } else { @@ -42,27 +43,3 @@ export class ConnectedMark extends Mark { } } } - -/** - * M4 is an optimization for value-preserving time-series aggregation - * (https://www.vldb.org/pvldb/vol7/p797-jugel.pdf). This implementation uses - * an efficient version with a single scan and the aggregate function - * argmin and argmax, following https://arxiv.org/pdf/2306.03714.pdf. - */ -function m4(input, bin, x, y, cols = []) { - const pixel = sql`FLOOR(${bin})::INTEGER`; - - const q = (sel) => Query - .from(input) - .select(sel) - .groupby(pixel, cols); - - return Query - .union( - q([{ [x]: min(x), [y]: argmin(y, x) }, ...cols]), - q([{ [x]: max(x), [y]: argmax(y, x) }, ...cols]), - q([{ [x]: argmin(x, y), [y]: min(y) }, ...cols]), - q([{ [x]: argmax(x, y), [y]: max(y) }, ...cols]) - ) - .orderby(cols, x); -} diff --git a/packages/plot/src/marks/DenseLineMark.js b/packages/plot/src/marks/DenseLineMark.js index bf8cc798..fc5ac7eb 100644 --- a/packages/plot/src/marks/DenseLineMark.js +++ b/packages/plot/src/marks/DenseLineMark.js @@ -1,4 +1,4 @@ -import { Query, and, count, isNull, isBetween, sql, sum } from '@uwdata/mosaic-sql'; +import { Query, and, lineDensity } from '@uwdata/mosaic-sql'; import { binExpr } from './util/bin-expr.js'; import { extentX, extentY } from './util/extent.js'; import { handleParam } from './util/handle-param.js'; @@ -52,101 +52,21 @@ function stripXY(mark, filter) { if (Array.isArray(filter) && !filter.length) return filter; // get column expressions for x and y encoding channels - const { column: xc } = mark.channelField('x'); - const { column: yc } = mark.channelField('y'); + const xc = mark.channelField('x').column; + const yc = mark.channelField('y').column; // test if a range predicate filters the x or y channels const test = p => { - const col = `${p.field}`; - return p.op !== 'BETWEEN' || col !== xc && col !== yc; + const col = `${p.expr}`; + return p.type !== 'BETWEEN' || (col !== xc && col !== yc); }; // filter boolean 'and' operations const filterAnd = p => p.op === 'AND' - ? and(p.children.filter(c => test(c))) + ? and(p.clauses.filter(c => test(c))) : p; return Array.isArray(filter) ? filter.filter(p => test(p)).map(p => filterAnd(p)) : filterAnd(filter); } - -function lineDensity( - q, x, y, z, xn, yn, - groupby = [], normalize = true -) { - // select x, y points binned to the grid - q.select({ - x: sql`FLOOR(${x})::INTEGER`, - y: sql`FLOOR(${y})::INTEGER` - }); - - // select line segment end point pairs - const groups = groupby.concat(z); - const pairPart = groups.length ? `PARTITION BY ${groups.join(', ')} ` : ''; - const pairs = Query - .from(q) - .select(groups, { - x0: 'x', - y0: 'y', - dx: sql`(lead(x) OVER sw - x)`, - dy: sql`(lead(y) OVER sw - y)` - }) - .window({ sw: sql`${pairPart}ORDER BY x ASC` }) - .qualify(and( - sql`(x0 < ${xn} OR x0 + dx < ${xn})`, - sql`(y0 < ${yn} OR y0 + dy < ${yn})`, - sql`(x0 > 0 OR x0 + dx > 0)`, - sql`(y0 > 0 OR y0 + dy > 0)` - )); - - // indices to join against for rasterization - // generate the maximum number of indices needed - const num = Query - .select({ x: sql`GREATEST(MAX(ABS(dx)), MAX(ABS(dy)))` }) - .from('pairs'); - const indices = Query.select({ i: sql`UNNEST(range((${num})))::INTEGER` }); - - // rasterize line segments - const raster = Query.unionAll( - Query - .select(groups, { - x: sql`x0 + i`, - y: sql`y0 + ROUND(i * dy / dx::FLOAT)::INTEGER` - }) - .from('pairs', 'indices') - .where(sql`ABS(dy) <= ABS(dx) AND i < ABS(dx)`), - Query - .select(groups, { - x: sql`x0 + ROUND(SIGN(dy) * i * dx / dy::FLOAT)::INTEGER`, - y: sql`y0 + SIGN(dy) * i` - }) - .from('pairs', 'indices') - .where(sql`ABS(dy) > ABS(dx) AND i < ABS(dy)`), - Query - .select(groups, { x: 'x0', y: 'y0' }) - .from('pairs') - .where(isNull('dx')) - ); - - // filter raster, normalize columns for each series - const pointPart = ['x'].concat(groups).join(', '); - const points = Query - .from('raster') - .select(groups, 'x', 'y', - normalize - ? { w: sql`1.0 / COUNT(*) OVER (PARTITION BY ${pointPart})` } - : null - ) - .where(and(isBetween('x', [0, xn], true), isBetween('y', [0, yn], true))); - - // sum normalized, rasterized series into output grids - return Query - .with({ pairs, indices, raster, points }) - .from('points') - .select(groupby, { - index: sql`x + y * ${xn}::INTEGER`, - density: normalize ? sum('w') : count() - }) - .groupby('index', groupby); -} diff --git a/packages/plot/src/marks/Density1DMark.js b/packages/plot/src/marks/Density1DMark.js index d61153d0..d7f45912 100644 --- a/packages/plot/src/marks/Density1DMark.js +++ b/packages/plot/src/marks/Density1DMark.js @@ -1,5 +1,5 @@ import { toDataColumns } from '@uwdata/mosaic-core'; -import { Query, gt, isBetween, sql, sum } from '@uwdata/mosaic-sql'; +import { binLinear1d, isBetween } from '@uwdata/mosaic-sql'; import { Transient } from '../symbols.js'; import { binExpr } from './util/bin-expr.js'; import { dericheConfig, dericheConv1d } from './util/density.js'; @@ -87,25 +87,3 @@ export class Density1DMark extends Mark { return [{ type, data: { length }, options }]; } } - -function binLinear1d(q, p, density) { - const w = density ? `* ${density}` : ''; - - const u = q.clone().select({ - p, - i: sql`FLOOR(p)::INTEGER`, - w: sql`(FLOOR(p) + 1 - p)${w}` - }); - - const v = q.clone().select({ - p, - i: sql`FLOOR(p)::INTEGER + 1`, - w: sql`(p - FLOOR(p))${w}` - }); - - return Query - .from(Query.unionAll(u, v)) - .select({ index: 'i', density: sum('w') }) - .groupby('index') - .having(gt('density', 0)); -} diff --git a/packages/plot/src/marks/ErrorBarMark.js b/packages/plot/src/marks/ErrorBarMark.js index c1cf5e56..124cd26b 100644 --- a/packages/plot/src/marks/ErrorBarMark.js +++ b/packages/plot/src/marks/ErrorBarMark.js @@ -1,5 +1,5 @@ import { toDataColumns } from '@uwdata/mosaic-core'; -import { avg, count, stddev } from '@uwdata/mosaic-sql'; +import { avg, count, div, sqrt, stddev } from '@uwdata/mosaic-sql'; import { erfinv } from './util/stats.js'; import { Mark, markPlotSpec, markQuery } from './Mark.js'; import { handleParam } from './util/handle-param.js'; @@ -23,8 +23,7 @@ export class ErrorBarMark extends Mark { const { channels, field, source: { table } } = this; const fields = channels.concat([ { field: avg(field), as: '__avg__' }, - { field: count(field), as: '__n__', }, - { field: stddev(field), as: '__sd__' } + { field: div(stddev(field), sqrt(count(field))), as: '__se__', } ]); return markQuery(fields, table).where(filter); } @@ -39,10 +38,10 @@ export class ErrorBarMark extends Mark { // compute confidence interval channels const p = Math.SQRT2 * erfinv(ci); - const { columns: { __avg__: u, __sd__: s, __n__: n } } = data; + const { columns: { __avg__: u, __se__: s } } = data; const options = { - [`${dim}1`]: u.map((u, i) => u - p * s[i] / Math.sqrt(n[i])), - [`${dim}2`]: u.map((u, i) => u + p * s[i] / Math.sqrt(n[i])) + [`${dim}1`]: u.map((u, i) => u - p * s[i]), + [`${dim}2`]: u.map((u, i) => u + p * s[i]) }; return markPlotSpec(type, detail, channels, data, options); diff --git a/packages/plot/src/marks/Grid2DMark.js b/packages/plot/src/marks/Grid2DMark.js index c2d0e0a9..ef3b7cda 100644 --- a/packages/plot/src/marks/Grid2DMark.js +++ b/packages/plot/src/marks/Grid2DMark.js @@ -1,6 +1,6 @@ import { interpolatorBarycentric, interpolateNearest, interpolatorRandomWalk } from '@observablehq/plot'; import { toDataColumns } from '@uwdata/mosaic-core'; -import { Query, count, isBetween, lt, lte, neq, sql, sum } from '@uwdata/mosaic-sql'; +import { Query, bin2d, binLinear2d, collectColumns, count, isAggregateExpression, isBetween, lt, lte, sum } from '@uwdata/mosaic-sql'; import { Transient } from '../symbols.js'; import { binExpr } from './util/bin-expr.js'; import { dericheConfig, dericheConv2d } from './util/density.js'; @@ -99,7 +99,7 @@ export class Grid2DMark extends Mark { for (const c of channels) { if (Object.hasOwn(c, 'field')) { const { as, channel, field } = c; - if (field.aggregate) { + if (isAggregateExpression(field)) { // include custom aggregate aggrMap[channel] = field; densityMap[channel] = true; @@ -123,7 +123,7 @@ export class Grid2DMark extends Mark { // if no aggregates, default to count density if (!aggr.length) { aggr.push(DENSITY); - aggrMap.density = count(); + aggrMap[DENSITY] = count(); } // generate grid binning query @@ -131,10 +131,11 @@ export class Grid2DMark extends Mark { if (aggr.length > 1) { throw new Error('Linear binning not applicable to multiple aggregates.'); } - if (!aggrMap.density) { + if (!aggrMap[DENSITY]) { throw new Error('Linear binning not applicable to custom aggregates.'); } - return binLinear2d(q, x, y, aggrMap[DENSITY], nx, groupby); + const weight = collectColumns(aggrMap[DENSITY])[0]; + return binLinear2d(q, x, y, weight, nx, groupby); } else { return bin2d(q, x, y, aggrMap, nx, groupby); } @@ -228,47 +229,3 @@ function maybeInterpolate(interpolate = 'none') { } throw new Error(`invalid interpolate: ${interpolate}`); } - -function bin2d(q, xp, yp, aggs, xn, groupby) { - return q - .select({ - index: sql`FLOOR(${xp})::INTEGER + FLOOR(${yp})::INTEGER * ${xn}`, - ...aggs - }) - .groupby('index', groupby); -} - -function binLinear2d(q, xp, yp, density, xn, groupby) { - const w = density?.column ? `* ${density.column}` : ''; - const subq = (i, w) => q.clone().select({ xp, yp, i, w }); - - // grid[xu + yu * xn] += (xv - xp) * (yv - yp) * wi; - const a = subq( - sql`FLOOR(xp)::INTEGER + FLOOR(yp)::INTEGER * ${xn}`, - sql`(FLOOR(xp)::INTEGER + 1 - xp) * (FLOOR(yp)::INTEGER + 1 - yp)${w}` - ); - - // grid[xu + yv * xn] += (xv - xp) * (yp - yu) * wi; - const b = subq( - sql`FLOOR(xp)::INTEGER + (FLOOR(yp)::INTEGER + 1) * ${xn}`, - sql`(FLOOR(xp)::INTEGER + 1 - xp) * (yp - FLOOR(yp)::INTEGER)${w}` - ); - - // grid[xv + yu * xn] += (xp - xu) * (yv - yp) * wi; - const c = subq( - sql`FLOOR(xp)::INTEGER + 1 + FLOOR(yp)::INTEGER * ${xn}`, - sql`(xp - FLOOR(xp)::INTEGER) * (FLOOR(yp)::INTEGER + 1 - yp)${w}` - ); - - // grid[xv + yv * xn] += (xp - xu) * (yp - yu) * wi; - const d = subq( - sql`FLOOR(xp)::INTEGER + 1 + (FLOOR(yp)::INTEGER + 1) * ${xn}`, - sql`(xp - FLOOR(xp)::INTEGER) * (yp - FLOOR(yp)::INTEGER)${w}` - ); - - return Query - .from(Query.unionAll(a, b, c, d)) - .select({ index: 'i', density: sum('w') }, groupby) - .groupby('index', groupby) - .having(neq('density', 0)); -} diff --git a/packages/plot/src/marks/HexbinMark.js b/packages/plot/src/marks/HexbinMark.js index 75c2b2d0..bcf91ea4 100644 --- a/packages/plot/src/marks/HexbinMark.js +++ b/packages/plot/src/marks/HexbinMark.js @@ -1,4 +1,4 @@ -import { Query, isNotNull, sql } from '@uwdata/mosaic-sql'; +import { Query, abs, add, and, bitAnd, column, cond, div, float64, gt, int32, isAggregateExpression, isNotNull, lt, mul, neq, pow, round, sub } from '@uwdata/mosaic-sql'; import { Transient } from '../symbols.js'; import { extentX, extentY, xyext } from './util/extent.js'; import { Mark } from './Mark.js'; @@ -27,20 +27,20 @@ export class HexbinMark extends Mark { // Extract channel information, update top-level query // and extract dependent columns for aggregates - let x, y; + let xc, yc; const dims = new Set; const cols = {}; for (const c of channels) { if (c.channel === 'orderby') { // ignore ordering, as we will aggregate } else if (c.channel === 'x') { - x = c; + xc = c; } else if (c.channel === 'y') { - y = c; + yc = c; } else if (Object.hasOwn(c, 'field')) { const { as, field } = c; cols[as] = field; - if (!field.aggregate) { + if (!isAggregateExpression(field)) { dims.add(as); } } @@ -54,33 +54,63 @@ export class HexbinMark extends Mark { // margins as this is what Observable Plot does. const ox = 0.5 - plot.getAttribute('marginLeft'); const oy = 0 - plot.getAttribute('marginTop'); - const dx = `${binWidth}::DOUBLE`; - const dy = `${binWidth * (1.5 / Math.sqrt(3))}::DOUBLE`; - const xr = `${plot.innerWidth() / (x2 - x1)}::DOUBLE`; - const yr = `${plot.innerHeight() / (y2 - y1)}::DOUBLE`; + const dx = float64(binWidth); + const dy = float64(binWidth * (1.5 / Math.sqrt(3))); + const xr = float64(plot.innerWidth() / (x2 - x1)); + const yr = float64(plot.innerHeight() / (y2 - y1)); + + // column references + const x = column('_x'); + const y = column('_y'); + const px = column('_px'); + const py = column('_py'); + const pi = column('_pi'); + const pj = column('_pj'); + const tt = column('_tt'); // Top-level query maps from screen space back to data values. // Doing so ensures that Plot generates correct data-driven scales. return Query.select({ - [x.as]: sql`${x1}::DOUBLE + ((_x + 0.5 * (_y & 1)) * ${dx} + ${ox})::DOUBLE / ${xr}`, - [y.as]: sql`${y2}::DOUBLE - (_y * ${dy} + ${oy})::DOUBLE / ${yr}`, + [xc.as]: add( + float64(x1), + div(add(mul(add(x, mul(0.5, bitAnd(y, 1))), dx), ox), xr) + ), + [yc.as]: sub(float64(y2), div(add(mul(y, dy), oy), yr)), ...cols }) - .groupby('_x', '_y', ...dims) + .groupby(x, y, ...dims) .from( // Subquery performs hex binning in screen space and also passes // original columns through (the DB should optimize this). Query.select({ - _py: sql`(${yr} * (${y2}::DOUBLE - ${y.field}) - ${oy}) / ${dy}`, - _pj: sql`ROUND(_py)::INTEGER`, - _px: sql`(${xr} * (${x.field} - ${x1}::DOUBLE) - ${ox}) / ${dx} - 0.5 * (_pj & 1)`, - _pi: sql`ROUND(_px)::INTEGER`, - _tt: sql`ABS(_py-_pj) * 3 > 1 AND (_px-_pi)**2 + (_py-_pj)**2 > (_px - _pi - 0.5 * CASE WHEN _px < _pi THEN -1 ELSE 1 END)**2 + (_py - _pj - CASE WHEN _py < _pj THEN -1 ELSE 1 END)**2`, - _x: sql`CASE WHEN _tt THEN (_pi + (CASE WHEN _px < _pi THEN -0.5 ELSE 0.5 END) + (CASE WHEN _pj & 1 <> 0 THEN 0.5 ELSE -0.5 END))::INTEGER ELSE _pi END`, - _y: sql`CASE WHEN _tt THEN (_pj + CASE WHEN _py < _pj THEN -1 ELSE 1 END)::INTEGER ELSE _pj END` + [py]: div(mul(yr, sub(sub(y2, yc.field), oy)), dy), + [pj]: int32(round(py)), + [px]: sub( + div(sub(mul(xr, sub(xc.field, x1)), ox), dx), + mul(0.5, bitAnd(pj, 1)) + ), + [pi]: int32(round(px)), + [tt]: and( + gt(mul(abs(sub(py, pj)), 3), 1), + gt( + add(pow(sub(px, pi), 2), pow(sub(py, pj), 2)), + add( + pow(sub(sub(px, pi), mul(0.5, cond(lt(px, pi), -1, 1))), 2), + pow(sub(sub(py, pj), cond(lt(py, pj), -1, 1)), 2) + ) + ) + ), + [x]: cond(tt, + int32(add( + add(pi, cond(lt(px, pi), -0.5, 0.5)), + cond(neq(bitAnd(pj, 1), 0), 0.5, -0.5) + )), + pi + ), + [y]: cond(tt, int32(add(pj, cond(lt(py, pj), -1, 1))), pj) }, '*') .from(source.table) - .where(isNotNull(x.field), isNotNull(y.field), filter) + .where(isNotNull(xc.field), isNotNull(yc.field), filter) ); } } diff --git a/packages/plot/src/marks/Mark.js b/packages/plot/src/marks/Mark.js index 7c17ac5f..36347c2a 100644 --- a/packages/plot/src/marks/Mark.js +++ b/packages/plot/src/marks/Mark.js @@ -1,5 +1,5 @@ import { MosaicClient, toDataColumns } from '@uwdata/mosaic-core'; -import { Query, Ref, column, isParamLike } from '@uwdata/mosaic-sql'; +import { Query, collectParams, column, isAggregateExpression, isColumnRef, isNode, isParamLike } from '@uwdata/mosaic-sql'; import { isColor } from './util/is-color.js'; import { isConstantOption } from './util/is-constant-option.js'; import { isSymbol } from './util/is-symbol.js'; @@ -15,7 +15,7 @@ const isFieldObject = (channel, field) => { const fieldEntry = (channel, field) => ({ channel, field, - as: field instanceof Ref ? field.column : channel + as: isColumnRef(field) ? field.column : channel }); const valueEntry = (channel, value) => ({ channel, value }); @@ -62,20 +62,16 @@ export class Mark extends MosaicClient { channels.push(fieldEntry(channel, column(entry))); } } else if (isParamLike(entry)) { - if (Array.isArray(entry.columns)) { - // we currently duck-type to having a columns array - // as a check that this is SQLExpression-compatible - channels.push(fieldEntry(channel, entry)); - params.add(entry); - } else { - const c = valueEntry(channel, entry.value); - channels.push(c); - entry.addEventListener('value', value => { - // update immediately, the value is simply passed to Plot - c.value = value; - return this.update(); - }); - } + const c = valueEntry(channel, entry.value); + channels.push(c); + entry.addEventListener('value', value => { + // update immediately, the value is simply passed to Plot + c.value = value; + return this.update(); + }); + } else if (isNode(entry)) { + collectParams(entry).forEach(p => params.add(p)); + channels.push(fieldEntry(channel, entry)) } else if (type === 'object' && isFieldObject(channel, entry)) { channels.push(fieldEntry(channel, entry)); } else if (entry !== undefined) { @@ -225,7 +221,7 @@ export function markQuery(channels, table, skip = []) { if (channel === 'orderby') { q.orderby(c.value); } else if (field) { - if (field.aggregate) { + if (isAggregateExpression(field)) { aggr = true; } else { if (dims.has(as)) continue; diff --git a/packages/plot/src/marks/RegressionMark.js b/packages/plot/src/marks/RegressionMark.js index 67c92d27..fc30783d 100644 --- a/packages/plot/src/marks/RegressionMark.js +++ b/packages/plot/src/marks/RegressionMark.js @@ -1,7 +1,7 @@ import { range } from 'd3'; import { toDataColumns } from '@uwdata/mosaic-core'; import { - Query, max, min, castDouble, isNotNull, + Query, max, min, float64, isNotNull, regrIntercept, regrSlope, regrCount, regrSYY, regrSXX, regrAvgX } from '@uwdata/mosaic-sql'; @@ -43,8 +43,8 @@ export class RegressionMark extends Mark { ssy: regrSYY(y, x), ssx: regrSXX(y, x), xm: regrAvgX(y, x), - x0: castDouble(min(x).where(isNotNull(y))), - x1: castDouble(max(x).where(isNotNull(y))) + x0: float64(min(x).where(isNotNull(y))), + x1: float64(max(x).where(isNotNull(y))) }) .select(groupby) .groupby(groupby); diff --git a/packages/plot/src/marks/util/bin-expr.js b/packages/plot/src/marks/util/bin-expr.js index 4b5fb0d9..a92b5179 100644 --- a/packages/plot/src/marks/util/bin-expr.js +++ b/packages/plot/src/marks/util/bin-expr.js @@ -1,12 +1,12 @@ -import { sql } from '@uwdata/mosaic-sql'; +import { bin1d } from '@uwdata/mosaic-sql'; import { channelScale } from './channel-scale.js'; /** * Generates a SQL expression for 1D pixel-level binning. * Adjusts for scale transformations (log, sqrt, ...). * Returns a [binExpression, field] array, where field is the - * input value that is binned. Often the field is just a column - * name. For time data, fields are mapped to numerical timestamps. + * input value being binned. Often the field is just a column + * name. For time data, fields are mapped to millisecond timestamps. */ export function binExpr(mark, channel, n, extent, pad = 1, expr) { // get base expression, the channel field unless otherwise given @@ -21,10 +21,5 @@ export function binExpr(mark, channel, n, extent, pad = 1, expr) { const [lo, hi] = extent.map(v => apply(v)); const v = sqlApply(expr); const f = type === 'time' || type === 'utc' ? v : expr; - const d = hi === lo ? 0 : (n - pad) / (hi - lo); - const s = d !== 1 ? ` * ${d}::DOUBLE` : ''; - const bin = reverse - ? sql`(${hi} - ${v}::DOUBLE)${s}` - : sql`(${v}::DOUBLE - ${lo})${s}`; - return [bin, f]; + return [bin1d(v, lo, hi, n - pad, reverse), f]; } diff --git a/packages/plot/src/marks/util/extent.js b/packages/plot/src/marks/util/extent.js index 6a622c90..08695e24 100644 --- a/packages/plot/src/marks/util/extent.js +++ b/packages/plot/src/marks/util/extent.js @@ -1,5 +1,6 @@ import { scaleLinear } from 'd3'; import { Fixed, Transient } from '../../symbols.js'; +import { isNode, walk } from '@uwdata/mosaic-sql'; export const xext = { x: ['min', 'max'] }; export const yext = { y: ['min', 'max'] }; @@ -36,19 +37,14 @@ export function filteredExtent(filter, column) { let lo; let hi; - const visitor = (type, clause) => { - if (type === 'BETWEEN' && `${clause.field}` === column) { - const { range } = clause; - if (range && (lo == null || range[0] < lo)) lo = range[0]; - if (range && (hi == null || range[1] > hi)) hi = range[1]; - } - }; - if (Array.isArray(filter)) { - filter.forEach(p => p.visit?.(visitor)); - } else if (filter.visit) { - filter.visit(visitor); - } + [filter].flat().forEach(p => walk(p, (node) => { + if (node.type === 'BETWEEN' && `${node.expr}` === column) { + const extent = (node.extent ?? []).map(v => isNode(v) ? v.value : v); + if (lo == null || extent[0] < lo) lo = extent[0]; + if (hi == null || extent[1] > hi) hi = extent[1]; + } + })); return lo != null && hi != null && lo !== hi ? [lo, hi] : undefined; } diff --git a/packages/plot/src/plot-renderer.js b/packages/plot/src/plot-renderer.js index f05af1ab..5aba7f3b 100644 --- a/packages/plot/src/plot-renderer.js +++ b/packages/plot/src/plot-renderer.js @@ -1,3 +1,4 @@ +// import { isColumnRef, isNode } from '@uwdata/mosaic-sql'; import * as Plot from '@observablehq/plot'; import { setAttributes } from './plot-attributes.js'; import { Fixed } from './symbols.js'; @@ -105,15 +106,15 @@ function inferLabel(key, spec, marks) { // check for consistent columns / labels let candCol; let candLabel; - let type; for (let i = 0; i < fields.length; ++i) { - const { column, label } = fields[i] || {}; + // const { column, label = `${fields[i]}` } = fields[i] || {}; + let column; + const label = fieldLabel(fields[i]); if (column === undefined && label === undefined) { continue; } else if (candCol === undefined && candLabel === undefined) { candCol = column; candLabel = label; - type = getType(marks[i].data, key) || 'number'; } else if (candLabel !== label) { candLabel = undefined; } else if (candCol !== column) { @@ -123,21 +124,27 @@ function inferLabel(key, spec, marks) { let candidate = candLabel || candCol; if (candidate === undefined) return; - // adjust candidate label formatting - if ((type === 'number' || type === 'date') && (key === 'x' || key === 'y')) { - if (scale.percent) candidate = `${candidate} (%)`; - const order = (key === 'x' ? 1 : -1) * (scale.reverse ? -1 : 1); - if (key === 'x' || scale.labelAnchor === 'center') { - candidate = (key === 'x') === order < 0 ? `← ${candidate}` : `${candidate} →`; - } else { - candidate = `${order < 0 ? '↑ ' : '↓ '}${candidate}`; - } - } - // add label to spec spec[key] = { ...scale, label: candidate }; } +function fieldLabel(field) { + if (!field) return undefined; + switch (field.type) { + case 'COLUMN_REF': return field.column; + case 'CAST': return fieldLabel(field.expr); + case 'FUNCTION': + if (field.name === 'make_date') return 'date'; + break; + } + return exprLabel(field); +} + +function exprLabel(field) { + const s = `${field}`.replaceAll('"', '').replaceAll('*', ''); + return s.endsWith('()') ? s.slice(0, -2) : s; +} + function annotatePlot(svg, indices) { const facets = svg.querySelectorAll('g[aria-label="facet"]'); if (facets.length) { @@ -161,16 +168,3 @@ function annotateMarks(svg, indices) { } } } - -function getType(data, channel) { - if (!data) return; - const { columns } = data; - const col = columns[channel] ?? columns[channel+'1'] ?? columns[channel+'2']; - if (col) { - for (const v of col) { - if (v != null) { - return v instanceof Date ? 'date' : typeof v; - } - } - } -} diff --git a/packages/plot/src/transforms/bin.js b/packages/plot/src/transforms/bin.js index 588b5690..4f0dd9ef 100644 --- a/packages/plot/src/transforms/bin.js +++ b/packages/plot/src/transforms/bin.js @@ -1,4 +1,4 @@ -import { dateBin } from '@uwdata/mosaic-sql'; +import { ExprNode, add, dateBin, div, float64, floor, interval, mul, sub } from '@uwdata/mosaic-sql'; import { Transform } from '../symbols.js'; import { channelScale } from '../marks/util/channel-scale.js'; import { bins } from './bin-step.js'; @@ -16,12 +16,12 @@ export function bin(field, options = {}) { const fn = (mark, channel) => { if (hasExtent(mark, channel)) { return { - [`${channel}1`]: binField(mark, channel, field, options), - [`${channel}2`]: binField(mark, channel, field, { ...options, offset: 1 }) + [`${channel}1`]: binNode(mark, channel, field, options), + [`${channel}2`]: binNode(mark, channel, field, { ...options, offset: 1 }) }; } else { return { - [channel]: binField(mark, channel, field, options) + [channel]: binNode(mark, channel, field, options) }; } }; @@ -29,43 +29,54 @@ export function bin(field, options = {}) { return fn; } -function binField(mark, channel, column, options) { - return { - column, - label: column, - get columns() { return [column]; }, - get basis() { return column; }, - get stats() { return { column, stats: ['min', 'max'] }; }, - toString() { - const { type, min, max } = mark.channelField(channel); - const { interval: i, steps, offset = 0 } = options; - const interval = i ?? ( - type === 'date' || hasTimeScale(mark, channel) ? 'date' : 'number' - ); - - if (interval === 'number') { - // perform number binning - const { apply, sqlApply, sqlInvert } = channelScale(mark, channel); - const b = bins(apply(min), apply(max), options); - const col = sqlApply(column); - const base = b.min === 0 ? col : `(${col} - ${b.min})`; - const alpha = `${(b.max - b.min) / b.steps}::DOUBLE`; - const off = offset ? `${offset} + ` : ''; - const expr = `${b.min} + ${alpha} * (${off}FLOOR(${base} / ${alpha}))`; - return `${sqlInvert(expr)}`; - } else { - // perform date/time binning - const { interval: unit, step = 1 } = interval === 'date' - ? timeInterval(min, max, steps || 40) - : options; - const off = offset ? ` + INTERVAL ${offset * step} ${unit}` : ''; - return `(${dateBin(column, unit, step)}${off})`; - } - } - }; +function binNode(mark, channel, column, options) { + return new BinTransformNode(column, mark, channel, options); } function hasTimeScale(mark, channel) { const scale = mark.plot.getAttribute(`${channel}Scale`); return scale === 'utc' || scale === 'time'; } + +class BinTransformNode extends ExprNode { + constructor(column, mark, channel, options) { + super('COLUMN_REF'); + this.column = column; + this.mark = mark; + this.channel = channel; + this.options = options; + } + + get stats() { + return { column: this.column, stats: ['min', 'max'] }; + } + + toString() { + const { mark, channel, column, options } = this; + const { type, min, max } = mark.channelField(channel); + const { interval: i, steps, offset = 0 } = options; + const ival = i ?? ( + type === 'date' || hasTimeScale(mark, channel) ? 'date' : 'number' + ); + + let result; + if (ival === 'number') { + // perform number binning + const { apply, sqlApply, sqlInvert } = channelScale(mark, channel); + const b = bins(apply(min), apply(max), options); + const col = sqlApply(column); + const alpha = float64((b.max - b.min) / b.steps); + const bin = floor(div(b.min === 0 ? col : sub(col, b.min), alpha)); + const expr = add(b.min, mul(alpha, offset ? add(offset, bin) : bin)); + result = sqlInvert(expr); + } else { + // perform date/time binning + const { interval: unit, step = 1 } = ival === 'date' + ? timeInterval(min, max, steps || 40) + : options; + const bin = dateBin(column, unit, step); + result = offset ? add(bin, interval(unit, offset * step)) : bin; + } + return `${result}`; + } +} diff --git a/packages/plot/src/transforms/index.js b/packages/plot/src/transforms/index.js deleted file mode 100644 index 0a126a72..00000000 --- a/packages/plot/src/transforms/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export { - bin -} from './bin.js'; diff --git a/packages/spec/src/ast/ExpressionNode.js b/packages/spec/src/ast/ExpressionNode.js index aac098a6..43257fbd 100644 --- a/packages/spec/src/ast/ExpressionNode.js +++ b/packages/spec/src/ast/ExpressionNode.js @@ -1,14 +1,11 @@ -import { AGG, EXPRESSION, SQL } from '../constants.js'; +import { EXPRESSION, SQL } from '../constants.js'; import { ASTNode } from './ASTNode.js'; -export function parseExpression(spec, ctx) { - const { label } = spec; - const key = spec[SQL] ? SQL - : spec[AGG] ? AGG - : ctx.error('Unrecognized expression type', spec); +const tokenRegExp = /(\\'|\\"|"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\$\w+)/g; - const expr = spec[key]; - const tokens = expr.split(/(\\'|\\"|"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\$\w+)/g); +export function parseExpression(spec, ctx) { + const expr = spec[SQL]; + const tokens = expr.split(tokenRegExp); const spans = ['']; const params = []; @@ -22,29 +19,26 @@ export function parseExpression(spec, ctx) { } } - return new ExpressionNode(expr, spans, params, label, key === AGG); + return new ExpressionNode(expr, spans, params); } export class ExpressionNode extends ASTNode { - constructor(value, spans, params, label, aggregate) { + constructor(value, spans, params) { super(EXPRESSION); this.value = value; this.spans = spans; this.params = params; - this.label = label; - this.aggregate = aggregate; } instantiate(ctx) { - const { spans, params, label, aggregate } = this; - const tag = ctx.api[aggregate ? AGG : SQL]; + const { spans, params } = this; + const tag = ctx.api[SQL]; const args = params.map(e => e.instantiate(ctx)); - return tag(spans, ...args).annotate({ label }); + return tag(spans, ...args); } codegen(ctx) { - const { spans, params, label, aggregate } = this; - const method = aggregate ? AGG : SQL; + const { spans, params } = this; // reconstitute expression string let str = ''; @@ -54,12 +48,10 @@ export class ExpressionNode extends ASTNode { } str += spans[n]; - return `${ctx.ns()}${method}\`${str}\`` - + (label ? `.annotate({ label: ${JSON.stringify(label)} })` : ''); + return `${ctx.ns()}${SQL}\`${str}\``; } toJSON() { - const key = this.aggregate ? AGG : SQL; - return { [key]: this.value }; + return { [SQL]: this.value }; } } diff --git a/packages/spec/src/ast/PlotMarkNode.js b/packages/spec/src/ast/PlotMarkNode.js index eff322ec..09060ab4 100644 --- a/packages/spec/src/ast/PlotMarkNode.js +++ b/packages/spec/src/ast/PlotMarkNode.js @@ -1,4 +1,4 @@ -import { AGG, MARK, SQL } from '../constants.js'; +import { MARK, SQL } from '../constants.js'; import { isObject } from '../util.js'; import { ASTNode } from './ASTNode.js'; import { parseExpression } from './ExpressionNode.js'; @@ -8,7 +8,7 @@ import { parseTransform } from './TransformNode.js'; function maybeTransform(value, ctx) { if (isObject(value)) { - return (value[SQL] || value[AGG]) + return value[SQL] ? parseExpression(value, ctx) : parseTransform(value, ctx); } diff --git a/packages/spec/src/constants.js b/packages/spec/src/constants.js index 72bdde7d..64da329d 100644 --- a/packages/spec/src/constants.js +++ b/packages/spec/src/constants.js @@ -30,7 +30,6 @@ export const DATA = 'data'; // sql expressions export const EXPRESSION = 'expression'; export const SQL = 'sql'; -export const AGG = 'agg'; // inputs export const INPUT = 'input'; diff --git a/packages/sql/jsconfig.json b/packages/sql/jsconfig.json new file mode 100644 index 00000000..270e9f5a --- /dev/null +++ b/packages/sql/jsconfig.json @@ -0,0 +1,11 @@ +{ + "include": ["src/**/*"], + "compilerOptions": { + "checkJs": true, + "noEmit": true, + "noImplicitAny": false, + "module": "node16", + "skipLibCheck": true, + "types": [] + } +} \ No newline at end of file diff --git a/packages/sql/package.json b/packages/sql/package.json index bba8a43b..83ac8336 100644 --- a/packages/sql/package.json +++ b/packages/sql/package.json @@ -14,7 +14,7 @@ "module": "src/index.js", "jsdelivr": "dist/mosaic-sql.min.js", "unpkg": "dist/mosaic-sql.min.js", - "types": "dist/types/index.d.ts", + "types": "dist/types/index-types.d.ts", "repository": { "type": "git", "url": "https://github.com/uwdata/mosaic.git" @@ -24,7 +24,7 @@ "build": "npm run types && node ../../esbuild.js mosaic-sql", "types": "tsc", "lint": "eslint src test", - "test": "vitest run", + "test": "vitest run && tsc -p jsconfig.json", "prepublishOnly": "npm run test && npm run lint && npm run build" } } \ No newline at end of file diff --git a/packages/sql/src/Query.js b/packages/sql/src/Query.js deleted file mode 100644 index fa3abd7e..00000000 --- a/packages/sql/src/Query.js +++ /dev/null @@ -1,593 +0,0 @@ -import { isSQLExpression } from './expression.js'; -import { asColumn, asRelation, isColumnRefFor, Ref } from './ref.js'; - -export class Query { - - static select(...expr) { - return new Query().select(...expr); - } - - static from(...expr) { - return new Query().from(...expr); - } - - static with(...expr) { - return new Query().with(...expr); - } - - static union(...queries) { - return new SetOperation('UNION', queries.flat()); - } - - static unionAll(...queries) { - return new SetOperation('UNION ALL', queries.flat()); - } - - static intersect(...queries) { - return new SetOperation('INTERSECT', queries.flat()); - } - - static except(...queries) { - return new SetOperation('EXCEPT', queries.flat()); - } - - static describe(query) { - const q = query.clone(); - const { clone, toString } = q; - return Object.assign(q, { - describe: true, - clone: () => Query.describe(clone.call(q)), - toString: () => `DESCRIBE ${toString.call(q)}` - }); - } - - constructor() { - this.query = { - with: [], - select: [], - from: [], - where: [], - groupby: [], - having: [], - window: [], - qualify: [], - orderby: [] - }; - this.cteFor = null; - } - - clone() { - const q = new Query(); - q.query = { ...this.query }; - return q; - } - - /** - * Retrieve current WITH common table expressions (CTEs). - * @returns {any[]} - *//** - * Add WITH common table expressions (CTEs). - * @param {...any} expr Expressions to add. - * @returns {this} - */ - with(...expr) { - const { query } = this; - if (expr.length === 0) { - // @ts-ignore - return query.with; - } else { - const list = []; - const add = (as, q) => { - const query = q.clone(); - query.cteFor = this; - list.push({ as, query }); - }; - expr.flat().forEach(e => { - if (e == null) { - // do nothing - } else if (e.as && e.query) { - add(e.as, e.query); - } else { - for (const as in e) { - add(as, e[as]); - } - } - }); - query.with = query.with.concat(list); - return this; - } - } - - /** - * Retrieve current SELECT expressions. - * @returns {any[]} - *//** - * Add SELECT expressions. - * @param {...any} expr Expressions to add. - * @returns {this} - */ - select(...expr) { - const { query } = this; - if (expr.length === 0) { - // @ts-ignore - return query.select; - } else { - const list = []; - for (const e of expr.flat()) { - if (e == null) { - // do nothing - } else if (typeof e === 'string') { - list.push({ as: e, expr: asColumn(e) }); - } else if (e instanceof Ref) { - list.push({ as: e.column, expr: e }); - } else if (Array.isArray(e)) { - list.push({ as: e[0], expr: e[1] }); - } else { - for (const as in e) { - list.push({ as: unquote(as), expr: asColumn(e[as]) }); - } - } - } - - const keys = new Set(list.map(x => x.as)); - query.select = query.select - .filter(x => !keys.has(x.as)) - .concat(list.filter(x => x.expr)); - return this; - } - } - - $select(...expr) { - this.query.select = []; - return this.select(...expr); - } - - distinct(value = true) { - this.query.distinct = !!value; - return this; - } - - /** - * Retrieve current from expressions. - * @returns {any[]} - *//** - * Provide table from expressions. - * @param {...any} expr - * @returns {this} - */ - from(...expr) { - const { query } = this; - if (expr.length === 0) { - // @ts-ignore - return query.from; - } else { - const list = []; - expr.flat().forEach(e => { - if (e == null) { - // do nothing - } else if (typeof e === 'string') { - list.push({ as: e, from: asRelation(e) }); - } else if (e instanceof Ref) { - list.push({ as: e.table, from: e }); - } else if (isQuery(e) || isSQLExpression(e)) { - list.push({ from: e }); - } else if (Array.isArray(e)) { - list.push({ as: unquote(e[0]), from: asRelation(e[1]) }); - } else { - for (const as in e) { - list.push({ as: unquote(as), from: asRelation(e[as]) }); - } - } - }); - query.from = query.from.concat(list); - return this; - } - } - - $from(...expr) { - this.query.from = []; - return this.from(...expr); - } - - /** - * Retrieve current SAMPLE settings. - * @returns {any[]} - *//** - * Set SAMPLE settings. - * @param {number|object} value The percentage or number of rows to sample. - * @param {string} [method] The sampling method to use. - * @returns {this} - */ - sample(value, method) { - const { query } = this; - if (arguments.length === 0) { - // @ts-ignore - return query.sample; - } else { - let spec = value; - if (typeof value === 'number') { - spec = value > 0 && value < 1 - ? { perc: 100 * value, method } - : { rows: Math.round(value), method }; - } - query.sample = spec; - return this; - } - } - - /** - * Retrieve current WHERE expressions. - * @returns {any[]} - *//** - * Add WHERE expressions. - * @param {...any} expr Expressions to add. - * @returns {this} - */ - where(...expr) { - const { query } = this; - if (expr.length === 0) { - // @ts-ignore - return query.where; - } else { - query.where = query.where.concat( - expr.flat().filter(x => x) - ); - return this; - } - } - - $where(...expr) { - this.query.where = []; - return this.where(...expr); - } - - /** - * Retrieve current GROUP BY expressions. - * @returns {any[]} - *//** - * Add GROUP BY expressions. - * @param {...any} expr Expressions to add. - * @returns {this} - */ - groupby(...expr) { - const { query } = this; - if (expr.length === 0) { - // @ts-ignore - return query.groupby; - } else { - query.groupby = query.groupby.concat( - expr.flat().filter(x => x).map(asColumn) - ); - return this; - } - } - - $groupby(...expr) { - this.query.groupby = []; - return this.groupby(...expr); - } - - /** - * Retrieve current HAVING expressions. - * @returns {any[]} - *//** - * Add HAVING expressions. - * @param {...any} expr Expressions to add. - * @returns {this} - */ - having(...expr) { - const { query } = this; - if (expr.length === 0) { - // @ts-ignore - return query.having; - } else { - query.having = query.having.concat( - expr.flat().filter(x => x) - ); - return this; - } - } - - /** - * Retrieve current WINDOW definitions. - * @returns {any[]} - *//** - * Add WINDOW definitions. - * @param {...any} expr Expressions to add. - * @returns {this} - */ - window(...expr) { - const { query } = this; - if (expr.length === 0) { - // @ts-ignore - return query.window; - } else { - const list = []; - expr.flat().forEach(e => { - if (e == null) { - // do nothing - } else { - for (const as in e) { - list.push({ as: unquote(as), expr: e[as] }); - } - } - }); - query.window = query.window.concat(list); - return this; - } - } - - /** - * Retrieve current QUALIFY expressions. - * @returns {any[]} - *//** - * Add QUALIFY expressions. - * @param {...any} expr Expressions to add. - * @returns {this} - */ - qualify(...expr) { - const { query } = this; - if (expr.length === 0) { - // @ts-ignore - return query.qualify; - } else { - query.qualify = query.qualify.concat( - expr.flat().filter(x => x) - ); - return this; - } - } - - /** - * Retrieve current ORDER BY expressions. - * @returns {any[]} - *//** - * Add ORDER BY expressions. - * @param {...any} expr Expressions to add. - * @returns {this} - */ - orderby(...expr) { - const { query } = this; - if (expr.length === 0) { - // @ts-ignore - return query.orderby; - } else { - query.orderby = query.orderby.concat( - expr.flat().filter(x => x).map(asColumn) - ); - return this; - } - } - - /** - * Retrieve current LIMIT value. - * @returns {number|null} - *//** - * Set the query result LIMIT. - * @param {number} value The limit value. - * @returns {this} - */ - limit(value) { - const { query } = this; - if (arguments.length === 0) { - return query.limit; - } else { - query.limit = Number.isFinite(value) ? value : undefined; - return this; - } - } - - /** - * Retrieve current OFFSET value. - * @returns {number|null} - *//** - * Set the query result OFFSET. - * @param {number} value The offset value. - * @returns {this} - */ - offset(value) { - const { query } = this; - if (arguments.length === 0) { - return query.offset; - } else { - query.offset = Number.isFinite(value) ? value : undefined; - return this; - } - } - - get subqueries() { - const { query, cteFor } = this; - const ctes = (cteFor?.query || query).with; - const cte = ctes?.reduce((o, {as, query}) => (o[as] = query, o), {}); - const q = []; - query.from.forEach(({ from }) => { - if (isQuery(from)) { - q.push(from); - } else if (cte[from.table]) { - const sub = cte[from.table]; - q.push(sub); - } - }); - return q; - } - - toString() { - const { - with: cte, select, distinct, from, sample, where, groupby, - having, window, qualify, orderby, limit, offset - } = this.query; - - const sql = []; - - // WITH - if (cte.length) { - const list = cte.map(({ as, query })=> `"${as}" AS (${query})`); - sql.push(`WITH ${list.join(', ')}`); - } - - // SELECT - const sels = select.map( - ({ as, expr }) => isColumnRefFor(expr, as) && !expr.table - ? `${expr}` - : `${expr} AS "${as}"` - ); - sql.push(`SELECT${distinct ? ' DISTINCT' : ''} ${sels.join(', ')}`); - - // FROM - if (from.length) { - const rels = from.map(({ as, from }) => { - const rel = isQuery(from) ? `(${from})` : `${from}`; - return !as || as === from.table ? rel : `${rel} AS "${as}"`; - }); - sql.push(`FROM ${rels.join(', ')}`); - } - - // WHERE - if (where.length) { - const clauses = where.map(String).filter(x => x).join(' AND '); - if (clauses) sql.push(`WHERE ${clauses}`); - } - - // SAMPLE - if (sample) { - const { rows, perc, method, seed } = sample; - const size = rows ? `${rows} ROWS` : `${perc} PERCENT`; - const how = method ? ` (${method}${seed != null ? `, ${seed}` : ''})` : ''; - sql.push(`USING SAMPLE ${size}${how}`); - } - - // GROUP BY - if (groupby.length) { - sql.push(`GROUP BY ${groupby.join(', ')}`); - } - - // HAVING - if (having.length) { - const clauses = having.map(String).filter(x => x).join(' AND '); - if (clauses) sql.push(`HAVING ${clauses}`); - } - - // WINDOW - if (window.length) { - const windows = window.map(({ as, expr }) => `"${as}" AS (${expr})`); - sql.push(`WINDOW ${windows.join(', ')}`); - } - - // QUALIFY - if (qualify.length) { - const clauses = qualify.map(String).filter(x => x).join(' AND '); - if (clauses) sql.push(`QUALIFY ${clauses}`); - } - - // ORDER BY - if (orderby.length) { - sql.push(`ORDER BY ${orderby.join(', ')}`); - } - - // LIMIT - if (Number.isFinite(limit)) { - sql.push(`LIMIT ${limit}`); - } - - // OFFSET - if (Number.isFinite(offset)) { - sql.push(`OFFSET ${offset}`); - } - - return sql.join(' '); - } -} - -export class SetOperation { - constructor(op, queries) { - this.op = op; - this.queries = queries.map(q => q.clone()); - this.query = { orderby: [] }; - this.cteFor = null; - } - - clone() { - const q = new SetOperation(this.op, this.queries); - q.query = { ...this.query }; - return q; - } - - orderby(...expr) { - const { query } = this; - if (expr.length === 0) { - return query.orderby; - } else { - query.orderby = query.orderby.concat( - expr.flat().filter(x => x).map(asColumn) - ); - return this; - } - } - - limit(value) { - const { query } = this; - if (arguments.length === 0) { - return query.limit; - } else { - query.limit = Number.isFinite(value) ? value : undefined; - return this; - } - } - - offset(value) { - const { query } = this; - if (arguments.length === 0) { - return query.offset; - } else { - query.offset = Number.isFinite(value) ? value : undefined; - return this; - } - } - - get subqueries() { - const { queries, cteFor } = this; - if (cteFor) queries.forEach(q => q.cteFor = cteFor); - return queries; - } - - toString() { - const { op, queries, query: { orderby, limit, offset } } = this; - - // SUBQUERIES - const sql = [ queries.join(` ${op} `) ]; - - // ORDER BY - if (orderby.length) { - sql.push(`ORDER BY ${orderby.join(', ')}`); - } - - // LIMIT - if (Number.isFinite(limit)) { - sql.push(`LIMIT ${limit}`); - } - - // OFFSET - if (Number.isFinite(offset)) { - sql.push(`OFFSET ${offset}`); - } - - return sql.join(' '); - } -} - -export function isQuery(value) { - return value instanceof Query || value instanceof SetOperation; -} - -export function isDescribeQuery(value) { - // @ts-ignore - return isQuery(value) && value.describe; -} - -function unquote(s) { - return isDoubleQuoted(s) ? s.slice(1, -1) : s; -} - -function isDoubleQuoted(s) { - return s[0] === '"' && s[s.length-1] === '"'; -} diff --git a/packages/sql/src/aggregates.js b/packages/sql/src/aggregates.js deleted file mode 100644 index 503899ef..00000000 --- a/packages/sql/src/aggregates.js +++ /dev/null @@ -1,185 +0,0 @@ -import { SQLExpression, parseSQL, sql } from './expression.js'; -import { asColumn } from './ref.js'; -import { repeat } from './repeat.js'; -import { literalToSQL } from './to-sql.js'; -import { WindowFunction } from './windows.js'; - -/** - * Tag function for SQL aggregate expressions. Interpolated values - * may be strings, other SQL expression objects (such as column - * references), or parameterized values. - */ -export function agg(strings, ...exprs) { - return sql(strings, ...exprs).annotate({ aggregate: true }); -} - -/** - * Base class for individual aggregate functions. - * Most callers should use a dedicated aggregate function - * rather than instantiate this class. - */ -export class AggregateFunction extends SQLExpression { - /** - * Create a new AggregateFunction instance. - * @param {*} op The aggregate operation. - * @param {*} [args] The aggregate function arguments. - * @param {*} [type] The SQL data type to cast to. - * @param {boolean} [isDistinct] Flag indicating if this is a distinct value aggregate. - * @param {*} [filter] Filtering expression to apply prior to aggregation. - */ - constructor(op, args, type, isDistinct, filter) { - args = (args || []).map(asColumn); - const { strings, exprs } = aggExpr(op, args, type, isDistinct, filter); - const { spans, cols } = parseSQL(strings, exprs); - super(spans, cols); - this.aggregate = op; - this.args = args; - this.type = type; - this.isDistinct = isDistinct; - this.filter = filter; - } - - get basis() { - return this.column; - } - - get label() { - const { aggregate: op, args, isDistinct } = this; - const dist = isDistinct ? 'DISTINCT' + (args.length ? ' ' : '') : ''; - const tail = args.length ? `(${dist}${args.map(unquoted).join(', ')})` : ''; - return `${op.toLowerCase()}${tail}`; - } - - /** - * Return a new derived aggregate function over distinct values. - * @returns {AggregateFunction} A new aggregate function. - */ - distinct() { - const { aggregate: op, args, type, filter } = this; - return new AggregateFunction(op, args, type, true, filter); - } - - /** - * Return a new derived aggregate function that filters values. - * @param {*} filter The filter expresion. - * @returns {AggregateFunction} A new aggregate function. - */ - where(filter) { - const { aggregate: op, args, type, isDistinct } = this; - return new AggregateFunction(op, args, type, isDistinct, filter); - } - - /** - * Return a new window function over this aggregate. - * @returns {WindowFunction} A new aggregate function. - */ - window() { - const { aggregate: op, args, type, isDistinct } = this; - const func = new AggregateFunction(op, args, null, isDistinct); - return new WindowFunction(op, func, type); - } - - /** - * Return a window function over this aggregate with the given partitioning. - * @param {*} expr The grouping (partition by) criteria for the window function. - * @returns {WindowFunction} A new window function. - */ - partitionby(...expr) { - return this.window().partitionby(...expr); - } - - /** - * Return a window function over this aggregate with the given ordering. - * @param {*} expr The sorting (order by) criteria for the window function. - * @returns {WindowFunction} A new window function. - */ - orderby(...expr) { - return this.window().orderby(...expr); - } - - /** - * Return a window function over this aggregate with the given row frame. - * @param {(number|null)[] | import('./expression.js').ParamLike} frame The row-based window frame. - * @returns {WindowFunction} A new window function. - */ - rows(frame) { - return this.window().rows(frame); - } - - /** - * Return a window function over this aggregate with the given range frame. - * @param {(number|null)[] | import('./expression.js').ParamLike} frame The range-based window frame. - * @returns {WindowFunction} A new window function. - */ - range(frame) { - return this.window().range(frame); - } -} - -function aggExpr(op, args, type, isDistinct, filter) { - const close = `)${type ? `::${type}` : ''}`; - let strings = [`${op}(${isDistinct ? 'DISTINCT ' :''}`]; - let exprs = []; - if (args.length) { - strings = strings.concat([ - ...repeat(args.length - 1, ', '), - `${close}${filter ? ' FILTER (WHERE ' : ''}`, - ...(filter ? [')'] : []) - ]); - exprs = [...args, ...(filter ? [filter] : [])]; - } else { - strings[0] += '*' + close; - } - return { exprs, strings }; -} - -function unquoted(value) { - const s = literalToSQL(value); - return s && s.startsWith('"') && s.endsWith('"') ? s.slice(1, -1) : s -} - -function aggf(op, type) { - return (...args) => new AggregateFunction(op, args, type); -} - -export const count = aggf('COUNT', 'INTEGER'); -export const avg = aggf('AVG'); -export const mean = aggf('AVG'); -export const mad = aggf('MAD'); -export const max = aggf('MAX'); -export const min = aggf('MIN'); -export const sum = aggf('SUM', 'DOUBLE'); -export const product = aggf('PRODUCT'); -export const median = aggf('MEDIAN'); -export const quantile = aggf('QUANTILE'); -export const mode = aggf('MODE'); - -export const variance = aggf('VARIANCE'); -export const stddev = aggf('STDDEV'); -export const skewness = aggf('SKEWNESS'); -export const kurtosis = aggf('KURTOSIS'); -export const entropy = aggf('ENTROPY'); -export const varPop = aggf('VAR_POP'); -export const stddevPop = aggf('STDDEV_POP'); - -export const corr = aggf('CORR'); -export const covariance = aggf('COVAR_SAMP'); -export const covarPop = aggf('COVAR_POP'); -export const regrIntercept = aggf('REGR_INTERCEPT'); -export const regrSlope = aggf('REGR_SLOPE'); -export const regrCount = aggf('REGR_COUNT'); -export const regrR2 = aggf('REGR_R2'); -export const regrSYY = aggf('REGR_SYY'); -export const regrSXX = aggf('REGR_SXX'); -export const regrSXY = aggf('REGR_SXY'); -export const regrAvgX = aggf('REGR_AVGX'); -export const regrAvgY = aggf('REGR_AVGY'); - -export const first = aggf('FIRST'); -export const last = aggf('LAST'); - -export const argmin = aggf('ARG_MIN'); -export const argmax = aggf('ARG_MAX'); - -export const stringAgg = aggf('STRING_AGG'); -export const arrayAgg = aggf('ARRAY_AGG'); diff --git a/packages/sql/src/ast/aggregate.js b/packages/sql/src/ast/aggregate.js new file mode 100644 index 00000000..86ccd1b2 --- /dev/null +++ b/packages/sql/src/ast/aggregate.js @@ -0,0 +1,164 @@ +import { AGGREGATE } from '../constants.js'; +import { asVerbatim } from '../util/ast.js'; +import { isString } from '../util/type-check.js'; +import { ExprNode } from './node.js'; +import { WindowNode } from './window.js'; + +export class AggregateNode extends ExprNode { + /** + * Instantiate an aggregate function node. + * @param {string} name The aggregate function name. + * @param {ExprNode[]} args The aggregate function arguments. + * @param {boolean} [distinct] The distinct flag. + * @param {ExprNode} [filter] Filter expression. + */ + constructor(name, args, distinct, filter) { + super(AGGREGATE); + /** + * The aggregate function name. + * @type {string} + * @readonly + */ + this.name = name; + /** + * The aggregate function arguments. + * @type {ExprNode[]} + * @readonly + */ + this.args = args; + /** + * The distinct flag. + * @type {boolean} + * @readonly + */ + this.isDistinct = distinct; + /** + * Filter criteria. + * @type {ExprNode} + * @readonly + */ + this.filter = filter; + } + + /** + * Return a new derived aggregate over distinct values. + * @param {boolean} [isDistinct=true] The distinct flag. + * @returns {AggregateNode} A new aggregate node. + */ + distinct(isDistinct = true) { + return new AggregateNode(this.name, this.args, isDistinct, this.filter); + } + + /** + * Return a new derived aggregate function that filters values. + * @param {ExprNode | string} filter The filter expression. + * @returns {AggregateNode} A new aggregate node. + */ + where(filter) { + if (isString(filter)) filter = asVerbatim(filter); + return new AggregateNode(this.name, this.args, this.isDistinct, filter); + } + + /** + * Return a new window function over this aggregate. + * @returns {WindowNode} A new window node. + */ + window() { + return new WindowNode(this); + } + + /** + * Return a new window function over this aggregate with the given partitions. + * @param {...import('../types.js').ExprVarArgs} expr The partition by criteria. + * @returns {WindowNode} A new window node. + */ + partitionby(...expr) { + return this.window().partitionby(...expr); + } + + /** + * Return a new window function over this aggregate with the given ordering. + * @param {...import('../types.js').ExprVarArgs} expr The order by criteria. + * @returns {WindowNode} A new window node. + */ + orderby(...expr) { + return this.window().orderby(...expr); + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + const { name, args, isDistinct, filter } = this; + const dist = isDistinct ? 'DISTINCT ' : ''; + const arg = args?.length ? args.join(', ') : '*'; + const filt = filter ? ` FILTER (WHERE ${filter})` : ''; + return `${name}(${dist}${arg})${filt}`; + } +} + +/** + * An array of known aggregate function names. + * From https://duckdb.org/docs/sql/functions/aggregates.html. + */ +export const aggregateNames = [ + 'any_value', + 'approx_count_distinct', + 'approx_quantile', + 'arbitrary', + 'arg_max', + 'arg_max_null', + 'arg_min', + 'arg_min_null', + 'array_agg', + 'avg', + 'bit_and', + 'bit_or', + 'bit_xor', + 'bitstring_agg', + 'bool_and', + 'bool_or', + 'corr', + 'count', + 'covar_pop', + 'covar_samp', + 'entropy', + 'favg', + 'first', + 'fsum', + 'geomean', + 'kurtosis_pop', + 'kurtosis', + 'last', + 'mad', + 'max', + 'max_by', + 'median', + 'min', + 'min_by', + 'mode', + 'product', + 'quantile', + 'quantile_cont', + 'quantile_disc', + 'regr_avgx', + 'regr_avgy', + 'regr_count', + 'regr_intercept', + 'regr_r2', + 'regr_sxx', + 'regr_sxy', + 'regr_syy', + 'regr_slope', + 'reservoir_quantile', + 'skewness', + 'stddev', + 'stddev_pop', + 'stddev_samp', + 'string_agg', + 'sum', + 'variance', + 'var_pop', + 'var_samp' +]; diff --git a/packages/sql/src/ast/between-op.js b/packages/sql/src/ast/between-op.js new file mode 100644 index 00000000..4167629c --- /dev/null +++ b/packages/sql/src/ast/between-op.js @@ -0,0 +1,75 @@ +import { BETWEEN_OPERATOR, NOT_BETWEEN_OPERATOR } from '../constants.js'; +import { ExprNode } from './node.js'; + +class AbstractBetweenOpNode extends ExprNode { + /** + * Instantiate an abstract between operator node. + * @param {string} type The node type. + * @param {ExprNode} expr The input expression. + * @param {[ExprNode, ExprNode]} extent The range extent. + */ + constructor(type, expr, extent) { + super(type); + /** + * The input expression. + * @type {ExprNode} + * @readonly + */ + this.expr = expr; + /** + * The range extent. + * @type {[ExprNode, ExprNode]} + * @readonly + */ + this.extent = extent; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toSQL(op) { + const { extent: r, expr } = this; + return r ? `(${expr} ${op} ${r[0]} AND ${r[1]})` : ''; + } +} + +export class BetweenOpNode extends AbstractBetweenOpNode { + /** + * Instantiate a between operator node. + * @param {ExprNode} expr The input expression. + * @param {[ExprNode, ExprNode]} extent + * The range extent. + */ + constructor(expr, extent) { + super(BETWEEN_OPERATOR, expr, extent); + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + return super.toSQL('BETWEEN'); + } +} + +export class NotBetweenOpNode extends AbstractBetweenOpNode { + /** + * Instantiate a not between operator node. + * @param {ExprNode} expr The input expression. + * @param {[ExprNode, ExprNode]} extent + * The range extent. + */ + constructor(expr, extent) { + super(NOT_BETWEEN_OPERATOR, expr, extent); + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + return super.toSQL('NOT BETWEEN'); + } +} diff --git a/packages/sql/src/ast/binary-op.js b/packages/sql/src/ast/binary-op.js new file mode 100644 index 00000000..00f9d660 --- /dev/null +++ b/packages/sql/src/ast/binary-op.js @@ -0,0 +1,40 @@ +import { BINARY_OPERATOR } from '../constants.js'; +import { ExprNode } from './node.js'; + +export class BinaryOpNode extends ExprNode { + /** + * Instantiate a binary operator node. + * @param {string} op The operator type. + * @param {ExprNode} left The left input expression. + * @param {ExprNode} right The right input expression. + */ + constructor(op, left, right) { + super(BINARY_OPERATOR); + /** + * The operator type. + * @type {string} + * @readonly + */ + this.op = op; + /** + * The left input expression. + * @type {ExprNode} + * @readonly + */ + this.left = left; + /** + * The right input expression. + * @type {ExprNode} + * @readonly + */ + this.right = right; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + return `(${this.left} ${this.op} ${this.right})`; + } +} diff --git a/packages/sql/src/ast/case.js b/packages/sql/src/ast/case.js new file mode 100644 index 00000000..b0414424 --- /dev/null +++ b/packages/sql/src/ast/case.js @@ -0,0 +1,105 @@ +import { CASE, WHEN } from '../constants.js'; +import { asNode } from '../util/ast.js'; +import { ExprNode, SQLNode } from './node.js'; + +export class CaseNode extends ExprNode { + /** + * Instantiate a case node. + * @param {ExprNode} [expr] An optional base expression, that comes + * immediately after the CASE keyword. If specified, this case statement + * acts like a switch statement, with WHEN expressions as values to + * match against the switch value rather than boolean conditions. + * @param {WhenNode[]} [when] An array of WHEN/THEN expression nodes. + * @param {ExprNode} [elseExpr] An ELSE expression. + */ + constructor(expr = undefined, when = [], elseExpr = undefined) { + super(CASE); + /** + * The optional base expression. + * @type {ExprNode} + * @readonly + */ + this.expr = expr; + /** + * An array of WHEN/THEN expression nodes. + * @type {WhenNode[]} + * @readonly + */ + this._when = when; + /** + * An ELSE expression. + * @type {ExprNode} + * @readonly + */ + this._else = elseExpr; + } + + /** + * Return a new case node with the given conditional added as + * the last WHEN / THEN pair. + * @param {import('../types.js').ExprValue} cond + * The WHEN condition expression. + * @param {import('../types.js').ExprValue} value + * The THEN value expression. + * @returns {CaseNode} + */ + when(cond, value) { + return new CaseNode( + this.expr, + this._when.concat(new WhenNode(asNode(cond), asNode(value))), + this._else + ); + } + + /** + * Return a new case node with the given ELSE expression. + * @param {import('../types.js').ExprValue} expr The ELSE expression. + * @returns {CaseNode} + */ + else(expr) { + return new CaseNode(this.expr, this._when, asNode(expr)); + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + return 'CASE ' + + (this.expr ? `${this.expr} ` : '') + + this._when.join(' ') + + (this._else ? ` ELSE ${this._else}` : '') + + ' END'; + } +} + +export class WhenNode extends SQLNode { + /** + * Instantiate a case node. + * @param {ExprNode} when The WHEN condition expression. + * @param {ExprNode} then The THEN value expression. + */ + constructor(when, then) { + super(WHEN); + /** + * The condition expression. + * @type {ExprNode} + * @readonly + */ + this.when = when; + /** + * The value expression. + * @type {ExprNode} + * @readonly + */ + this.then = then; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + return `WHEN ${this.when} THEN ${this.then}`; + } +} diff --git a/packages/sql/src/ast/cast.js b/packages/sql/src/ast/cast.js new file mode 100644 index 00000000..3cbb2f4c --- /dev/null +++ b/packages/sql/src/ast/cast.js @@ -0,0 +1,34 @@ +import { CAST } from '../constants.js'; +import { ExprNode } from './node.js'; + +export class CastNode extends ExprNode { + /** + * Instantiate a cast node. + * @param {ExprNode} expr The expression to type cast. + * @param {string} type The type to cast to. + */ + constructor(expr, type) { + super(CAST); + /** + * The expression to type cast. + * @type {ExprNode} + * @readonly + */ + this.expr = expr; + /** + * The type to cast to. + * @type {string} + * @readonly + */ + this.cast = type; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + // TODO? could include check to see if parens are necessary + return `(${this.expr})::${this.cast}`; + } +} diff --git a/packages/sql/src/ast/column-ref.js b/packages/sql/src/ast/column-ref.js new file mode 100644 index 00000000..27e043b8 --- /dev/null +++ b/packages/sql/src/ast/column-ref.js @@ -0,0 +1,46 @@ +import { COLUMN_REF } from '../constants.js'; +import { quoteIdentifier } from '../util/string.js'; +import { ExprNode } from './node.js'; + +/** + * Check if a value is a column reference node. + * @param {*} value The value to check. + * @returns {value is ColumnRefNode} + */ +export function isColumnRef(value) { + return value instanceof ColumnRefNode; +} + +export class ColumnRefNode extends ExprNode { + /** + * Instantiate a column reference node. + * @param {string} column The column name. + * @param {import('./table-ref.js').TableRefNode} [table] The table reference. + */ + constructor(column, table) { + super(COLUMN_REF); + /** + * The column name. + * @type {string} + * @readonly + */ + this.column = column; + /** + * The table reference. + * @type {import('./table-ref.js').TableRefNode} + * @readonly + */ + this.table = table; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + const { column, table } = this; + const tref = `${table ?? ''}`; + const id = column === '*' ? '*' : quoteIdentifier(column); + return (tref ? (tref + '.') : '') + id; + } +} diff --git a/packages/sql/src/ast/fragment.js b/packages/sql/src/ast/fragment.js new file mode 100644 index 00000000..d18cef3d --- /dev/null +++ b/packages/sql/src/ast/fragment.js @@ -0,0 +1,26 @@ +import { FRAGMENT } from '../constants.js'; +import { ExprNode } from './node.js'; + +export class FragmentNode extends ExprNode { + /** + * Instantiate a fragment node with arbitrary content. + * @param {ExprNode[]} spans The consecutive parts making up the fragment. + */ + constructor(spans) { + super(FRAGMENT); + /** + * The consecutive parts making up the fragment. + * @type {ExprNode[]} + * @readonly + */ + this.spans = spans; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + return this.spans.join(''); + } +} diff --git a/packages/sql/src/ast/from.js b/packages/sql/src/ast/from.js new file mode 100644 index 00000000..2e85fda7 --- /dev/null +++ b/packages/sql/src/ast/from.js @@ -0,0 +1,40 @@ +import { FROM_CLAUSE } from '../constants.js'; +import { quoteIdentifier } from '../util/string.js'; +import { SQLNode } from './node.js'; +import { isQuery } from './query.js'; +import { isTableRef } from './table-ref.js'; + +export class FromClauseNode extends SQLNode { + /** + * Instantiate a from node. + * @param {SQLNode} expr The from expression. + * @param {string} alias The output name. + */ + constructor(expr, alias) { + super(FROM_CLAUSE); + /** + * The from expression. + * @type {SQLNode} + * @readonly + */ + this.expr = expr; + /** + * The output name. + * @type {string} + * @readonly + */ + this.alias = alias; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + const { expr, alias } = this; + const ref = isQuery(expr) ? `(${expr})` : `${expr}`; + return alias && !(isTableRef(expr) && expr.table.join('.') === alias) + ? `${ref} AS ${quoteIdentifier(alias)}` + : `${ref}`; + } +} diff --git a/packages/sql/src/ast/function.js b/packages/sql/src/ast/function.js new file mode 100644 index 00000000..04adc772 --- /dev/null +++ b/packages/sql/src/ast/function.js @@ -0,0 +1,34 @@ +import { FUNCTION } from '../constants.js'; +import { ExprNode } from './node.js'; + +export class FunctionNode extends ExprNode { + /** + * Instantiate a function node. + * @param {string} name The function name. + * @param {ExprNode[]} [args=[]] The function arguments. + */ + constructor(name, args = []) { + super(FUNCTION); + /** + * The function name. + * @type {string} + * @readonly + */ + this.name = name; + /** + * The function arguments. + * @type {ExprNode[]} + * @readonly + */ + this.args = args; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + const { name, args } = this; + return `${name}(${args.join(', ')})`; + } +} diff --git a/packages/sql/src/ast/in-op.js b/packages/sql/src/ast/in-op.js new file mode 100644 index 00000000..4484372c --- /dev/null +++ b/packages/sql/src/ast/in-op.js @@ -0,0 +1,33 @@ +import { IN_OPERATOR } from '../constants.js'; +import { ExprNode } from './node.js'; + +export class InOpNode extends ExprNode { + /** + * Instantiate an in operator node. + * @param {ExprNode} expr The input expression. + * @param {ExprNode[]} values The value set. + */ + constructor(expr, values) { + super(IN_OPERATOR); + /** + * The input expression. + * @type {ExprNode} + * @readonly + */ + this.expr = expr; + /** + * The value set. + * @type {ExprNode[]} + * @readonly + */ + this.values = values; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + return `(${this.expr} IN (${this.values.join(', ')}))`; + } +} diff --git a/packages/sql/src/ast/interval.js b/packages/sql/src/ast/interval.js new file mode 100644 index 00000000..44ce960b --- /dev/null +++ b/packages/sql/src/ast/interval.js @@ -0,0 +1,33 @@ +import { INTERVAL } from '../constants.js'; +import { ExprNode } from './node.js'; + +export class IntervalNode extends ExprNode { + /** + * Instantiate an interval node. + * @param {string} name The interval name. + * @param {number} [steps=1] The interval steps. + */ + constructor(name, steps = 1) { + super(INTERVAL); + /** + * The interval name. + * @type {string} + * @readonly + */ + this.name = name; + /** + * The interval steps. + * @type {number} + * @readonly + */ + this.steps = steps; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + return `INTERVAL ${this.steps} ${this.name}`; + } +} diff --git a/packages/sql/src/ast/literal.js b/packages/sql/src/ast/literal.js new file mode 100644 index 00000000..a0e904a6 --- /dev/null +++ b/packages/sql/src/ast/literal.js @@ -0,0 +1,55 @@ +import { LITERAL } from '../constants.js'; +import { ExprNode } from './node.js'; + +export class LiteralNode extends ExprNode { + /** + * Instantiate an literal node. + * @param {*} value The literal value. + */ + constructor(value) { + super(LITERAL); + /** + * The literal value. + * @type {any} + * @readonly + */ + this.value = value; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + return literalToSQL(this.value); + } +} + +export function literalToSQL(value) { + switch (typeof value) { + case 'number': + return Number.isFinite(value) ? `${value}` : 'NULL'; + case 'string': + return `'${value.replace(`'`, `''`)}'`; + case 'boolean': + return value ? 'TRUE' : 'FALSE'; + default: + if (value == null) { + return 'NULL'; + } else if (value instanceof Date) { + const ts = +value; + if (Number.isNaN(ts)) return 'NULL'; + const y = value.getUTCFullYear(); + const m = value.getUTCMonth(); + const d = value.getUTCDate(); + return ts === Date.UTC(y, m, d) + ? `DATE '${y}-${m+1}-${d}'` // utc date + : `epoch_ms(${ts})`; // timestamp + } else if (value instanceof RegExp) { + return `'${value.source}'`; + } else { + // otherwise rely on string coercion + return `${value}`; + } + } +} diff --git a/packages/sql/src/ast/logical-op.js b/packages/sql/src/ast/logical-op.js new file mode 100644 index 00000000..6329c8b7 --- /dev/null +++ b/packages/sql/src/ast/logical-op.js @@ -0,0 +1,67 @@ +import { LOGICAL_OPERATOR } from '../constants.js'; +import { ExprNode } from './node.js'; + +/** + * @template {ExprNode} T + */ +export class LogicalOpNode extends ExprNode { + /** + * Instantiate a logical operator node. + * @param {string} op The logical operation. + * @param {T[]} clauses The input clause expressions. + */ + constructor(op, clauses) { + super(LOGICAL_OPERATOR); + /** + * The logical operator. + * @type {string} + * @readonly + */ + this.op = op; + /** + * The input clause expressions. + * @type {T[]} + * @readonly + */ + this.clauses = clauses; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + const c = this.clauses; + return c.length === 0 ? '' + : c.length === 1 ? `${c[0]}` + : `(${c.join(` ${this.op} `)})`; + } +} + +/** + * @template {ExprNode} T + * @extends {LogicalOpNode} + */ +export class AndNode extends LogicalOpNode { + /** + * Instantiate a logical AND operator node. + * @param {T[]} clauses The input clause expressions. + */ + constructor(clauses) { + super('AND', clauses); + } +} + +/** + * @template {ExprNode} T + * @extends {LogicalOpNode} + */ +export class OrNode extends LogicalOpNode { + /** + * Instantiate a logical OR operator node. + * @param {T[]} clauses The input clause expressions. + */ + constructor(clauses) { + super('OR', clauses); + } +} diff --git a/packages/sql/src/ast/node.js b/packages/sql/src/ast/node.js new file mode 100644 index 00000000..9d55c5d3 --- /dev/null +++ b/packages/sql/src/ast/node.js @@ -0,0 +1,29 @@ +/** + * Check if a value is a SQL AST node. + * @param {*} value The value to check. + * @returns {value is SQLNode} + */ +export function isNode(value) { + return value instanceof SQLNode; +} + +export class SQLNode { + /** + * Instantiate a SQL AST node. + * @param {string} type The SQL AST node type. + */ + constructor(type) { + /** + * The SQL AST node type. + * @type {string} + * @readonly + */ + this.type = type; + } +} + +/** + * AST node corresponding to an individual expression. + */ +export class ExprNode extends SQLNode { +} diff --git a/packages/sql/src/ast/order-by.js b/packages/sql/src/ast/order-by.js new file mode 100644 index 00000000..0143622c --- /dev/null +++ b/packages/sql/src/ast/order-by.js @@ -0,0 +1,48 @@ +import { ORDER_BY } from '../constants.js'; +import { ExprNode } from './node.js'; + +export class OrderByNode extends ExprNode { + /** + * Instantiate an order by entry node. + * @param {ExprNode} expr The expression to order by. + * @param {boolean | undefined} [desc] Flag indicating descending order. + * @param {boolean | undefined} [nullsFirst] Flag indicating if null + * values should be sorted first. + */ + constructor(expr, desc, nullsFirst) { + super(ORDER_BY); + /** + * The expression to order by. + * @type {ExprNode} + * @readonly + */ + this.expr = expr; + /** + * Flag indicating descending order. + * @type {boolean | undefined} + * @readonly + */ + this.desc = desc; + /** + * Flag indicating if null values should be sorted first. + * @type {boolean | undefined} + * @readonly + */ + this.nullsFirst = nullsFirst; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + const { expr, desc, nullsFirst } = this; + const dir = desc ? ' DESC' + : desc === false ? ' ASC' + : ''; + const nf = nullsFirst ? ' NULLS FIRST' + : nullsFirst === false ? ' NULLS LAST' + : ''; + return `${expr}${dir}${nf}`; + } +} diff --git a/packages/sql/src/ast/param.js b/packages/sql/src/ast/param.js new file mode 100644 index 00000000..e7e58838 --- /dev/null +++ b/packages/sql/src/ast/param.js @@ -0,0 +1,35 @@ +import { PARAM } from '../constants.js'; +import { literalToSQL } from './literal.js'; +import { ExprNode } from './node.js'; + +export class ParamNode extends ExprNode { + /** + * Instantiate a param node with a dynamic parameter. + * @param {import('../types.js').ParamLike} param The dynamic parameter. + */ + constructor(param) { + super(PARAM); + /** + * The dynamic parameter. + * @type {import('../types.js').ParamLike} + * @readonly + */ + this.param = param; + } + + /** + * Returns the current parameter value. + * @returns {*} + */ + get value() { + return this.param.value; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + return literalToSQL(this.value); + } +} diff --git a/packages/sql/src/ast/query.js b/packages/sql/src/ast/query.js new file mode 100644 index 00000000..3fbab4ce --- /dev/null +++ b/packages/sql/src/ast/query.js @@ -0,0 +1,578 @@ +import { DESCRIBE_QUERY, SELECT_QUERY, SET_OPERATION } from '../constants.js'; +import { asNode, asTableRef, asVerbatim } from '../util/ast.js'; +import { exprList } from '../util/function.js'; +import { unquote } from '../util/string.js'; +import { isArray, isString } from '../util/type-check.js'; +import { isColumnRef } from './column-ref.js'; +import { FromClauseNode } from './from.js'; +import { ExprNode, SQLNode, isNode } from './node.js'; +import { SampleClauseNode } from './sample.js'; +import { SelectClauseNode } from './select.js'; +import { isTableRef } from './table-ref.js'; +import { WindowClauseNode } from './window.js'; +import { WithClauseNode } from './with.js'; + +/** + * Check if a value is a selection query or set operation. + * @param {*} value The value to check. + * @returns {value is Query} + */ +export function isQuery(value) { + return value instanceof Query; +} + +/** + * Check if a value is a selection query. + * @param {*} value The value to check. + * @returns {value is SelectQuery} + */ +export function isSelectQuery(value) { + return value instanceof SelectQuery; +} + +/** + * Check if a value is a describe query. + * @param {*} value The value to check. + * @returns {value is DescribeQuery} + */ +export function isDescribeQuery(value) { + return value instanceof DescribeQuery; +} + +export class Query extends ExprNode { + /** + * Create a new select query with the given SELECT expressions. + * @param {...import('../types.js').SelectExpr} expr The SELECT expressions. + * @returns {SelectQuery} + */ + static select(...expr) { + return new SelectQuery().select(...expr); + } + + /** + * Create a new select query with the given FROM expressions. + * @param {...import('../types.js').FromExpr} expr The FROM expressions. + * @returns {SelectQuery} + */ + static from(...expr) { + return new SelectQuery().from(...expr); + } + + /** + * Create a new select query with the given WITH CTE queries. + * @param {...import('../types.js').WithExpr} expr The WITH CTE queries. + * @returns {SelectQuery} + */ + static with(...expr) { + return new SelectQuery().with(...expr); + } + + /** + * Create a new UNION set operation over the given queries. + * @param {...Query} queries The queries. + * @returns {SetOperation} + */ + static union(...queries) { + return new SetOperation('UNION', queries.flat()); + } + + /** + * Create a new UNION ALL set operation over the given queries. + * @param {...Query} queries The queries. + * @returns {SetOperation} + */ + static unionAll(...queries) { + return new SetOperation('UNION ALL', queries.flat()); + } + + /** + * Create a new INTERSECT set operation over the given queries. + * @param {...Query} queries The queries. + * @returns {SetOperation} + */ + static intersect(...queries) { + return new SetOperation('INTERSECT', queries.flat()); + } + + /** + * Create a new EXCEPT set operation over the given queries. + * @param {...Query} queries The queries. + * @returns {SetOperation} + */ + static except(...queries) { + return new SetOperation('EXCEPT', queries.flat()); + } + + /** + * Create a new describe query for the given input query. + * @param {Query} query The query to describe. + * @returns {DescribeQuery} + */ + static describe(query) { + return new DescribeQuery(query); + } + + /** + * Instantiate a new query. + */ + constructor(type) { + super(type); + /** @type {ExprNode[]} */ + this._orderby = []; + /** @type {number} */ + this._limit = undefined; + /** @type {number} */ + this._offset = undefined; + /** @type {Query | null} */ + this.cteFor = null; + } + + /** + * Return a list of subqueries. + * @returns {Query[]} + */ + get subqueries() { + return []; + } + + /** + * Clone this query. + * @returns {Query} + */ + clone() { + return this; + } + + /** + * Add ORDER BY expressions. + * @param {...import('../types.js').OrderByExpr} expr Expressions to add. + * @returns + */ + orderby(...expr) { + this._orderby = this._orderby.concat(exprList(expr)); + return this; + } + + /** + * Set the query result LIMIT. + * @param {number} value The limit value. + * @returns {this} + */ + limit(value) { + this._limit = Number.isFinite(value) ? value : undefined; + return this; + } + + /** + * Set the query result OFFSET. + * @param {number} value The offset value. + * @returns {this} + */ + offset(value) { + this._offset = Number.isFinite(value) ? value : undefined; + return this; + } +} + +export class SelectQuery extends Query { + /** + * Instantiate a new select query. + */ + constructor() { + super(SELECT_QUERY); + /** @type {WithClauseNode[]} */ + this._with = []; + /** @type {SelectClauseNode[]} */ + this._select = []; + /** @type {FromClauseNode[]} */ + this._from = []; + /** @type {ExprNode[]} */ + this._where = []; + /** @type {SampleClauseNode} */ + this._sample = undefined; + /** @type {ExprNode[]} */ + this._groupby = []; + /** @type {ExprNode[]} */ + this._having = []; + /** @type {WindowClauseNode[]} */ + this._window = []; + /** @type {ExprNode[]} */ + this._qualify = []; + } + + /** + * Return a list of subqueries. + * @returns {Query[]} + */ + get subqueries() { + // build map of ctes within base query WITH clause + const q = this.cteFor || this; + const w = q instanceof SelectQuery ? q._with : []; + const cte = w.reduce((obj, c) => (obj[c.name] = c.query, obj), {}); + + // extract subqueries in FROM clause + // unused CTEs will be ignored + const queries = []; + this._from.forEach(({ expr }) => { + if (isQuery(expr)) { + queries.push(expr); + } else if (isTableRef(expr)) { + const subq = cte[expr.name]; + if (subq) queries.push(subq); + } + }); + return queries; + } + + /** + * Clone this query. + * @returns {SelectQuery} + */ + clone() { + return Object.assign(new SelectQuery(), this); + } + + /** + * Add WITH common table expressions (CTEs). + * @param {...import('../types.js').WithExpr} expr Expressions to add. + * @returns {this} + */ + with(...expr) { + /** @type {WithClauseNode[]} */ + const list = []; + const add = (name, q) => { + const query = q.clone(); + query.cteFor = this; + list.push(new WithClauseNode(name, query)); + }; + expr.flat().forEach(e => { + if (e != null) for (const name in e) add(name, e[name]); + }); + this._with = this._with.concat(list); + return this; + } + + /** + * Add SELECT expressions. + * @param {...import('../types.js').SelectExpr} expr Expressions to add. + * @returns {this} + */ + select(...expr) { + /** @type {SelectClauseNode[]} */ + const list = []; + const add = (v, as) => list.push( + new SelectClauseNode(v == null ? v : asNode(v), unquote(as)) + ); + expr.flat().forEach(e => { + if (e == null) return; + else if (isString(e)) add(e, e); + else if (isColumnRef(e)) add(e, e.column); + else if (isArray(e)) add(e[1], e[0]); + else for (const alias in e) add(e[alias], alias); + }); + + const keys = new Set(list.map(x => x.alias)); + this._select = this._select + .filter(x => !keys.has(x.alias)) + .concat(list.filter(x => x.expr)); + return this; + } + + /** + * Set SELECT expressions, replacing any prior expressions. + * @param {...import('../types.js').SelectExpr} expr Expressions to add. + * @returns {this} + */ + setSelect(...expr) { + this._select = []; + return this.select(...expr); + } + + /** + * Indicate if this query should retrieve distinct values only. + * @param {boolean} value The distinct flag + * @returns {this} + */ + distinct(value = true) { + this._distinct = !!value; + return this; + } + + /** + * Add table FROM expressions. + * @param {...import('../types.js').FromExpr} expr Expressions to add. + * @returns {this} + */ + from(...expr) { + const list = []; + const add = (v, as) => list.push(new FromClauseNode(asTableRef(v), unquote(as))); + expr.flat().forEach(e => { + if (e == null) return; + else if (isString(e)) add(e, e); + else if (isTableRef(e)) add(e, e.name); + else if (isNode(e)) add(e); + else if (isArray(e)) add(e[1], e[0]); + else for (const alias in e) add(e[alias], alias); + }); + this._from = this._from.concat(list); + return this; + } + + /** + * Set FROM expressions, replacing any prior expressions. + * @param {...import('../types.js').FromExpr} expr Expressions to add. + * @returns {this} + */ + setFrom(...expr) { + this._from = []; + return this.from(...expr); + } + + /** + * Set SAMPLE settings. + * @param {number | SampleClauseNode} value Either a sample clause node + * or the sample size as either a row count or percentage. + * @param {import('./sample.js').SampleMethod} [method] The sampling method + * to use. + * @param {number} [seed] The random seed. + * @returns {this} + */ + sample(value, method, seed) { + let clause; + if (typeof value === 'number') { + const perc = value > 0 && value < 1; + const size = perc ? value * 100 : Math.floor(value); + clause = new SampleClauseNode(size, perc, method, seed); + } else { + clause = value; + } + this._sample = clause; + return this; + } + + /** + * Add WHERE expressions. + * @param {...import('../types.js').FilterExpr} expr Expressions to add. + * @returns {this} + */ + where(...expr) { + this._where = this._where.concat(exprList(expr, asVerbatim)); + return this; + } + + /** + * Set WHERE expressions, replacing any prior expressions. + * @param {...import('../types.js').FilterExpr} expr Expressions to add. + * @returns {this} + */ + setWhere(...expr) { + this._where = []; + return this.where(...expr); + } + + /** + * Add GROUP BY expressions. + * @param {...import('../types.js').GroupByExpr} expr Expressions to add. + * @returns {this} + */ + groupby(...expr) { + this._groupby = this._groupby.concat(exprList(expr)); + return this; + } + + /** + * Set GROUP BY expressions, replacing any prior expressions. + * @param {...import('../types.js').GroupByExpr} expr Expressions to add. + * @returns {this} + */ + setGroupby(...expr) { + this._groupby = []; + return this.groupby(...expr); + } + + /** + * Add HAVING expressions. + * @param {...import('../types.js').FilterExpr} expr Expressions to add. + * @returns {this} + */ + having(...expr) { + this._having = this._having.concat(exprList(expr, asVerbatim)); + return this; + } + + /** + * Add WINDOW definitions. + * @param {...any} expr Expressions to add. + * @returns {this} + */ + window(...expr) { + const list = []; + expr.flat().forEach(e => { + if (e != null) for (const name in e) { + list.push(new WindowClauseNode(unquote(name), e[name])); + } + }); + this._window = this._window.concat(list); + return this; + } + + /** + * Add QUALIFY expressions. + * @param {...import('../types.js').FilterExpr} expr Expressions to add. + * @returns {this} + */ + qualify(...expr) { + this._qualify = this._qualify.concat(exprList(expr, asVerbatim)); + return this; + } + + /** + * Generate a SQL query string. + * @returns {string} + */ + toString() { + const { + _with, _select, _distinct, _from, _sample, _where, _groupby, + _having, _window, _qualify, _orderby, _limit, _offset + } = this; + const sql = []; + + // WITH + if (_with.length) sql.push(`WITH ${_with.join(', ')}`); + + // SELECT + sql.push(`SELECT${_distinct ? ' DISTINCT' : ''} ${_select.join(', ')}`); + + // FROM + if (_from.length) sql.push(`FROM ${_from.join(', ')}`); + + // WHERE + if (_where.length) { + const clauses = _where.map(String).filter(x => x).join(' AND '); + if (clauses) sql.push(`WHERE ${clauses}`); + } + + // SAMPLE + if (_sample) sql.push(`USING SAMPLE ${_sample}`); + + // GROUP BY + if (_groupby.length) { + sql.push(`GROUP BY ${_groupby.join(', ')}`); + } + + // HAVING + if (_having.length) { + const clauses = _having.map(String).filter(x => x).join(' AND '); + if (clauses) sql.push(`HAVING ${clauses}`); + } + + // WINDOW + if (_window.length) sql.push(`WINDOW ${_window.join(', ')}`); + + // QUALIFY + if (_qualify.length) { + const clauses = _qualify.map(String).filter(x => x).join(' AND '); + if (clauses) sql.push(`QUALIFY ${clauses}`); + } + + // ORDER BY + if (_orderby.length) sql.push(`ORDER BY ${_orderby.join(', ')}`); + + // LIMIT + if (Number.isFinite(_limit)) sql.push(`LIMIT ${_limit}`); + + // OFFSET + if (Number.isFinite(_offset)) sql.push(`OFFSET ${_offset}`); + + return sql.join(' '); + } +} + +export class DescribeQuery extends SQLNode { + /** + * Instantiate a describe query. + */ + constructor(query) { + super(DESCRIBE_QUERY); + this.query = query; + } + + /** + * Clone this describe query. + * @returns {DescribeQuery} + */ + clone() { + return new DescribeQuery(this.query.clone()); + } + + /** + * Generate a SQL query string. + * @returns {string} + */ + toString() { + return `DESCRIBE ${this.query}`; + } +} + +export class SetOperation extends Query { + /** + * Instantiate a new set operation instance. + * @param {string} op The set operation. + * @param {Query[]} queries The subqueries. + */ + constructor(op, queries) { + super(SET_OPERATION); + /** + * @type {string} + * @readonly + */ + this.op = op; + /** + * @type {Query[]} + * @readonly + */ + this.queries = queries; + } + + /** + * Return a list of subqueries. + * @returns {Query[]} + */ + get subqueries() { + const { queries, cteFor } = this; + // TODO: revisit this? + if (cteFor) queries.forEach(q => q.cteFor = cteFor); + return queries; + } + + /** + * Clone this set operation. + * @returns {SetOperation} + */ + clone() { + const { op, queries, ...rest } = this; + return Object.assign(new SetOperation(op, queries), rest); + } + + /** + * Generate a SQL query string. + * @returns {string} + */ + toString() { + const { op, queries, _orderby, _limit, _offset } = this; + + // SUBQUERIES + const sql = [ queries.join(` ${op} `) ]; + + // ORDER BY + if (_orderby.length) sql.push(`ORDER BY ${_orderby.join(', ')}`); + + // LIMIT + if (Number.isFinite(_limit)) sql.push(`LIMIT ${_limit}`); + + // OFFSET + if (Number.isFinite(_offset)) sql.push(`OFFSET ${_offset}`); + + return sql.join(' '); + } +} diff --git a/packages/sql/src/ast/sample.js b/packages/sql/src/ast/sample.js new file mode 100644 index 00000000..ed286043 --- /dev/null +++ b/packages/sql/src/ast/sample.js @@ -0,0 +1,53 @@ +import { SAMPLE_CLAUSE } from '../constants.js'; +import { SQLNode } from './node.js'; + +/** + * @typedef {'reservoir' | 'bernoulli' | 'system'} SampleMethod + */ + +export class SampleClauseNode extends SQLNode { + /** + * Instantiate a sample clause node. + * @param {number} size The sample size as either a row count or percentage. + * @param {boolean} [perc=false] Flag indicating if the sampling unit is + * rows (`false`) or a percentage (`true`). + * @param {SampleMethod} [method] The sampling method. If unspecified, + * a default method is applied based on the sampling unit. + * @param {number} [seed] The random seed. + */ + constructor(size, perc = false, method = undefined, seed = undefined) { + super(SAMPLE_CLAUSE); + /** + * The sample size as either a row count or percentage. + * @type {number} + * @readonly + */ + this.size = size; + /** + * Flag indicating if the sampling unit is rows (`false`) or a + * percentage (`true`). + * @type {boolean} + * @readonly + */ + this.perc = perc; + /** + * The sampling method. + * @type {SampleMethod} + * @readonly + */ + this.method = method; + /** + * The random seed. + * @type {number} + * @readonly + */ + this.seed = seed; + } + + toString() { + const { size, perc, method, seed } = this; + const unit = perc ? '%' : ' ROWS'; + const s = seed != null ? `, ${seed}` : ''; + return `${size}${unit}${method ? ` (${method}${s})` : ''}`; + } +} diff --git a/packages/sql/src/ast/select.js b/packages/sql/src/ast/select.js new file mode 100644 index 00000000..c5f804d1 --- /dev/null +++ b/packages/sql/src/ast/select.js @@ -0,0 +1,44 @@ +import { SELECT_CLAUSE } from '../constants.js'; +import { quoteIdentifier } from '../util/string.js'; +import { ColumnRefNode } from './column-ref.js'; +import { ExprNode, SQLNode } from './node.js'; + +export class SelectClauseNode extends SQLNode { + /** + * Instantiate a select node. + * @param {ExprNode} expr The select expression. + * @param {string} alias The output name. + */ + constructor(expr, alias) { + super(SELECT_CLAUSE); + /** + * The select expression. + * @type {ExprNode} + * @readonly + */ + this.expr = expr; + /** + * The output name. + * @type {string} + * @readonly + */ + this.alias = alias; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + const { expr, alias } = this; + return !alias || isColumnRefFor(expr, alias) + ? `${expr}` + : `${expr} AS ${quoteIdentifier(alias)}`; + } +} + +function isColumnRefFor(expr, name) { + return expr instanceof ColumnRefNode + && expr.table == null + && expr.column === name; +} diff --git a/packages/sql/src/ast/table-ref.js b/packages/sql/src/ast/table-ref.js new file mode 100644 index 00000000..0d6378d4 --- /dev/null +++ b/packages/sql/src/ast/table-ref.js @@ -0,0 +1,44 @@ +import { TABLE_REF } from '../constants.js'; +import { quoteIdentifier } from '../util/string.js'; +import { ExprNode } from './node.js'; + +/** + * Check if a value is a table reference node. + * @param {*} value The value to check. + * @returns {value is TableRefNode} + */ +export function isTableRef(value) { + return value instanceof TableRefNode; +} + +export class TableRefNode extends ExprNode { + /** + * Instantiate a table reference node. + * @param {string | string[]} table The table name. + */ + constructor(table) { + super(TABLE_REF); + /** + * The table name, including namespaces. + * @type {string[]} + * @readonly + */ + this.table = [table].flat(); + } + + /** + * The table name without database or schema namespaces. + * @returns {string} + */ + get name() { + return this.table[this.table.length - 1]; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + return this.table.map(t => quoteIdentifier(t)).join('.'); + } +} diff --git a/packages/sql/src/ast/unary-op.js b/packages/sql/src/ast/unary-op.js new file mode 100644 index 00000000..83ca6f06 --- /dev/null +++ b/packages/sql/src/ast/unary-op.js @@ -0,0 +1,64 @@ +import { UNARY_OPERATOR, UNARY_POSTFIX_OPERATOR } from '../constants.js'; +import { ExprNode } from './node.js'; + +class AbstractUnaryOpNode extends ExprNode { + /** + * Instantiate an abstract unary operator node. + * @param {string} type The node type. + * @param {string} op The operator type. + * @param {ExprNode} expr The input expression. + */ + constructor(type, op, expr) { + super(type); + /** + * The operator type. + * @type {string} + * @readonly + */ + this.op = op; + /** + * The input expression. + * @type {ExprNode} + * @readonly + */ + this.expr = expr; + } +} + +export class UnaryOpNode extends AbstractUnaryOpNode { + /** + * Instantiate a unary operator node. + * @param {string} op The operator type. + * @param {ExprNode} expr The input expression. + */ + constructor(op, expr) { + super(UNARY_OPERATOR, op, expr); + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + return `(${this.op} ${this.expr})`; + } +} + +export class UnaryPosftixOpNode extends AbstractUnaryOpNode { + /** + * Instantiate a unary operator node. + * @param {string} op The operator type. + * @param {ExprNode} expr The input expression. + */ + constructor(op, expr) { + super(UNARY_POSTFIX_OPERATOR, op, expr); + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + return `(${this.expr} ${this.op})`; + } +} diff --git a/packages/sql/src/ast/verbatim.js b/packages/sql/src/ast/verbatim.js new file mode 100644 index 00000000..6a3eb342 --- /dev/null +++ b/packages/sql/src/ast/verbatim.js @@ -0,0 +1,26 @@ +import { VERBATIM } from '../constants.js'; +import { ExprNode } from './node.js'; + +export class VerbatimNode extends ExprNode { + /** + * Instantiate a raw node with verbatim content. + * @param {string} value The verbatim content to include. + */ + constructor(value) { + super(VERBATIM); + /** + * The verbatim content to include. + * @type {string} + * @readonly + */ + this.value = value; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + return this.value; + } +} diff --git a/packages/sql/src/ast/window.js b/packages/sql/src/ast/window.js new file mode 100644 index 00000000..3fa99221 --- /dev/null +++ b/packages/sql/src/ast/window.js @@ -0,0 +1,290 @@ +import { WINDOW, WINDOW_CLAUSE, WINDOW_DEF, WINDOW_FRAME } from '../constants.js'; +import { exprList } from '../util/function.js'; +import { quoteIdentifier } from '../util/string.js'; +import { isParamLike } from '../util/type-check.js'; +import { AggregateNode } from './aggregate.js'; +import { FunctionNode } from './function.js'; +import { ExprNode, isNode, SQLNode } from './node.js'; +import { ParamNode } from './param.js'; + +/** + * @typedef {[any, any] | import('../types.js').ParamLike} FrameExtent + */ + +export class WindowClauseNode extends SQLNode { + /** + * Instantiate a window clause node. + * @param {string} name The window name. + * @param {WindowDefNode} def The window definition. + */ + constructor(name, def) { + super(WINDOW_CLAUSE); + /** + * The window name. + * @type {string} + * @readonly + */ + this.name = name; + /** + * The window definition. + * @type {WindowDefNode} + * @readonly + */ + this.def = def; + } + + toString() { + return `${quoteIdentifier(this.name)} AS ${this.def}`; + } +} + +export class WindowNode extends ExprNode { + /** + * Instantiate a window node. + * @param {WindowFunctionNode | AggregateNode} func The window function call. + * @param {WindowDefNode} [over] The window definition or name. + */ + constructor(func, over = new WindowDefNode()) { + super(WINDOW); + /** + * @type {WindowFunctionNode | AggregateNode} + * @readonly + */ + this.func = func; + /** + * @type {WindowDefNode} + * @readonly + */ + this.def = over; + } + + /** + * Return an updated window over a named window definition. + * @param {string} name The window definition name. + * @returns {WindowNode} A new window node. + */ + over(name) { + return new WindowNode(this.func, this.def.over(name)); + } + + /** + * Return an updated window with the given partitions. + * @param {...import('../types.js').ExprVarArgs} expr The partition by criteria. + * @returns {WindowNode} A new window node. + */ + partitionby(...expr) { + return new WindowNode(this.func, this.def.partitionby(...expr)); + } + + /** + * Return an updated window with the given ordering. + * @param {...import('../types.js').ExprVarArgs} expr The order by criteria. + * @returns {WindowNode} A new window node. + */ + orderby(...expr) { + return new WindowNode(this.func, this.def.orderby(...expr)); + } + + /** + * Return an updated window with the given rows frame. + * @param {FrameExtent} extent The row-based window frame extent. + * @returns {WindowNode} A new window node. + */ + rows(extent) { + return new WindowNode(this.func, this.def.rows(extent)); + } + + /** + * Return an updated window with the given range frame. + * @param {FrameExtent} extent The range-based window frame extent. + * @returns {WindowNode} A new window node. + */ + range(extent) { + return new WindowNode(this.func, this.def.range(extent)); + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + return `${this.func} OVER ${this.def}`; + } +} + +export class WindowFunctionNode extends FunctionNode { + /** + * Instantiate a window function call node. + * @param {import('../types.js').WindowFunctionName} name The function name. + * @param {ExprNode[]} [args=[]] The function arguments. + */ + constructor(name, args) { + super(name, args); + } +} + +export class WindowDefNode extends SQLNode { + /** + * Instantiate a window definition node. + * @param {string} [name] The base window definition name. + * @param {ExprNode[]} [partition] The partition by criteria. + * @param {ExprNode[]} [order] The order by criteria. + * @param {WindowFrameNode} [frame] The window frame definition. + */ + constructor(name, partition, order, frame) { + super(WINDOW_DEF); + /** + * The base window definition name. + * @type {string} + * @readonly + */ + this.name = name; + /** + * The partition by criteria. + * @type {ExprNode[]} + * @readonly + */ + this.partition = partition; + /** + * The order by criteria. + * @type {ExprNode[]} + * @readonly + */ + this.order = order; + /** + * The window frame definition. + * @type {WindowFrameNode} + * @readonly + */ + this.frame = frame; + } + + /** + * Return an updated window definition with the given base name. + * @param {string} name The base window definition name. + * @returns {WindowDefNode} A new window definition node. + */ + over(name) { + return deriveDef(this, { name }); + } + + /** + * Return an updated window definition with the given partitions. + * @param {...import('../types.js').ExprVarArgs} expr The partition by criteria. + * @returns {WindowDefNode} A new window definition node. + */ + partitionby(...expr) { + return deriveDef(this, { partition: exprList(expr) }); + } + + /** + * Return an updated window definition with the given ordering. + * @param {...import('../types.js').ExprVarArgs} expr The order by criteria. + * @returns {WindowDefNode} A new window definition node. + */ + orderby(...expr) { + return deriveDef(this, { order: exprList(expr) }); + } + + /** + * Return an updated window definition with the given rows frame. + * @param {FrameExtent} extent The row-based window frame extent. + * @returns {WindowDefNode} A new window definition node. + */ + rows(extent) { + return deriveDef(this, { frame: new WindowFrameNode(extent) }); + } + + /** + * Return an updated window definition with the given range frame. + * @param {FrameExtent} extent The range-based window frame extent. + * @returns {WindowDefNode} A new window definition node. + */ + range(extent) { + return deriveDef(this, { frame: new WindowFrameNode(extent, true) }); + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + const { name, partition, order, frame } = this; + const base = name && quoteIdentifier(name); + const def = [ + base, + partition?.length && `PARTITION BY ${partition.join(', ')}`, + order?.length && `ORDER BY ${order.join(', ')}`, + frame + ].filter(x => x); + return base && def.length < 2 ? base : `(${def.join(' ')})`; + } +} + +export class WindowFrameNode extends SQLNode { + /** + * Instantiate a window frame definition node. + * @param {FrameExtent} extent The frame extent as [preceding, following] + * row or interval offsets. + * @param {boolean} [range] The frame type: `true` for range, otherwise rows. + * @param {ExprNode} [exclude] The window exclusion criteria. + */ + constructor(extent, range = false, exclude = undefined) { + super(WINDOW_FRAME); + /** + * The frame extent as [preceding, following] row or interval offsets. + * @type {ParamNode | [any, any]} + * @readonly + */ + this.extent = isParamLike(extent) ? new ParamNode(extent) : extent; + /** + * The frame type: `true` for range, otherwise rows. + * @type {boolean} + * @readonly + */ + this.range = range; + /** + * The window exclusion criteria. + * @type {ExprNode} + * @readonly + */ + this.exclude = exclude; + } + + /** + * Generate a SQL query string for this node. + * @returns {string} + */ + toString() { + const { range, exclude, extent } = this; + const type = range ? 'RANGE' : 'ROWS'; + const [prev, next] = isNode(extent) ? extent.value : extent; + const a = frameValue(prev, 'PRECEDING'); + const b = frameValue(next, 'FOLLOWING'); + return `${type} BETWEEN ${a} AND ${b}${exclude ? ` ${exclude}` : ''}`; + } +} + +/** + * Derive a new window definition node from an existing one. + * @param {WindowDefNode} def The existing window definition. + * @param {object} options An options object with new definition properties. + * @param {string} [options.name] The base window definition name. + * @param {ExprNode[]} [options.partition] The partition by criteria. + * @param {ExprNode[]} [options.order] The order by criteria. + * @param {WindowFrameNode} [options.frame] The window frame definition. + */ +function deriveDef(def, options) { + return new WindowDefNode( + options.name ?? def.name, + options.partition ?? def.partition, + options.order ?? def.order, + options.frame ?? def.frame + ); +} + +function frameValue(value, order) { + return value === 0 ? 'CURRENT ROW' + : Number.isFinite(value) ? `${Math.abs(value)} ${order}` + : `UNBOUNDED ${order}`; +} diff --git a/packages/sql/src/ast/with.js b/packages/sql/src/ast/with.js new file mode 100644 index 00000000..a3630125 --- /dev/null +++ b/packages/sql/src/ast/with.js @@ -0,0 +1,30 @@ +import { WITH_CLAUSE } from '../constants.js'; +import { SQLNode } from './node.js'; +import { Query } from './query.js'; + +export class WithClauseNode extends SQLNode { + /** + * Instantiate a with clause node for a common table expression (CTE). + * @param {string} name The common table expression (CTE) name. + * @param {Query} query The common table expression (CTE) query. + */ + constructor(name, query) { + super(WITH_CLAUSE); + /** + * The common table expression (CTE) name. + * @type {string} + * @readonly + */ + this.name = name; + /** + * The common table expression (CTE) query. + * @type {Query} + * @readonly + */ + this.query = query; + } + + toString() { + return `"${this.name}" AS (${this.query})`; + } +} diff --git a/packages/sql/src/cast.js b/packages/sql/src/cast.js deleted file mode 100644 index 2236b8c0..00000000 --- a/packages/sql/src/cast.js +++ /dev/null @@ -1,19 +0,0 @@ -import { sql } from './expression.js'; -import { asColumn } from './ref.js'; - -export function cast(expr, type) { - const arg = asColumn(expr); - const e = sql`CAST(${arg} AS ${type})`; - Object.defineProperty(e, 'label', { - enumerable: true, - get() { return expr.label; } - }); - Object.defineProperty(e, 'aggregate', { - enumerable: true, - get() { return expr.aggregate || false; } - }); - return e; -} - -export const castDouble = expr => cast(expr, 'DOUBLE'); -export const castInteger = expr => cast(expr, 'INTEGER'); diff --git a/packages/sql/src/constants.js b/packages/sql/src/constants.js new file mode 100644 index 00000000..0ceb819b --- /dev/null +++ b/packages/sql/src/constants.js @@ -0,0 +1,43 @@ +export const COLUMN_REF = 'COLUMN_REF'; +export const TABLE_REF = 'TABLE_REF'; +export const LITERAL = 'LITERAL'; +export const INTERVAL = 'INTERVAL'; + +export const ORDER_BY = 'ORDER_BY'; +export const CAST = 'CAST'; +export const CASE = 'CASE'; +export const WHEN = 'WHEN'; + +export const UNARY_OPERATOR = 'UNARY'; +export const UNARY_POSTFIX_OPERATOR = 'UNARY_POSTFIX'; +export const BINARY_OPERATOR = 'BINARY'; +export const BETWEEN_OPERATOR = 'BETWEEN'; +export const NOT_BETWEEN_OPERATOR = 'NOT_BETWEEN'; +export const LOGICAL_OPERATOR = 'LOGICAL_OPERATOR'; +export const IN_OPERATOR = 'IN'; + +export const FUNCTION = 'FUNCTION'; +export const AGGREGATE = 'AGGREGATE'; +export const WINDOW = 'WINDOW'; +export const WINDOW_DEF = 'WINDOW_DEF'; +export const WINDOW_FRAME = 'WINDOW_FRAME'; + +export const EXPRESSION = 'EXPRESSION'; +export const FRAGMENT = 'FRAGMENT'; +export const VERBATIM = 'VERBATIM'; +export const PARAM = 'PARAM'; + +export const WITH_CLAUSE = 'WITH_CLAUSE'; +export const SELECT_CLAUSE = 'SELECT_CLAUSE'; +export const FROM_CLAUSE = 'FROM_CLAUSE'; +export const WHERE_CLAUSE = 'WHERE_CLAUSE'; +export const SAMPLE_CLAUSE = 'SAMPLE_CLAUSE'; +export const GROUPBY_CLAUSE = 'GROUPBY_CLAUSE'; +export const HAVING_CLAUSE = 'HAVING_CLAUSE'; +export const WINDOW_CLAUSE = 'WINDOW_CLAUSE'; +export const QUALIFY_CLAUSE = 'QUALIFY_CLAUSE'; +export const ORDERBY_CLAUSE = 'ORDERBY_CLAUSE'; + +export const SELECT_QUERY = 'SELECT_QUERY'; +export const DESCRIBE_QUERY = 'DESCRIBE_QUERY'; +export const SET_OPERATION = 'SET_OPERATION'; diff --git a/packages/sql/src/datetime.js b/packages/sql/src/datetime.js deleted file mode 100644 index a0d988a8..00000000 --- a/packages/sql/src/datetime.js +++ /dev/null @@ -1,31 +0,0 @@ -import { sql } from './expression.js'; -import { asColumn } from './ref.js'; - -export const epoch_ms = expr => { - return sql`epoch_ms(${asColumn(expr)})`; -}; - -export function dateBin(expr, interval, steps = 1) { - const i = `INTERVAL ${steps} ${interval}`; - const d = asColumn(expr); - return sql`TIME_BUCKET(${i}, ${d})` - .annotate({ label: interval }); -} - -export const dateMonth = expr => { - const d = asColumn(expr); - return sql`MAKE_DATE(2012, MONTH(${d}), 1)` - .annotate({ label: 'month' }); -}; - -export const dateMonthDay = expr => { - const d = asColumn(expr); - return sql`MAKE_DATE(2012, MONTH(${d}), DAY(${d}))` - .annotate({ label: 'date' }); -}; - -export const dateDay = expr => { - const d = asColumn(expr); - return sql`MAKE_DATE(2012, 1, DAY(${d}))` - .annotate({ label: 'date' }); -}; diff --git a/packages/sql/src/desc.js b/packages/sql/src/desc.js deleted file mode 100644 index 2723988d..00000000 --- a/packages/sql/src/desc.js +++ /dev/null @@ -1,13 +0,0 @@ -import { sql } from './expression.js'; -import { asColumn } from './ref.js'; - -/** - * Annotate an expression to indicate descending sort order. - * Null values are ordered last. - * @param {import('./expression.js').SQLExpression|string} expr A SQL expression or column name string. - * @returns {import('./expression.js').SQLExpression} An expression with descending order. - */ -export function desc(expr) { - const e = asColumn(expr); - return sql`${e} DESC NULLS LAST`.annotate({ label: e?.label, desc: true }); -} diff --git a/packages/sql/src/expression.js b/packages/sql/src/expression.js deleted file mode 100644 index c84bbdba..00000000 --- a/packages/sql/src/expression.js +++ /dev/null @@ -1,170 +0,0 @@ -import { literalToSQL } from './to-sql.js'; - -/** - * @typedef {{ - * value: any; - * addEventListener(type: string, callback: Function): any; - * column?: string, - * columns?: string[] - * }} ParamLike - */ - -/** - * Test if a value is parameter-like. Parameters have addEventListener methods. - * @param {*} value The value to test. - * @returns {value is ParamLike} True if the value is param-like, false otherwise. - */ -export const isParamLike = value => typeof value?.addEventListener === 'function'; - -/** - * Test if a value is a SQL expression instance. - * @param {*} value The value to test. - * @returns {value is SQLExpression} True if value is a SQL expression, false otherwise. - */ -export function isSQLExpression(value) { - return value instanceof SQLExpression; -} - -/** - * Base class for all SQL expressions. Most callers should use the `sql` - * template tag rather than instantiate this class. - */ -export class SQLExpression { - - /** - * Create a new SQL expression instance. - * @param {(string | ParamLike | SQLExpression | import('./ref.js').Ref)[]} parts The parts of the expression. - * @param {string[]} [columns=[]] The column dependencies - * @param {object} [props] Additional properties for this expression. - */ - constructor(parts, columns, props) { - this._expr = Array.isArray(parts) ? parts : [parts]; - this._deps = columns || []; - this.annotate(props); - - const params = this._expr.filter(part => isParamLike(part)); - if (params.length > 0) { - /** @type {ParamLike[]} */ - // @ts-ignore - this._params = Array.from(new Set(params)); - this._params.forEach(param => { - param.addEventListener('value', () => update(this, this.map?.get('value'))); - }); - } else { - // do not support event listeners if not needed - // this causes the expression instance to NOT be param-like - this.addEventListener = undefined; - } - } - - /** - * A reference to this expression. - * Provides compatibility with param-like objects. - */ - get value() { - return this; - } - - /** - * The column dependencies of this expression. - * @returns {string[]} The columns dependencies. - */ - get columns() { - const { _params, _deps } = this; - if (_params) { - // pull latest dependencies, as they may change across updates - const pset = new Set(_params.flatMap(p => { - const cols = p.value?.columns; - return Array.isArray(cols) ? cols : []; - })); - if (pset.size) { - const set = new Set(_deps); - pset.forEach(col => set.add(col)); - return Array.from(set); - } - } - // if no params, return fixed dependencies - return _deps; - } - - /** - * The first column dependency in this expression, or undefined if none. - * @returns {string} The first column dependency. - */ - get column() { - return this._deps.length ? this._deps[0] : this.columns[0]; - } - - /** - * Annotate this expression instance with additional properties. - * @param {object[]} [props] One or more objects with properties to add. - * @returns This SQL expression. - */ - annotate(...props) { - return Object.assign(this, ...props); - } - - /** - * Generate a SQL code string corresponding to this expression. - * @returns {string} A SQL code string. - */ - toString() { - return this._expr - .map(p => isParamLike(p) && !isSQLExpression(p) ? literalToSQL(p.value) : p) - .join(''); - } - - /** - * Add an event listener callback for the provided event type. - * @param {string} type The event type to listen for (for example, "value"). - * @param {(a: SQLExpression) => Promise?} callback The callback function to - * invoke upon updates. A callback may optionally return a Promise that - * upstream listeners may await before proceeding. - */ - addEventListener(type, callback) { - const map = this.map || (this.map = new Map()); - const set = map.get(type) || (map.set(type, new Set), map.get(type)); - set.add(callback); - } -} - -function update(expr, callbacks) { - if (callbacks?.size) { - return Promise.allSettled(Array.from(callbacks, fn => fn(expr))); - } -} - -export function parseSQL(strings, exprs) { - const spans = [strings[0]]; - const cols = new Set; - const n = exprs.length; - for (let i = 0, k = 0; i < n;) { - const e = exprs[i]; - if (isParamLike(e)) { - spans[++k] = e; - } else { - if (Array.isArray(e?.columns)) { - e.columns.forEach(col => cols.add(col)); - } - spans[k] += typeof e === 'string' ? e : literalToSQL(e); - } - const s = strings[++i]; - if (isParamLike(spans[k])) { - spans[++k] = s; - } else { - spans[k] += s; - } - } - - return { spans, cols: Array.from(cols) }; -} - -/** - * Tag function for SQL expressions. Interpolated values - * may be strings, other SQL expression objects (such as column - * references), or parameterized values. - */ -export function sql(strings, ...exprs) { - const { spans, cols } = parseSQL(strings, exprs); - return new SQLExpression(spans, cols); -} diff --git a/packages/sql/src/functions.js b/packages/sql/src/functions.js deleted file mode 100644 index af9e091c..00000000 --- a/packages/sql/src/functions.js +++ /dev/null @@ -1,25 +0,0 @@ -import { sql } from './expression.js'; -import { asColumn } from './ref.js'; -import { repeat } from './repeat.js'; - -export function functionCall(op, type) { - return (...values) => { - const args = values.map(asColumn); - const cast = type ? `::${type}` : ''; - const expr = args.length - ? sql([`${op}(`, ...repeat(args.length - 1, ', '), `)${cast}`], ...args) - : sql`${op}()${cast}`; - return expr.annotate({ func: op, args }); - } -} - -export const regexp_matches = functionCall('REGEXP_MATCHES'); -export const contains = functionCall('CONTAINS'); -export const prefix = functionCall('PREFIX'); -export const suffix = functionCall('SUFFIX'); -export const lower = functionCall('LOWER'); -export const upper = functionCall('UPPER'); -export const length = functionCall('LENGTH'); -export const isNaN = functionCall('ISNAN'); -export const isFinite = functionCall('ISFINITE'); -export const isInfinite = functionCall('ISINF'); diff --git a/packages/sql/src/functions/aggregate.js b/packages/sql/src/functions/aggregate.js new file mode 100644 index 00000000..f602c4a8 --- /dev/null +++ b/packages/sql/src/functions/aggregate.js @@ -0,0 +1,335 @@ +import { AggregateNode } from '../ast/aggregate.js'; +import { aggFn } from '../util/function.js'; + +/** + * Compute an arg_max aggregate. + * @param {import('../types.js').ExprValue} y The argument to return. + * @param {import('../types.js').ExprValue} x The expression to maximize. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function argmax(y, x) { + return aggFn('arg_max', y, x); +} + +/** + * Compute an arg_min aggregate. + * @param {import('../types.js').ExprValue} y The argument to return. + * @param {import('../types.js').ExprValue} x The expression to minimize. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function argmin(y, x) { + return aggFn('arg_min', y, x); +} + +/** + * Compute an array aggregation. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function arrayAgg(expr) { + return aggFn('array_agg', expr); +} + +/** + * Compute an average aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function avg(expr) { + return aggFn('avg', expr); +} + +/** + * Compute a correlation aggregate. + * @param {import('../types.js').ExprValue} x The x expression to aggregate. + * @param {import('../types.js').ExprValue} y The y expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function corr(x, y) { + return aggFn('corr', x, y); +} + +/** + * Compute a count aggregate. + * @param {import('../types.js').ExprValue} [expr] An optional expression + * to count. If specified, only non-null expression values are counted. + * If omitted, all rows within a group are counted. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function count(expr) { + return aggFn('count', expr); +} + +/** + * Compute a sample covariance aggregate. + * @param {import('../types.js').ExprValue} x The x expression to aggregate. + * @param {import('../types.js').ExprValue} y The y expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function covariance(x, y) { + return aggFn('covar_samp', x, y); +} + +/** + * Compute a population covariance aggregate. + * @param {import('../types.js').ExprValue} x The x expression to aggregate. + * @param {import('../types.js').ExprValue} y The y expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function covarPop(x, y) { + return aggFn('covar_pop', x, y); +} + +/** + * Compute an entropy aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function entropy(expr) { + return aggFn('entropy', expr); +} + +/** + * Compute a first aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function first(expr) { + return aggFn('first', expr); +} + +/** + * Compute a sample kurtosis aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function kurtosis(expr) { + return aggFn('kurtosis', expr); +} + +/** + * Compute a median absolute deviation (MAD) aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function mad(expr) { + return aggFn('mad', expr); +} + +/** + * Compute a maximum aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function max(expr) { + return aggFn('max', expr); +} + +/** + * Compute a median aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function median(expr) { + return aggFn('median', expr); +} + +/** + * Compute a minimum aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function min(expr) { + return aggFn('min', expr); +} + +/** + * Compute a mode aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function mode(expr) { + return aggFn('mode', expr); +} + +/** + * Compute a last aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function last(expr) { + return aggFn('last', expr); +} + +/** + * Compute a product aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function product(expr) { + return aggFn('product', expr); +} + +/** + * Compute a quantile aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @param {import('../types.js').ExprValue} p The quantile value. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function quantile(expr, p) { + return aggFn('quantile', expr, p); +} + +/** + * Compute a linear regression reg_avgX aggregate. + * @param {import('../types.js').ExprValue} x The x expression to aggregate. + * @param {import('../types.js').ExprValue} y The y expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function regrAvgX(x, y) { + return aggFn('regr_avgx', x, y); +} + +/** + * Compute a linear regression reg_avgY aggregate. + * @param {import('../types.js').ExprValue} x The x expression to aggregate. + * @param {import('../types.js').ExprValue} y The y expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function regrAvgY(x, y) { + return aggFn('regr_avgy', x, y); +} + +/** + * Compute a linear regression count aggregate. + * This returns the count of rows where both x and y are non-null. + * @param {import('../types.js').ExprValue} x The x expression to aggregate. + * @param {import('../types.js').ExprValue} y The y expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function regrCount(x, y) { + return aggFn('regr_count', x, y); +} + +/** + * Compute a linear regression intercept aggregate. + * @param {import('../types.js').ExprValue} x The x expression to aggregate. + * @param {import('../types.js').ExprValue} y The y expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function regrIntercept(x, y) { + return aggFn('regr_intercept', x, y); +} + +/** + * Compute a linear regression R^2 aggregate. + * @param {import('../types.js').ExprValue} x The x expression to aggregate. + * @param {import('../types.js').ExprValue} y The y expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function regrR2(x, y) { + return aggFn('regr_r2', x, y); +} + +/** + * Compute a linear regression regr_sxx aggregate. + * @param {import('../types.js').ExprValue} x The x expression to aggregate. + * @param {import('../types.js').ExprValue} y The y expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function regrSXX(x, y) { + return aggFn('regr_sxx', x, y); +} + +/** + * Compute a linear regression regr_sxy aggregate. + * @param {import('../types.js').ExprValue} x The x expression to aggregate. + * @param {import('../types.js').ExprValue} y The y expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function regrSXY(x, y) { + return aggFn('regr_sxy', x, y); +} + +/** + * Compute a linear regression regr_syy aggregate. + * @param {import('../types.js').ExprValue} x The x expression to aggregate. + * @param {import('../types.js').ExprValue} y The y expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function regrSYY(x, y) { + return aggFn('regr_syy', x, y); +} + +/** + * Compute a linear regression slope aggregate. + * @param {import('../types.js').ExprValue} x The x expression to aggregate. + * @param {import('../types.js').ExprValue} y The y expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function regrSlope(x, y) { + return aggFn('regr_slope', x, y); +} + +/** + * Compute a skewness aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function skewness(expr) { + return aggFn('skewness', expr); +} + +/** + * Compute a sample standard deviation aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function stddev(expr) { + return aggFn('stddev', expr); +} + +/** + * Compute a population standard deviation aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function stddevPop(expr) { + return aggFn('stddev_pop', expr); +} + +/** + * Compute a string aggregation. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function stringAgg(expr) { + return aggFn('string_agg', expr); +} + +/** + * Compute a sum aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function sum(expr) { + return aggFn('sum', expr); +} + +/** + * Compute a sample variance aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function variance(expr) { + return aggFn('var_samp', expr); +} + +/** + * Compute a population variance aggregate. + * @param {import('../types.js').ExprValue} expr The expression to aggregate. + * @returns {AggregateNode} A SQL aggregate function call. + */ +export function varPop(expr) { + return aggFn('var_pop', expr); +} diff --git a/packages/sql/src/functions/case.js b/packages/sql/src/functions/case.js new file mode 100644 index 00000000..7791eba3 --- /dev/null +++ b/packages/sql/src/functions/case.js @@ -0,0 +1,21 @@ +import { CaseNode, WhenNode } from '../ast/case.js'; +import { asNode } from '../util/ast.js'; + +/** + * Create a new conditional CASE statement. If three arguments are provided, + * acts like a ternary conditional (if, then else). If no arguments are + * provided, the chained `when` and `else` methods can be used to to complete + * a conditional statement with WHEN/THEN and ELSE expressions. + * @param {import('../types.js').ExprValue} [when] + * A conditional WHEN expression. + * @param {import('../types.js').ExprValue} [then] + * A THEN value expression. + * @param {import('../types.js').ExprValue} [other] + * An ELSE expression. + * @returns {CaseNode} + */ +export function cond(when, then, other) { + return when + ? new CaseNode(undefined, [new WhenNode(asNode(when), asNode(then))], asNode(other)) + : new CaseNode(); +} diff --git a/packages/sql/src/functions/cast.js b/packages/sql/src/functions/cast.js new file mode 100644 index 00000000..ddd74dfc --- /dev/null +++ b/packages/sql/src/functions/cast.js @@ -0,0 +1,39 @@ +import { CastNode } from '../ast/cast.js'; +import { asNode } from '../util/ast.js'; + +/** + * Perform a type cast. + * @param {*} expr The expression to type cast. + * @param {string} type The type to cast to. + * @returns {CastNode} + */ +export function cast(expr, type) { + return new CastNode(asNode(expr), type); +} + +/** + * Cast an expression to a 32-bit integer type. + * @param {*} expr The expression to type cast. + * @returns {CastNode} + */ +export function int32(expr) { + return cast(expr, 'INTEGER'); +} + +/** + * Cast an expression to a 32-bit floating point number type. + * @param {*} expr The expression to type cast. + * @returns {CastNode} + */ +export function float32(expr) { + return cast(expr, 'FLOAT'); +} + +/** + * Cast an expression to a 64-bit floating point number type. + * @param {*} expr The expression to type cast. + * @returns {CastNode} + */ +export function float64(expr) { + return cast(expr, 'DOUBLE'); +} diff --git a/packages/sql/src/functions/column.js b/packages/sql/src/functions/column.js new file mode 100644 index 00000000..3a6fba8a --- /dev/null +++ b/packages/sql/src/functions/column.js @@ -0,0 +1,13 @@ +import { ColumnRefNode } from '../ast/column-ref.js'; +import { TableRefNode } from '../ast/table-ref.js'; +import { asTableRef } from '../util/ast.js'; + +/** + * Create a column reference. + * @param {string} name The column name. + * @param {string | string[] | TableRefNode} [table] The table reference. + * @returns {ColumnRefNode} + */ +export function column(name, table) { + return new ColumnRefNode(name, asTableRef(table)); +} diff --git a/packages/sql/src/functions/datetime.js b/packages/sql/src/functions/datetime.js new file mode 100644 index 00000000..1a353a4f --- /dev/null +++ b/packages/sql/src/functions/datetime.js @@ -0,0 +1,65 @@ +import { FunctionNode } from '../ast/function.js'; +import { IntervalNode } from '../ast/interval.js'; +import { asNode } from '../util/ast.js'; +import { fn } from '../util/function.js'; + +/** + * Create a new interval. + * @param {string} unit The interval unit, such as day or year. + * @param {number} steps The number interval unit steps. + * @returns {IntervalNode} + */ +export function interval(unit, steps) { + return new IntervalNode(unit, steps); +} + +/** + * Given a date/time value, return the milliseconds since the UNIX epoch. + * @param {import('../types.js').ExprValue} expr The date/time expression. + * @returns {FunctionNode} + */ +export function epoch_ms(expr) { + return fn('epoch_ms', expr); +} + +/** + * Perform data binning according to the provided interval unit and steps. + * @param {import('../types.js').ExprValue} expr The date/time expression to bin. + * @param {string} unit The datetime interval unit to bin by. + * @param {number} [steps=1] The number of interval steps. + * @returns {FunctionNode} + */ +export function dateBin(expr, unit, steps = 1) { + return fn('time_bucket', interval(unit, steps), expr); +} + +/** + * Map date/times to a month value, all within the same year for comparison. + * The resulting value is still date-typed. + * @param {import('../types.js').ExprValue} expr The date/time expression. + * @returns {FunctionNode} + */ +export function dateMonth(expr) { + return fn('make_date', 2012, fn('month', expr), 1); +} + +/** + * Map date/times to a month and day value, all within the same year for + * comparison. The resulting value is still date-typed. + * @param {import('../types.js').ExprValue} expr The date/time expression. + * @returns {FunctionNode} + */ +export function dateMonthDay(expr) { + const d = asNode(expr); + return fn('make_date', 2012, fn('month', d), fn('day', d)); +} + +/** + * Map date/times to a day of the month value, all within the same year and month + * for comparison. The resulting value is still date-typed. + * @param {import('../types.js').ExprValue} expr The date/time expression. + * @returns {FunctionNode} + */ +export function dateDay(expr) { + return fn('make_date', 2012, 1, fn('day', expr)); +} diff --git a/packages/sql/src/functions/literal.js b/packages/sql/src/functions/literal.js new file mode 100644 index 00000000..011cc727 --- /dev/null +++ b/packages/sql/src/functions/literal.js @@ -0,0 +1,22 @@ +import { LiteralNode } from '../ast/literal.js'; +import { VerbatimNode } from '../ast/verbatim.js'; + +/** + * Return a SQL AST node for a literal value. The supported types are + * null, string, number, boolean, Date, and RegExp. Otherwise, the + * input value will be directly coerced to a string. + * @param {*} value The literal value. + * @returns {LiteralNode} + */ +export function literal(value) { + return new LiteralNode(value); +} + +/** + * Return a SQL AST node for verbatim string content. + * @param {string} value The verbatim value. + * @returns {VerbatimNode} + */ +export function verbatim(value) { + return new VerbatimNode(value); +} diff --git a/packages/sql/src/functions/numeric.js b/packages/sql/src/functions/numeric.js new file mode 100644 index 00000000..b9b247b3 --- /dev/null +++ b/packages/sql/src/functions/numeric.js @@ -0,0 +1,139 @@ +import { fn } from '../util/function.js'; + +/** + * Return true if the floating point value is not a number, false otherwise. + * @param {import('../types.js').ExprValue} expr The input number. + * @returns {import('../ast/function.js').FunctionNode} + */ +export function isNaN(expr) { + return fn('isnan', expr); +} + +/** + * Return true if the floating point value is finite, false otherwise. + * @param {import('../types.js').ExprValue} expr The input number. + * @returns {import('../ast/function.js').FunctionNode} + */ +export function isFinite(expr) { + return fn('isfinite', expr); +} + +/** + * Return true if the floating point value is infinite, false otherwise. + * @param {import('../types.js').ExprValue} expr The input number. + * @returns {import('../ast/function.js').FunctionNode} + */ +export function isInfinite(expr) { + return fn('isinf', expr); +} + +/** + * Selects the largest value. + * @param {...import('../types.js').ExprValue} expr The input expressions. + * @returns {import('../ast/function.js').FunctionNode} + */ +export function greatest(...expr) { + return fn('greatest', ...expr); +} + +/** + * Selects the smallest value. + * @param {...import('../types.js').ExprValue} expr The input expressions. + * @returns {import('../ast/function.js').FunctionNode} + */ +export function least(...expr) { + return fn('least', ...expr); +} + +/** + * Compute the exponentional function `e ** expr`. + * @param {import('../types.js').ExprValue} expr The input number. + * @returns {import('../ast/function.js').FunctionNode} + */ +export function exp(expr) { + return fn('exp', expr); +} + +/** + * Compute a base 10 logarithm. + * @param {import('../types.js').ExprValue} expr The input number. + * @returns {import('../ast/function.js').FunctionNode} + */ +export function log(expr) { + return fn('log', expr); +} + +/** + * Compute a natural logarithm. + * @param {import('../types.js').ExprValue} expr The input number. + * @returns {import('../ast/function.js').FunctionNode} + */ +export function ln(expr) { + return fn('ln', expr); +} + +/** + * Compute the sign of a number. + * @param {import('../types.js').ExprValue} expr The input number. + * @returns {import('../ast/function.js').FunctionNode} + */ +export function sign(expr) { + return fn('sign', expr); +} + +/** + * Compute the absolute value of a number. + * @param {import('../types.js').ExprValue} expr The input number. + * @returns {import('../ast/function.js').FunctionNode} + */ +export function abs(expr) { + return fn('abs', expr); +} + +/** + * Compute the square root of a number. + * @param {import('../types.js').ExprValue} expr The input number. + * @returns {import('../ast/function.js').FunctionNode} + */ +export function sqrt(expr) { + return fn('sqrt', expr); +} + +/** + * Rounds the number up. + * @param {import('../types.js').ExprValue} expr The input number. + * @returns {import('../ast/function.js').FunctionNode} + */ +export function ceil(expr) { + return fn('ceil', expr); +} + +/** + * Rounds the number down. + * @param {import('../types.js').ExprValue} expr The input number. + * @returns {import('../ast/function.js').FunctionNode} + */ +export function floor(expr) { + return fn('floor', expr); +} + +/** + * Round to the given decimal places. + * @param {import('../types.js').ExprValue} expr The input number. + * @param {import('../types.js').ExprValue} [places] The decimal places. + * Negative values are allowed, to round to tens, hundreds, etc. + * If unspecified, defaults to zero. + * @returns {import('../ast/function.js').FunctionNode} + */ +export function round(expr, places) { + return fn('round', expr, places); +} + +/** + * Truncates the number. + * @param {import('../types.js').ExprValue} expr The input number. + * @returns {import('../ast/function.js').FunctionNode} + */ +export function trunc(expr) { + return fn('trunc', expr); +} diff --git a/packages/sql/src/functions/operators.js b/packages/sql/src/functions/operators.js new file mode 100644 index 00000000..10b2d0b3 --- /dev/null +++ b/packages/sql/src/functions/operators.js @@ -0,0 +1,298 @@ +import { BetweenOpNode, NotBetweenOpNode } from '../ast/between-op.js'; +import { BinaryOpNode } from '../ast/binary-op.js'; +import { InOpNode } from '../ast/in-op.js'; +import { AndNode, OrNode } from '../ast/logical-op.js'; +import { UnaryOpNode, UnaryPosftixOpNode } from '../ast/unary-op.js'; +import { asNode } from '../util/ast.js'; +import { exprList } from '../util/function.js'; + +function unaryOp(op, expr) { + return new UnaryOpNode(op, asNode(expr)); +} + +function unaryPostfixOp(op, expr) { + return new UnaryPosftixOpNode(op, asNode(expr)); +} + +function binaryOp(op, left, right) { + return new BinaryOpNode(op, asNode(left), asNode(right)); +} + +function betweenOp(expr, extent, negate = false) { + const Op = negate ? NotBetweenOpNode : BetweenOpNode; + return new Op(asNode(expr), extent?.map(asNode)); +} + +/** + * Logical and (AND) operator. + * @param {...import('../types.js').ExprVarArgs} clauses The input expressions. + * @returns {AndNode} + */ +export function and(...clauses) { + return new AndNode(exprList(clauses)); +} + +/** + * Logical or (OR) operator. + * @param {...import('../types.js').ExprVarArgs} clauses The input expressions. + * @returns {OrNode} + */ +export function or(...clauses) { + return new OrNode(exprList(clauses)); +} + +/** + * Logical not (NOT) operator. + * @param {import('../types.js').ExprValue} expr The expression to negate. + * @returns {UnaryOpNode} + */ +export function not(expr) { + return unaryOp('NOT', expr); +} + +/** + * Null check (IS NULL) operator. + * @param {import('../types.js').ExprValue} expr The expression to test. + * @returns {UnaryPosftixOpNode} + */ +export function isNull(expr) { + return unaryPostfixOp('IS NULL', expr); +} + +/** + * Non-null check (IS NOT NULL) operator. + * @param {import('../types.js').ExprValue} expr The expression to test. + * @returns {UnaryPosftixOpNode} + */ +export function isNotNull(expr) { + return unaryPostfixOp('IS NOT NULL', expr); +} + +/** + * Bitwise not (~) operator. + * @param {import('../types.js').ExprValue} expr The input expression. + * @returns {UnaryOpNode} + */ +export function bitNot(expr) { + return unaryOp('~', expr); +} + +/** + * Bitwise and (&) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function bitAnd(left, right) { + return binaryOp('&', left, right); +} + +/** + * Bitwise or (|) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function bitOr(left, right) { + return binaryOp('|', left, right); +} + +/** + * Bit shift left (<<) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function bitLeft(left, right) { + return binaryOp('<<', left, right); +} + +/** + * Bit shift right (>>) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function bitRight(left, right) { + return binaryOp('>>', left, right); +} + +/** + * Addition (+) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function add(left, right) { + return binaryOp('+', left, right); +} + +/** + * Subtraction (-) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function sub(left, right) { + return binaryOp('-', left, right); +} + +/** + * Multiplication (*) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function mul(left, right) { + return binaryOp('*', left, right); +} + +/** + * Division (/) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function div(left, right) { + return binaryOp('/', left, right); +} + +/** + * Integer division (//) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function idiv(left, right) { + return binaryOp('//', left, right); +} + +/** + * Modulo (%) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function mod(left, right) { + return binaryOp('%', left, right); +} + +/** + * Exponentiation (**) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function pow(left, right) { + return binaryOp('**', left, right); +} + +/** + * Equality comparision (=) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function eq(left, right) { + return binaryOp('=', left, right); +} + +/** + * Non-equality comparision (<>) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function neq(left, right) { + return binaryOp('<>', left, right); +} + +/** + * Less-than comparision (<) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function lt(left, right) { + return binaryOp('<', left, right); +} + +/** + * Greater-than comparision (>) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function gt(left, right) { + return binaryOp('>', left, right); +} + +/** + * Less-than or equal comparision (<=) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function lte(left, right) { + return binaryOp('<=', left, right); +} + +/** + * Greater-than or equal comparision (>=) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function gte(left, right) { + return binaryOp('>=', left, right); +} + +/** + * Null-inclusive non-equality (IS DISTINCT FROM) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function isDistinct(left, right) { + return binaryOp('IS DISTINCT FROM', left, right); +} + +/** + * Null-inclusive equality (IS NOT DISTINCT FROM) operator. + * @param {import('../types.js').ExprValue} left The left argument. + * @param {import('../types.js').ExprValue} right The right argument. + * @returns {BinaryOpNode} + */ +export function isNotDistinct(left, right) { + return binaryOp('IS NOT DISTINCT FROM', left, right); +} + +/** + * Range inclusion (BETWEEN) operator. + * @param {import('../types.js').ExprValue} expr The expression to test. + * @param {import('../types.js').ExprValue[]} extent The range extent. + * @returns {BetweenOpNode} + */ +export function isBetween(expr, extent) { + return betweenOp(expr, extent, false); +} + +/** + * Range exclusion (NOT BETWEEN) operator. + * @param {import('../types.js').ExprValue} expr The expression to test. + * @param {import('../types.js').ExprValue[]} extent The range extent. + * @returns {NotBetweenOpNode} + */ +export function isNotBetween(expr, extent) { + return betweenOp(expr, extent, true); +} + +/** + * Set inclusion (IN) operator. + * @param {import('../types.js').ExprValue} expr The expression to test. + * @param {import('../types.js').ExprValue[]} values The included values. + * @returns {InOpNode} + */ +export function isIn(expr, values) { + return new InOpNode(asNode(expr), values.map(asNode)); +} diff --git a/packages/sql/src/functions/order-by.js b/packages/sql/src/functions/order-by.js new file mode 100644 index 00000000..d1679501 --- /dev/null +++ b/packages/sql/src/functions/order-by.js @@ -0,0 +1,24 @@ +import { OrderByNode } from '../ast/order-by.js'; +import { asNode } from '../util/ast.js'; + +/** + * Indicate ascending sort order for an expression. + * @param {import('../types.js').ExprValue} expr An expression to order by. + * @param {boolean | undefined} [nullsFirst] Flag indicating if null values + * should be sorted first. + * @returns {OrderByNode} + */ +export function asc(expr, nullsFirst) { + return new OrderByNode(asNode(expr), false, nullsFirst); +} + +/** + * Indicate descending sort order for an expression. + * @param {import('../types.js').ExprValue} expr An expression to order by. + * @param {boolean | undefined} [nullsFirst] Flag indicating if null values + * should be sorted first. + * @returns {OrderByNode} + */ +export function desc(expr, nullsFirst) { + return new OrderByNode(asNode(expr), true, nullsFirst); +} diff --git a/packages/sql/src/functions/spatial.js b/packages/sql/src/functions/spatial.js new file mode 100644 index 00000000..337c8706 --- /dev/null +++ b/packages/sql/src/functions/spatial.js @@ -0,0 +1,56 @@ +import { FunctionNode } from '../ast/function.js'; +import { fn } from '../util/function.js'; + +/** + * Function that converts geometry data to GeoJSON format. + * @param {import('../types.js').ExprValue} expr The input expression. + * @returns {FunctionNode} + */ +export function geojson(expr) { + return fn('st_asgeojson', expr); +} + +/** + * Function that returns a spatial x position (using ST_X). + * @param {import('../types.js').ExprValue} expr The input expression. + * @returns {FunctionNode} + */ +export function x(expr) { + return fn('st_x', expr); +} + +/** + * Function that returns a spatial y position (using ST_Y). + * @param {import('../types.js').ExprValue} expr The input expression. + * @returns {FunctionNode} + */ +export function y(expr) { + return fn('st_y', expr); +} + +/** + * Function that returns the centroid point for geometry data. + * @param {import('../types.js').ExprValue} expr The input expression. + * @returns {FunctionNode} + */ +export function centroid(expr) { + return fn('st_centroid', expr); +} + +/** + * Function that returns the centroid x-coordinate for geometry data. + * @param {import('../types.js').ExprValue} expr The input expression. + * @returns {FunctionNode} + */ +export function centroidX(expr) { + return x(centroid(expr)); +} + +/** + * Function that returns yhe centroid y-coordinate for geometry data. + * @param {import('../types.js').ExprValue} expr The input expression. + * @returns {FunctionNode} + */ +export function centroidY(expr) { + return y(centroid(expr)); +} diff --git a/packages/sql/src/functions/sql-template-tag.js b/packages/sql/src/functions/sql-template-tag.js new file mode 100644 index 00000000..3b89827a --- /dev/null +++ b/packages/sql/src/functions/sql-template-tag.js @@ -0,0 +1,51 @@ +/* eslint-disable jsdoc/no-undefined-types */ +import { FragmentNode } from '../ast/fragment.js'; +import { isNode } from '../ast/node.js'; +import { ParamNode } from '../ast/param.js'; +import { VerbatimNode } from '../ast/verbatim.js'; +import { isParamLike, isString } from '../util/type-check.js'; +import { literal } from './literal.js'; + +/** + * @typedef {import('../ast/node.js').ExprNode + * | import('../types.js').ParamLike + * | string | number | boolean | Date + * } TemplateValue + */ + +/** + * Template tag function for SQL fragments. Interpolated values + * may be strings, other SQL expression objects (such as column + * references), primitive values, or dynamic parameters. + * @param {TemplateStringsArray} strings Template string constants. + * @param {...TemplateValue} exprs Template expression values. + */ +export function sql(strings, ...exprs) { + return new FragmentNode(parseSQL(strings, exprs)); +} + +function parseSQL(strings, exprs) { + const spans = [strings[0]]; + const n = exprs.length; + for (let i = 0, k = 0; i < n;) { + const e = exprs[i]; + if (isNode(e)) { + spans[++k] = e; + } else if (isParamLike(e)) { + spans[++k] = new ParamNode(e); + } else { + spans[k] += isString(e) ? e : literal(e); + } + const s = strings[++i]; + if (isNode(spans[k])) { + spans[++k] = s; + } else { + spans[k] += s; + } + } + + // remove empty strings and preserve verbatim content + return spans + .filter(s => s) + .map(s => isString(s) ? new VerbatimNode(s) : s); +} diff --git a/packages/sql/src/functions/string.js b/packages/sql/src/functions/string.js new file mode 100644 index 00000000..e6ce4eac --- /dev/null +++ b/packages/sql/src/functions/string.js @@ -0,0 +1,82 @@ +import { FunctionNode } from '../ast/function.js'; +import { asLiteral } from '../util/ast.js'; +import { argsList, fn } from '../util/function.js'; + +function strFn(name, expr, ...args) { + return fn(name, expr, ...(argsList(args).map(asLiteral))); +} + +/** + * Function that returns true if a string contains a regexp pattern, + * false otherwise. + * @param {import('../types.js').ExprValue} string The string match against. + * @param {import('../types.js').StringValue} pattern The regular expression pattern to match. + * @param {import('../types.js').StringValue} [options] Regular expression options: + * 'c': case-sensitive matching + * 'i': case-insensitive matching + * 'l': match literals instead of regular expression tokens + * 'm', 'n', 'p': newline sensitive matching + * 'g': global replace, only available for regexp_replace + * 's': non-newline sensitive matching + * @returns {FunctionNode} + */ +export function regexp_matches(string, pattern, options) { + return strFn('regexp_matches', string, pattern, options); +} + +/** + * Function that returns true if search_string is found within string. + * @param {import('../types.js').ExprValue} string The string to match against. + * @param {import('../types.js').StringValue} search_string The substring to search for. + * @returns {FunctionNode} + */ +export function contains(string, search_string) { + return strFn('contains', string, search_string); +} + +/** + * Function that returns true if string begins with search_string. + * @param {import('../types.js').ExprValue} string The string to match against. + * @param {import('../types.js').StringValue} search_string The substring to search for. + * @returns {FunctionNode} + */ +export function prefix(string, search_string) { + return strFn('starts_with', string, search_string); +} + +/** + * Function that returns true if string ends with search_string. + * @param {import('../types.js').ExprValue} string The string to match against. + * @param {import('../types.js').StringValue} search_string The substring to search for. + * @returns {FunctionNode} + */ +export function suffix(string, search_string) { + return strFn('ends_with', string, search_string); +} + +/** + * Function that converts string to lower case. + * @param {import('../types.js').ExprValue} string The string to convert. + * @returns {FunctionNode} + */ +export function lower(string) { + return strFn('lower', string); +} + +/** + * Function that converts string to upper case. + * @param {import('../types.js').ExprValue} string The string to convert. + * @returns {FunctionNode} + */ +export function upper(string) { + return strFn('upper', string); +} + +/** + * Function that returns the number of characters in string. + * @param {import('../types.js').ExprValue} value The string to measure. + * @returns {FunctionNode} + */ +export function length(value) { + return strFn('length', value); +} diff --git a/packages/sql/src/functions/table-ref.js b/packages/sql/src/functions/table-ref.js new file mode 100644 index 00000000..8a33317e --- /dev/null +++ b/packages/sql/src/functions/table-ref.js @@ -0,0 +1,14 @@ +import { TableRefNode } from '../ast/table-ref.js'; +import { exprList } from '../util/function.js'; + +/** + * Returns a named table or view reference. + * @param {...(string | string[])} ids Table and namespace identifiers. + * For example 'name' or ['schema', 'name']. + * @returns {TableRefNode | undefined} A table reference, or undefined + * if the input identifier list is empty. + */ +export function tableRef(...ids) { + const args = exprList(ids, String); + return args?.length ? new TableRefNode(args) : undefined; +} diff --git a/packages/sql/src/functions/window.js b/packages/sql/src/functions/window.js new file mode 100644 index 00000000..eb7fd5de --- /dev/null +++ b/packages/sql/src/functions/window.js @@ -0,0 +1,121 @@ +import { WindowNode } from '../ast/window.js'; +import { winFn } from '../util/function.js'; + +/** + * Create a window function that returns the number of the current row + * within the partition, counting from 1. + * @returns {WindowNode} + */ +export function row_number() { + return winFn('row_number'); +} + +/** + * Create a window function that returns the rank of the current row with gaps. + * This is the same as the row_number of its first peer. + * @returns {WindowNode} + */ +export function rank() { + return winFn('rank'); +} + +/** + * Create a window function that returns the rank of the current row without + * gaps. The function counts peer groups. + * @returns {WindowNode} + */ +export function dense_rank() { + return winFn('dense_rank'); +} + +/** + * Create a window function that returns the relative rank of the current row. + * Computed as (rank() - 1) / (total partition rows - 1). + * @returns {WindowNode} + */ +export function percent_rank() { + return winFn('percent_rank'); +} + +/** + * Create a window function that returns the cumulative distribution. Computed + * as (number of preceding or peer partition rows) / total partition rows. + * @returns {WindowNode} + */ +export function cume_dist() { + return winFn('cume_dist'); +} + +/** + * Create a window function that returns an integer ranging from 1 to the + * argument value, dividing the partition as equally as possible. + * @param {import('../types.js').NumberValue} num_buckets The number of + * quantile buckets. + * @returns {WindowNode} + */ +export function ntile(num_buckets) { + return winFn('ntile', num_buckets); +} + +/** + * Create a window function that returns the expression evaluated at the row + * that is offset rows before the current row within the partition. + * If there is no such row, instead return default (which must be of the same + * type as the expression). Both offset and default are evaluated with respect + * to the current row. If omitted, offset defaults to 1 and default to null. + * @param {import('../types.js').ExprValue} expr The expression to evaluate. + * @param {import('../types.js').NumberValue} [offset] The row offset + * (default `1`). + * @param {*} [defaultValue] The default value (default `null`). + * @returns {WindowNode} + */ +export function lag(expr, offset, defaultValue){ + return winFn('lag', expr, offset, defaultValue); +} + +/** + * Create a window function that returns the expression evaluated at the row + * that is offset rows after the current row within the partition. + * If there is no such row, instead return default (which must be of the same + * type as the expression). Both offset and default are evaluated with respect + * to the current row. If omitted, offset defaults to 1 and default to null. + * @param {import('../types.js').ExprValue} expr The expression to evaluate. + * @param {import('../types.js').NumberValue} [offset] The row offset + * (default `1`). + * @param {*} [defaultValue] The default value (default `null`). + * @returns {WindowNode} + */ +export function lead(expr, offset, defaultValue){ + return winFn('lead', expr, offset, defaultValue); +} + +/** + * Create a window function that returns the expression evaluated at the row + * that is the first row of the window frame. + * @param {import('../types.js').ExprValue} expr The expression to evaluate. + * @returns {WindowNode} + */ +export function first_value(expr) { + return winFn('first_value', expr); +} + +/** + * Create a window function that returns the expression evaluated at the row + * that is the last row of the window frame. + * @param {import('../types.js').ExprValue} expr The expression to evaluate. + * @returns {WindowNode} + */ +export function last_value(expr) { + return winFn('last_value', expr); +} + +/** + * Create a window function that returns the expression evaluated at the + * nth row of the window frame (counting from 1), or null if no such row. + * @param {import('../types.js').ExprValue} expr The expression to evaluate. + * @param {import('../types.js').NumberValue} nth The 1-based window frame index. + * @returns {WindowNode} + */ +export function nth_value(expr, nth) { + return winFn('nth_value', expr, nth); +} diff --git a/packages/sql/src/index-types.ts b/packages/sql/src/index-types.ts new file mode 100644 index 00000000..fd48ba7e --- /dev/null +++ b/packages/sql/src/index-types.ts @@ -0,0 +1,2 @@ +export * from './index.js'; +export * from './types.js'; diff --git a/packages/sql/src/index.js b/packages/sql/src/index.js index 2a3c5c61..59efeb19 100644 --- a/packages/sql/src/index.js +++ b/packages/sql/src/index.js @@ -1,157 +1,58 @@ -export { - Ref, - asColumn, - asRelation, - all, - column, - relation -} from './ref.js'; +export { AggregateNode } from './ast/aggregate.js'; +export { BetweenOpNode, NotBetweenOpNode } from './ast/between-op.js' +export { BinaryOpNode } from './ast/binary-op.js'; +export { CaseNode, WhenNode } from './ast/case.js'; +export { CastNode } from './ast/cast.js'; +export { ColumnRefNode, isColumnRef } from './ast/column-ref.js'; +export { FragmentNode } from './ast/fragment.js'; +export { FromClauseNode } from './ast/from.js'; +export { FunctionNode } from './ast/function.js'; +export { InOpNode } from './ast/in-op.js'; +export { IntervalNode } from './ast/interval.js'; +export { LiteralNode } from './ast/literal.js'; +export { AndNode, OrNode } from './ast/logical-op.js'; +export { SQLNode, ExprNode, isNode } from './ast/node.js'; +export { OrderByNode } from './ast/order-by.js'; +export { ParamNode } from './ast/param.js'; +export { DescribeQuery, Query, SelectQuery, SetOperation, isDescribeQuery, isQuery, isSelectQuery } from './ast/query.js'; +export { SampleClauseNode } from './ast/sample.js'; +export { SelectClauseNode } from './ast/select.js'; +export { TableRefNode, isTableRef } from './ast/table-ref.js'; +export { UnaryOpNode, UnaryPosftixOpNode } from './ast/unary-op.js'; +export { VerbatimNode } from './ast/verbatim.js'; +export { WindowClauseNode, WindowDefNode, WindowFrameNode, WindowFunctionNode, WindowNode } from './ast/window.js'; +export { WithClauseNode } from './ast/with.js'; + +export { argmax, argmin, arrayAgg, avg, corr, count, covariance, covarPop, entropy, first, kurtosis, mad, max, median, min, mode, last, product, quantile, regrAvgX, regrAvgY, regrCount, regrIntercept, regrR2, regrSXX, regrSXY, regrSYY, regrSlope, skewness, stddev, stddevPop, stringAgg, sum, variance, varPop } from './functions/aggregate.js'; +export { cond } from './functions/case.js'; +export { cast, float32, float64, int32 } from './functions/cast.js'; +export { column } from './functions/column.js'; +export { dateBin, dateMonth, dateMonthDay, dateDay, epoch_ms, interval } from './functions/datetime.js'; +export { literal } from './functions/literal.js'; +export { abs, ceil, exp, floor, greatest, isFinite, isInfinite, isNaN, least, ln, log, round, sign, sqrt, trunc } from './functions/numeric.js'; +export { and, or, not, isNull, isNotNull, bitNot, bitAnd, bitOr, bitLeft, bitRight, add, sub, mul, div, idiv, mod, pow, eq, neq, lt, gt, lte, gte, isDistinct, isNotDistinct, isBetween, isNotBetween, isIn } from './functions/operators.js'; +export { asc, desc } from './functions/order-by.js'; +export { geojson, x, y, centroid, centroidX, centroidY } from './functions/spatial.js'; +export { sql } from './functions/sql-template-tag.js'; +export { regexp_matches, contains, prefix, suffix, lower, upper, length } from './functions/string.js'; +export { cume_dist, dense_rank, first_value, lag, last_value, lead, nth_value, ntile, percent_rank, rank, row_number } from './functions/window.js'; + +export { rewrite } from './visit/rewrite.js'; +export { collectAggregates, collectColumns, collectParams, isAggregateExpression } from './visit/visitors.js'; +export { walk } from './visit/walk.js'; -export { - isSQLExpression, - isParamLike, - sql, - SQLExpression -} from './expression.js'; - -export { - desc -} from './desc.js'; - -export { - literal -} from './literal.js'; - -export { - and, - or, - not, - eq, - neq, - lt, - gt, - lte, - gte, - isBetween, - isNotBetween, - isDistinct, - isNotDistinct, - isIn, - isNull, - isNotNull -} from './operators.js'; - -export { - agg, - argmax, - argmin, - arrayAgg, - avg, - corr, - count, - covariance, - covarPop, - entropy, - first, - kurtosis, - mean, - mad, - max, - median, - min, - mode, - last, - product, - quantile, - regrAvgX, - regrAvgY, - regrCount, - regrIntercept, - regrR2, - regrSXX, - regrSXY, - regrSYY, - regrSlope, - skewness, - stddev, - stddevPop, - stringAgg, - sum, - variance, - varPop -} from './aggregates.js'; - -export { - cast, - castDouble, - castInteger -} from './cast.js'; - -export { - epoch_ms, - dateBin, - dateMonth, - dateMonthDay, - dateDay -} from './datetime.js'; - -export { - regexp_matches, - contains, - prefix, - suffix, - lower, - upper, - length, - isNaN, - isFinite, - isInfinite -} from './functions.js'; - -export { - centroid, - centroidX, - centroidY, - geojson, - x, - y -} from './spatial.js'; - -export { - row_number, - rank, - dense_rank, - percent_rank, - cume_dist, - ntile, - lag, - lead, - first_value, - last_value, - nth_value -} from './windows.js'; - -export { - Query, - isQuery, - isDescribeQuery -} from './Query.js'; +export { createTable, createSchema } from './load/create.js'; +export { loadExtension } from './load/extension.js'; +export { loadCSV, loadJSON, loadObjects, loadParquet, loadSpatial } from './load/load.js'; -export { - toSQL, - literalToSQL -} from './to-sql.js'; +export { bin1d } from './transforms/bin-1d.js'; +export { bin2d } from './transforms/bin-2d.js'; +export { binLinear1d } from './transforms/bin-linear-1d.js'; +export { binLinear2d } from './transforms/bin-linear-2d.js'; +export { lineDensity } from './transforms/line-density.js'; +export { m4 } from './transforms/m4.js'; +export { scaleTransform } from './transforms/scales.js'; -export { - scaleTransform -} from './scales.js'; +export { asLiteral, asNode, asTableRef, asVerbatim, over } from './util/ast.js'; +export { isParamLike } from './util/type-check.js'; -export { createTable, createSchema } from './load/create.js'; -export { loadExtension } from './load/extension.js'; -export { - loadCSV, - loadJSON, - loadObjects, - loadParquet, - loadSpatial -} from './load/load.js'; diff --git a/packages/sql/src/literal.js b/packages/sql/src/literal.js deleted file mode 100644 index 4a6ae61b..00000000 --- a/packages/sql/src/literal.js +++ /dev/null @@ -1,6 +0,0 @@ -import { literalToSQL } from './to-sql.js'; - -export const literal = value => ({ - value, - toString: () => literalToSQL(value) -}); diff --git a/packages/sql/src/load/sql-from.js b/packages/sql/src/load/sql-from.js index 8feecf00..020ae8be 100644 --- a/packages/sql/src/load/sql-from.js +++ b/packages/sql/src/load/sql-from.js @@ -1,11 +1,12 @@ -import { literalToSQL } from '../to-sql.js'; +import { asLiteral } from '../util/ast.js'; /** * Create a SQL query that embeds the given data for loading. - * @param {*} data The dataset - * @param {object} [options] Loading options - * @param {string[]|object} [options.columns] The columns to include - * @returns {string} SQL query string to load data + * @param {*} data The dataset as an array of objects. + * @param {object} [options] Loading options. + * @param {string[]|object} [options.columns] The columns to include. + * If not specified, the keys of the first data object are used. + * @returns {string} SQL query string to load data. */ export function sqlFrom(data, { columns = Object.keys(data?.[0] || {}) @@ -22,7 +23,7 @@ export function sqlFrom(data, { } const subq = []; for (const datum of data) { - const sel = keys.map(k => `${literalToSQL(datum[k])} AS "${columns[k]}"`); + const sel = keys.map(k => `${asLiteral(datum[k])} AS "${columns[k]}"`); subq.push(`(SELECT ${sel.join(', ')})`); } return subq.join(' UNION ALL '); diff --git a/packages/sql/src/operators.js b/packages/sql/src/operators.js deleted file mode 100644 index d981d525..00000000 --- a/packages/sql/src/operators.js +++ /dev/null @@ -1,59 +0,0 @@ -import { sql } from './expression.js'; -import { asColumn } from './ref.js'; - -function visit(callback) { - callback(this.op, this); - this.children?.forEach(v => v.visit(callback)); -} - -function logical(op, clauses) { - const children = clauses.filter(x => x != null).map(asColumn); - const strings = children.map((c, i) => i ? ` ${op} ` : ''); - if (children.length === 1) { - strings.push('') - } else if (children.length > 1) { - strings[0] = '('; - strings.push(')'); - } - return sql(strings, ...children).annotate({ op, children, visit }); -} - -export const and = (...clauses) => logical('AND', clauses.flat()); -export const or = (...clauses) => logical('OR', clauses.flat()); - -const unaryOp = op => a => sql`(${op} ${asColumn(a)})`.annotate({ op, a, visit }); - -export const not = unaryOp('NOT'); - -const unaryPostOp = op => a => sql`(${asColumn(a)} ${op})`.annotate({ op, a, visit }); - -export const isNull = unaryPostOp('IS NULL'); -export const isNotNull = unaryPostOp('IS NOT NULL'); - -const binaryOp = op => (a, b) => sql`(${asColumn(a)} ${op} ${asColumn(b)})`.annotate({ op, a, b, visit }); - -export const eq = binaryOp('='); -export const neq = binaryOp('<>'); -export const lt = binaryOp('<'); -export const gt = binaryOp('>'); -export const lte = binaryOp('<='); -export const gte = binaryOp('>='); -export const isDistinct = binaryOp('IS DISTINCT FROM'); -export const isNotDistinct = binaryOp('IS NOT DISTINCT FROM'); - -function rangeOp(op, a, range, exclusive) { - a = asColumn(a); - const prefix = op.startsWith('NOT ') ? 'NOT ' : ''; - const expr = !range ? sql`` - : exclusive ? sql`${prefix}(${range[0]} <= ${a} AND ${a} < ${range[1]})` - : sql`(${a} ${op} ${range[0]} AND ${range[1]})`; - return expr.annotate({ op, visit, field: a, range }); -} - -export const isBetween = (a, range, exclusive) => rangeOp('BETWEEN', a, range, exclusive); -export const isNotBetween = (a, range, exclusive) => rangeOp('NOT BETWEEN', a, range, exclusive); - -export const isIn = (field, values) => { - return sql`(${asColumn(field)} IN (${values.join(', ')}))` - .annotate({ op: 'IN', field, values }); -} diff --git a/packages/sql/src/ref.js b/packages/sql/src/ref.js deleted file mode 100644 index 616e4cf2..00000000 --- a/packages/sql/src/ref.js +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Class representing a table and/or column reference. - */ -export class Ref { - /** - * Create a new Ref instance. - * @param {string|Ref|null} table The table name. - * @param {string|null} [column] The column name. - */ - constructor(table, column) { - if (table) this.table = String(table); - if (column) this.column = column; - } - - /** - * Get the list of referenced columns. Either a single element array - * if column is non-null, otherwise an empty array. - */ - get columns() { - return this.column ? [this.column] : []; - } - - /** - * Generate a SQL string for this reference. - * @returns {string} The SQL string. - */ - toString() { - const { table, column } = this; - if (column) { - const col = column.startsWith('*') ? column : `"${column}"`; - return `${table ? `${quoteTableName(table)}.` : ''}${col}`; - } else { - return table ? quoteTableName(table) : 'NULL'; - } - } -} - -/** - * Quote a table name. For example, `foo.bar` becomes `"foo"."bar". - * @param {string} table the name of the table which may contain a database reference - * @returns The quoted table name. - */ -function quoteTableName(table) { - const pieces = table.split('.'); - return pieces.map(p => `"${p}"`).join('.'); -} - -/** - * Test is a reference refers to a given column name. - * @param {*} ref The reference to test. - * @param {string} name The column name to check for. - * @returns {boolean} True if ref is a Ref instance that refers to - * the given column name. False otherwise. - */ -export function isColumnRefFor(ref, name) { - return ref instanceof Ref && ref.column === name; -} - -/** - * Interpret a value, defaulting to a column reference. - * @param {*} value The value to interpret. If string-typed, - * a new column reference will be returned. - * @returns {*} A column reference or the input value. - */ -export function asColumn(value) { - return typeof value === 'string' ? column(value) : value; -} - -/** - * Interpret a value, defaulting to a table (relation) reference. - * @param {*} value The value to interpret. If string-typed, - * a new table (relation) reference will be returned. - * @returns {*} A table reference or the input value. - */ -export function asRelation(value) { - return typeof value === 'string' ? relation(value) : value; -} - -/** - * Create a table (relation) reference. - * @param {string} name The table (relation) name. - * @returns {Ref} The generated table reference. - */ -export function relation(name) { - return new Ref(name); -} - -/** - * Create a column reference. - * @param {string} table The table name (optional). - * @param {string} [column] The column name. - * @returns {Ref} The generated column reference. - */ -export function column(table, column = null) { - if (arguments.length === 1) { - column = table; - table = null; - } - return new Ref(table, column); -} - -/** - * Create a reference to all columns in a table (relation). - * @param {string} table The table name. - * @returns {Ref} The generated reference. - */ -export function all(table) { - return new Ref(table, '*'); -} diff --git a/packages/sql/src/repeat.js b/packages/sql/src/repeat.js deleted file mode 100644 index d015dd36..00000000 --- a/packages/sql/src/repeat.js +++ /dev/null @@ -1,3 +0,0 @@ -export function repeat(length, str) { - return Array.from({ length }, () => str); -} diff --git a/packages/sql/src/spatial.js b/packages/sql/src/spatial.js deleted file mode 100644 index 501b6005..00000000 --- a/packages/sql/src/spatial.js +++ /dev/null @@ -1,10 +0,0 @@ -import { functionCall } from './functions.js'; - -export const geojson = functionCall('ST_AsGeoJSON'); - -export const x = functionCall('ST_X'); -export const y = functionCall('ST_Y'); - -export const centroid = functionCall('ST_CENTROID'); -export const centroidX = geom => x(centroid(geom)); -export const centroidY = geom => y(centroid(geom)); diff --git a/packages/sql/src/to-sql.js b/packages/sql/src/to-sql.js deleted file mode 100644 index fd2d742f..00000000 --- a/packages/sql/src/to-sql.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Convert a value to a corresponding SQL string. - * Input string values are assumed to be column references, - * otherwise the logic of literalToSQL applies. - * @param {*} value The value to convert to SQL. - * @returns {string} A SQL string. - */ -export function toSQL(value) { - return typeof value === 'string' - ? `"${value}"` // strings as column refs - : literalToSQL(value); -} - -/** - * Convert a literal value to a corresponding SQL string. - * The values null, undefined, Infinity, NaN, and invalid - * dates are converted to SQL NULL values. - * UTC dates map to the SQL Date type, otherwise JavaScript - * date values map to the SQL Timestamp type. - * Values that are not JavaScript Date, RegExp, or primitive types - * are coerced to strings, relying on a defined toString method. - * @param {*} value The literal value. - * @returns {string} A SQL string. - */ -export function literalToSQL(value) { - switch (typeof value) { - case 'boolean': - return value ? 'TRUE' : 'FALSE'; - case 'string': - return `'${value.replace(`'`, `''`)}'`; - case 'number': - return Number.isFinite(value) ? String(value) : 'NULL'; - default: - if (value == null) { - return 'NULL'; - } else if (value instanceof Date) { - const ts = +value; - if (Number.isNaN(ts)) return 'NULL'; - const y = value.getUTCFullYear(); - const m = value.getUTCMonth(); - const d = value.getUTCDate(); - return ts === Date.UTC(y, m, d) - ? `MAKE_DATE(${y}, ${m+1}, ${d})` // utc date - : `EPOCH_MS(${ts})`; // timestamp - } else if (value instanceof RegExp) { - return `'${value.source}'`; - } else { - // otherwise rely on string coercion - return String(value); - } - } -} diff --git a/packages/sql/src/transforms/bin-1d.js b/packages/sql/src/transforms/bin-1d.js new file mode 100644 index 00000000..e3045b5a --- /dev/null +++ b/packages/sql/src/transforms/bin-1d.js @@ -0,0 +1,21 @@ +import { float64 } from '../functions/cast.js'; +import { mul, sub } from '../functions/operators.js'; + +/** + * Compute binned values over a one-dimensional extent. + * @param {import('../types.js').ExprValue} x The expression to bin. + * The expression must return numeric values. For example, to bin + * datetime values, the input expression might map them to numeric + * values such as milliseconds since the epoch. + * @param {number} lo The low value of the bin extent. + * @param {number} hi The high value of the bin extent. + * @param {number} bins The integer number of bins to use within the + * defined binning extent. + * @param {boolean} [reverse] Flag indicating if bins should be + * produced in reverse order from *hi* to *lo* (default `false`). + */ +export function bin1d(x, lo, hi, bins, reverse) { + const diff = reverse ? sub(hi, float64(x)) : sub(float64(x), lo); + const scale = hi === lo ? 0 : bins / (hi - lo); + return scale ? mul(diff, float64(scale)) : diff; +} diff --git a/packages/sql/src/transforms/bin-2d.js b/packages/sql/src/transforms/bin-2d.js new file mode 100644 index 00000000..6b40871a --- /dev/null +++ b/packages/sql/src/transforms/bin-2d.js @@ -0,0 +1,29 @@ +import { SelectQuery } from '../ast/query.js'; +import { int32 } from '../functions/cast.js'; +import { floor } from '../functions/numeric.js'; +import { add, mul } from '../functions/operators.js'; + +/** + * Perform aggregation over a binned 2D domain. This method takes expressions + * for the (non-truncated) x and y bin values; these expressions should be + * in units of grid indices, but can contain fractional components. The + * resulting query performs grouping and aggregation over the binned domain, + * and uses a 2D integer bin index of the form (xbin + num_xbins * ybin). + * @param {SelectQuery} q The input query. The FROM and WHERE clauses should + * be added to the query separately, either before or after this method. + * @param {import('../types.js').ExprValue} xp The x bin expression. + * @param {import('../types.js').ExprValue} yp The y bin expression. + * @param {Record} aggs Named + * aggregate expressions over bins. + * @param {number} xn The number of bins along the x dimension + * @param {string[]} groupby Group by expressions. + * @returns {SelectQuery} The input query, with binning expressions added. + */ +export function bin2d(q, xp, yp, aggs, xn, groupby) { + return q + .select({ + index: add(int32(floor(xp)), mul(int32(floor(yp)), xn)), + ...aggs + }) + .groupby('index', groupby); +} diff --git a/packages/sql/src/transforms/bin-linear-1d.js b/packages/sql/src/transforms/bin-linear-1d.js new file mode 100644 index 00000000..a4f08139 --- /dev/null +++ b/packages/sql/src/transforms/bin-linear-1d.js @@ -0,0 +1,26 @@ +import { Query } from '../ast/query.js'; +import { sum } from '../functions/aggregate.js'; +import { int32 } from '../functions/cast.js'; +import { floor } from '../functions/numeric.js'; +import { add, gt, mul, sub } from '../functions/operators.js'; + +/** + * Perform linear binning in one dimension. + * @param {import('../ast/query.js').SelectQuery} query The base query to bin. + * @param {import('../types.js').ExprValue} x The expression to bin. + * @param {import('../types.js').ExprValue} [weight] The expression to weight by. + * @returns {Query} + */ +export function binLinear1d(query, x, weight) { + const w = weight ? (x => mul(x, weight)) : (x => x); + const p0 = floor(x); + const p1 = add(p0, 1); + return Query + .from(Query.unionAll( + query.clone().select({ i: int32(p0), w: w(sub(p1, x)) }), + query.clone().select({ i: int32(p1), w: w(sub(x, p0)) }) + )) + .select({ index: 'i', density: sum('w') }) + .groupby('index') + .having(gt('density', 0)); +} diff --git a/packages/sql/src/transforms/bin-linear-2d.js b/packages/sql/src/transforms/bin-linear-2d.js new file mode 100644 index 00000000..63891c33 --- /dev/null +++ b/packages/sql/src/transforms/bin-linear-2d.js @@ -0,0 +1,71 @@ +import { Query, SelectQuery } from '../ast/query.js'; +import { sum } from '../functions/aggregate.js'; +import { int32 } from '../functions/cast.js'; +import { floor } from '../functions/numeric.js'; +import { add, mul, neq, sub } from '../functions/operators.js'; + +/** + * Identity function. + * @template T + * @param {T} x + * @returns {T} + */ +function identity(x) { + return x; +} + +/** + * Compute densities over a 2D domain using linear binning. The weight of + * each data point is linearly distributed over adjacent bins, providing + * a better base for subsequent kernel density estimation. This method takes + * expressions for the (non-truncated) x and y bin values; these expressions + * should be in units of grid indices, but can contain fractional components. + * @param {SelectQuery} q The input query. The FROM and WHERE clauses should + * be added to the query separately, before this method is invoked. + * @param {import('../types.js').ExprValue} xp The x grid bin expression + * @param {import('../types.js').ExprValue} yp The y grid bin expression + * @param {import('../types.js').ExprValue | undefined} weight Point weights. + * @param {number} xn The number of x grid bins. + * @param {string[]} groupby Group by expressions. + * @returns {SelectQuery} A linear binning query for bin `index` and + * aggregate `density` columns, in addition to any group by expressions. + */ +export function binLinear2d(q, xp, yp, weight, xn, groupby) { + + const w = weight ? x => mul(x, weight) : identity; + + /** + * @param {import('../types.js').ExprValue} i + * @param {import('../types.js').ExprValue} w + */ + const subq = (i, w) => q.clone().select({ xp, yp, i, w }); + /** + * @param {import('../types.js').ExprValue} x + * @param {import('../types.js').ExprValue} y + */ + const index = (x, y) => add(x, mul(y, xn)); + + const xu = int32(floor(xp)); + const yu = int32(floor(yp)); + const xv = add(xu, 1); + const yv = add(yu, 1); + const xpu = sub(xp, xu); + const xvp = sub(xv, xp); + const ypu = sub(yp, yu); + const yvp = sub(yv, yp); + + return Query + .from(Query.unionAll( + // grid[xu + yu * xn] += (xv - xp) * (yv - yp) * wi + subq(index(xu, yu), w(mul(xvp, yvp))), + // grid[xu + yv * xn] += (xv - xp) * (yp - yu) * wi + subq(index(xu, yv), w(mul(xvp, ypu))), + // grid[xv + yu * xn] += (xp - xu) * (yv - yp) * wi + subq(index(xv, yu), w(mul(xpu, yvp))), + // grid[xv + yv * xn] += (xp - xu) * (yp - yu) * wi + subq(index(xv, yv), w(mul(xpu, ypu))) + )) + .select({ index: 'i', density: sum('w') }, groupby) + .groupby('index', groupby) + .having(neq('density', 0)); +} diff --git a/packages/sql/src/transforms/line-density.js b/packages/sql/src/transforms/line-density.js new file mode 100644 index 00000000..13e00159 --- /dev/null +++ b/packages/sql/src/transforms/line-density.js @@ -0,0 +1,113 @@ +import { Query, SelectQuery } from '../ast/query.js'; +import { count, max, sum } from '../functions/aggregate.js'; +import { int32 } from '../functions/cast.js'; +import { abs, floor, greatest, round, sign } from '../functions/numeric.js'; +import { add, div, gt, isNull, lt, lte, mul, or, sub } from '../functions/operators.js'; +import { asc } from '../functions/order-by.js'; +import { sql } from '../functions/sql-template-tag.js'; +import { lead } from '../functions/window.js'; +import { over } from '../util/ast.js'; + +/** + * Compute line segment densities over a gridded 2D domain. The returned + * query uses multiple subqueries (CTEs) to identify line segment end point + * pairs, perform line rasterization in-database, normalize arc lengths, + * and then sum results for all line series to produce a density map. + * Based on Moritz and Fisher's work: https://arxiv.org/abs/1808.06019 + * @param {SelectQuery} q The base query over the data. + * @param {import('../types.js').ExprValue} x Bin expression for x dimension. + * Provides gridded x coordinates, potentially with a fractional component. + * @param {import('../types.js').ExprValue} y Bin expression for x dimension. + * Provides gridded y coordinates, potentially with a fractional component. + * @param {string[]} z Group by columns that segment data into individual line + * series. An empty array indicates there is only a single line series. + * @param {number} xn The number of grid bins for the x dimension. + * @param {number} yn The number of grid bins for the y dimension. + * @param {string[]} [groupby] Additional group by expressions. Separate + * line density maps are created for each of these groups. + * @param {boolean} [normalize=true] Flag toggling approximate arc-length + * normalization to improve accuracy and reduce artifacts (default `true`). + * @returns {SelectQuery} + */ +export function lineDensity( + q, x, y, z, xn, yn, + groupby = [], normalize = true +) { + // select x, y points binned to the grid + q.select({ + x: int32(floor(x)), + y: int32(floor(y)) + }); + + // select line segment end point pairs + // retain only segments within the grid region + const groups = groupby.concat(z); + const pairs = Query + .from(q) + .select(groups, { + x0: 'x', + y0: 'y', + dx: sub(lead('x').over('sw'), 'x'), + dy: sub(lead('y').over('sw'), 'y') + }) + .window({ + sw: over().partitionby(groups).orderby(asc('x')) + }) + .qualify([ + or(lt('x0', xn), lt(add('x0', 'dx'), xn)), + or(lt('y0', yn), lt(add('y0', 'dy'), yn)), + or(gt('x0', 0), gt(add('x0', 'dx'), 0)), + or(gt('y0', 0), gt(add('y0', 'dy'), 0)) + ]); + + // create indices to join against for rasterization + // generate the maximum number of indices needed + const num = Query + .select({ x: greatest(max(abs('dx')), max(abs('dy'))) }) + .from('pairs'); + const indices = Query.select({ + i: int32(sql`UNNEST(range((${num})))`) + }); + + // rasterize line segments + const raster = Query.unionAll( + Query + .select(groups, { + x: add('x0', 'i'), + y: add('y0', int32(round(div(mul('i', 'dy'), 'dx')))) + }) + .from('pairs', 'indices') + .where([lte(abs('dy'), abs('dx')), lt('i', abs('dx'))]), + Query + .select(groups, { + x: add('x0', int32(round(div(mul(mul(sign('dy'), 'i'), 'dx'), 'dy')))), + y: add('y0', mul(sign('dy'), 'i')) + }) + .from('pairs', 'indices') + .where([gt(abs('dy'), abs('dx')), lt('i', abs('dy'))]), + Query + .select(groups, { x: 'x0', y: 'y0' }) + .from('pairs') + .where(isNull('dx')) + ); + + // filter raster, normalize columns for each series + const points = Query + .from('raster') + .select(groups, 'x', 'y', + normalize + ? { w: div(1, count().partitionby(['x'].concat(groups))) } + : null + ) + .where([lte(0, 'x'), lt('x', xn), lte(0, 'y'), lt('y', yn)]); + + // sum normalized, rasterized series into output grids + return Query + .with({ pairs, indices, raster, points }) + .from('points') + .select(groupby, { + index: add('x', mul('y', int32(xn))), + density: normalize ? sum('w') : count() + }) + .groupby('index', groupby); +} diff --git a/packages/sql/src/transforms/m4.js b/packages/sql/src/transforms/m4.js new file mode 100644 index 00000000..939e34b3 --- /dev/null +++ b/packages/sql/src/transforms/m4.js @@ -0,0 +1,38 @@ +import { Query } from '../ast/query.js'; +import { argmax, argmin, max, min } from '../functions/aggregate.js'; +import { int32 } from '../functions/cast.js'; +import { floor } from '../functions/numeric.js'; + +/** + * M4 is an optimization for value-preserving time-series aggregation + * (https://www.vldb.org/pvldb/vol7/p797-jugel.pdf). This implementation uses + * an efficient version with a single scan and the aggregate functions + * argmin and argmax, following https://arxiv.org/pdf/2306.03714.pdf. + * This method can bin along either the *x* or *y* dimension, as determined + * by the caller-provided *bin* expression. + * @param {import('../types.js').FromExpr} input The base query or table. + * @param {import('../types.js').ExprValue} bin An expression that maps + * time-series values to fractional pixel positions. + * @param {string} x The x dimension column name. + * @param {string} y The y dimension column name. + * @param {import('../ast/node.js').ExprNode[]} [groups] Additional + * groupby columns, for example for faceted charts. + * @returns {Query} The resulting M4 query. + */ +export function m4(input, bin, x, y, groups = []) { + const pixel = int32(floor(bin)); + + const q = (sel) => Query + .from(input) + .select(sel) + .groupby(pixel, groups); + + return Query + .union( + q([{ [x]: min(x), [y]: argmin(y, x) }, ...groups]), + q([{ [x]: max(x), [y]: argmax(y, x) }, ...groups]), + q([{ [x]: argmin(x, y), [y]: min(y) }, ...groups]), + q([{ [x]: argmax(x, y), [y]: max(y) }, ...groups]) + ) + .orderby(groups, x); +} diff --git a/packages/sql/src/scales.js b/packages/sql/src/transforms/scales.js similarity index 54% rename from packages/sql/src/scales.js rename to packages/sql/src/transforms/scales.js index 52cca6d6..e494a536 100644 --- a/packages/sql/src/scales.js +++ b/packages/sql/src/transforms/scales.js @@ -1,6 +1,9 @@ -import { epoch_ms } from './datetime.js'; -import { sql } from './expression.js'; -import { asColumn } from './ref.js'; +import { LiteralNode } from '../ast/literal.js'; +import { epoch_ms } from '../functions/datetime.js'; +import { literal } from '../functions/literal.js'; +import { abs, exp, ln, log, sign, sqrt } from '../functions/numeric.js'; +import { add, div, mul, pow, sub } from '../functions/operators.js'; +import { asNode } from '../util/ast.js'; const identity = x => x; @@ -8,7 +11,7 @@ function scaleLinear() { return { apply: identity, invert: identity, - sqlApply: asColumn, + sqlApply: asNode, sqlInvert: identity }; } @@ -18,23 +21,23 @@ function scaleLog({ base = null } = {}) { return { apply: Math.log, invert: Math.exp, - sqlApply: c => sql`LN(${asColumn(c)})`, - sqlInvert: c => sql`EXP(${c})` + sqlApply: c => ln(c), + sqlInvert: c => exp(c) }; } else if (base === 10) { return { apply: Math.log10, invert: x => Math.pow(10, x), - sqlApply: c => sql`LOG(${asColumn(c)})`, - sqlInvert: c => sql`POW(10, ${c})` + sqlApply: c => log(c), + sqlInvert: c => pow(10, c) }; } else { const b = +base; return { apply: x => Math.log(x) / Math.log(b), invert: x => Math.pow(b, x), - sqlApply: c => sql`LN(${asColumn(c)}) / LN(${b})`, - sqlInvert: c => sql`POW(${b}, ${c})` + sqlApply: c => div(ln(c), ln(b)), + sqlInvert: c => pow(b, c) }; } } @@ -44,8 +47,8 @@ function scaleSymlog({ constant = 1 } = {}) { return { apply: x => Math.sign(x) * Math.log1p(Math.abs(x)), invert: x => Math.sign(x) * Math.exp(Math.abs(x) - _), - sqlApply: c => (c = asColumn(c), sql`SIGN(${c}) * LN(${_} + ABS(${c}))`), - sqlInvert: c => sql`SIGN(${c}) * (EXP(ABS(${c})) - ${_})` + sqlApply: c => (c = asNode(c), mul(sign(c), ln(add(_, abs(c))))), + sqlInvert: c => mul(sign(c), sub(exp(abs(c)), _)) }; } @@ -53,8 +56,8 @@ function scaleSqrt() { return { apply: x => Math.sign(x) * Math.sqrt(Math.abs(x)), invert: x => Math.sign(x) * x * x, - sqlApply: c => (c = asColumn(c), sql`SIGN(${c}) * SQRT(ABS(${c}))`), - sqlInvert: c => sql`SIGN(${c}) * (${c}) ** 2` + sqlApply: c => (c = asNode(c), mul(sign(c), sqrt(abs(c)))), + sqlInvert: c => mul(sign(c), pow(c, 2)) }; } @@ -63,8 +66,8 @@ function scalePow({ exponent = 1 } = {}) { return { apply: x => Math.sign(x) * Math.pow(Math.abs(x), e), invert: x => Math.sign(x) * Math.pow(Math.abs(x), 1/e), - sqlApply: c => (c = asColumn(c), sql`SIGN(${c}) * POW(ABS(${c}), ${e})`), - sqlInvert: c => sql`SIGN(${c}) * POW(ABS(${c}), 1/${e})` + sqlApply: c => (c = asNode(c), mul(sign(c), pow(abs(c), e))), + sqlInvert: c => mul(sign(c), pow(abs(c), div(1, e))) }; } @@ -72,7 +75,9 @@ function scaleTime() { return { apply: x => +x, invert: x => new Date(x), - sqlApply: c => c instanceof Date ? +c : epoch_ms(asColumn(c)), + sqlApply: c => c instanceof Date ? literal(+c) + : isDateLiteral(c) ? literal(+c.value) + : epoch_ms(c), sqlInvert: identity }; } @@ -92,3 +97,12 @@ export function scaleTransform(options) { const scale = scales[options.type]; return scale ? { ...options, ...scale(options) } : null; } + +/** + * Check if a value is a date-valued literal SQL AST node. + * @param {*} x The value to test. + * @returns {x is LiteralNode} + */ +function isDateLiteral(x) { + return x instanceof LiteralNode && x.value instanceof Date; +} diff --git a/packages/sql/src/types.ts b/packages/sql/src/types.ts new file mode 100644 index 00000000..3bcfdd7d --- /dev/null +++ b/packages/sql/src/types.ts @@ -0,0 +1,96 @@ +import { ColumnRefNode } from './ast/column-ref.js'; +import { ExprNode, SQLNode } from './ast/node.js'; +import { TableRefNode } from './ast/table-ref.js'; +import { Query } from './ast/query.js'; + +/** + * Interface representing a dynamic parameter value. + */ +export interface ParamLike { + /** The current parameter value. */ + value: any; + /** Add an event listener callback. */ + addEventListener(type: string, callback: EventCallback): void; + /** Remove an event listener callback. */ + removeEventListener(type: string, callback: EventCallback): void; +} + +/** + * Expression value input to SQL builder method. + */ +export type ExprValue = ExprNode | ParamLike | string | number | boolean | Date; + +/** + * Expression values that may be nested in arrays. + */ +export type ExprVarArgs = MaybeArray; + +/** + * String-typed expression value. + */ +export type StringValue = ExprNode | ParamLike | string; + +/** + * Number-typed expression value. + */ +export type NumberValue = ExprNode | ParamLike | number; + +/** + * Event listener callback function. + */ +export type EventCallback = (value: any) => Promise | undefined; + +/** + * SQL AST traversal visitor callback result. + * A falsy value (including `undefined`, `null`, `false`, and `0`) indicates + * that traversal should continue. + * A negative number values indicates that traversal should stop immediately. + * Any other truthy value indicates that traversal should not recurse on the + * current node, but should otherwise continue. + */ +export type VisitorResult = boolean | number | null | undefined | void; + +/** + * SQL AST traversal callback function. + */ +export type VisitorCallback = (node: SQLNode) => VisitorResult; + +/** Valid window function names. */ +export type WindowFunctionName = + | 'cume_dist' + | 'dense_rank' + | 'first_value' + | 'lag' + | 'last_value' + | 'lead' + | 'nth_value' + | 'ntile' + | 'percent_rank' + | 'rank_dense' + | 'rank' + | 'row_number'; + +export type MaybeArray = T | T[]; + +export type SelectEntry = + | string + | ColumnRefNode + | [string, ExprNode] + | Record; + +export type SelectExpr = MaybeArray; + +export type WithExpr = MaybeArray>; + +export type FromEntry = + | string + | TableRefNode + | SQLNode + | [string, SQLNode] + | Record; + +export type FromExpr = MaybeArray; + +export type FilterExpr = MaybeArray; +export type GroupByExpr = MaybeArray; +export type OrderByExpr = MaybeArray; diff --git a/packages/sql/src/util/ast.js b/packages/sql/src/util/ast.js new file mode 100644 index 00000000..4187e747 --- /dev/null +++ b/packages/sql/src/util/ast.js @@ -0,0 +1,96 @@ +import { ExprNode } from '../ast/node.js'; +import { ParamNode } from '../ast/param.js'; +import { TableRefNode } from '../ast/table-ref.js'; +import { WindowDefNode } from '../ast/window.js'; +import { column } from '../functions/column.js'; +import { literal, verbatim } from '../functions/literal.js'; +import { tableRef } from '../functions/table-ref.js'; +import { parseIdentifier } from './string.js'; +import { isArray, isParamLike, isString } from './type-check.js'; + +/** + * Interpret a value as a SQL AST node. String values are assumed to be + * column references. All other primitive values are interpreted as + * SQL literals. Dynamic parameters are interpreted as param AST nodes, + * while existing AST nodes are left as-is. + * @param {*} value The value to interpret as a SQL AST node. + * @returns {ExprNode} + */ +export function asNode(value) { + return isString(value) + ? parseColumnRef(value) + : asLiteral(value); +} + +/** + * Interpret a value as a verbatim SQL AST node. String values will be + * passed through to queries as verbatim text. All other primitive values + * are interpreted as SQL literals. Dynamic parameters are interpreted + * as param AST nodes, while existing AST nodes are left as-is. + * @param {*} value The value to interpret as a verbatim AST node. + * @returns {ExprNode} + */ +export function asVerbatim(value) { + return isString(value) + ? verbatim(value) + : asLiteral(value); +} + +/** + * Interpret a value as a literal AST node. All other primitive values + * are interpreted as SQL literals. Dynamic parameters are interpreted + * as param AST nodes, while existing AST nodes are left as-is. + * @param {*} value The value to interpret as a literal AST node. + * @returns {ExprNode} + */ +export function asLiteral(value) { + return value instanceof ExprNode ? value + : isParamLike(value) ? new ParamNode(value) + : literal(value); +} + +/** + * Interpret a value as a table reference AST node. String values are parsed + * assuming dot ('.') delimiters (as in `schema.table`). Array values are + * interpreted as pre-parsed name paths (as in `['schema', 'table']`). Any + * other values are left as-is. + * @param {string | string[] | TableRefNode} value The value to interpret as a + * table reference AST node. + * @returns {TableRefNode} + */ +export function asTableRef(value) { + return isString(value) ? parseTableRef(value) + : isArray(value) ? tableRef(value) + : value; +} + +/** + * Parse a string as a column reference, potentially with + * dot ('.') delimited table, schema, and database references. + * @param {string} ref The column reference string. + * @returns {import('../ast/column-ref.js').ColumnRefNode} + */ +export function parseColumnRef(ref) { + const ids = parseIdentifier(ref); + return column(ids.pop(), tableRef(ids)); +} + +/** + * Parse a string as a table reference, potentially with + * dot ('.') delimited schema and database references. + * @param {string} ref The table reference string. + * @returns {import('../ast/table-ref.js').TableRefNode} + */ +export function parseTableRef(ref) { + return tableRef(parseIdentifier(ref)); +} + +/** + * Create a new window definition node. The return value is an empty + * window definition. Use chained calls such as `partitionby` and `orderby` + * to specify the window settings. + * @returns {WindowDefNode} + */ +export function over() { + return new WindowDefNode(); +} diff --git a/packages/sql/src/util/function.js b/packages/sql/src/util/function.js new file mode 100644 index 00000000..899ebf8c --- /dev/null +++ b/packages/sql/src/util/function.js @@ -0,0 +1,78 @@ +import { AggregateNode } from '../ast/aggregate.js'; +import { FunctionNode } from '../ast/function.js'; +import { WindowFunctionNode, WindowNode } from '../ast/window.js'; +import { asNode } from './ast.js'; + +/** + * Test if an AST node is a specific function call. + * @param {import('../ast/node.js').SQLNode} node The SQL AST node to test. + * @param {string} name The function name. + * @returns {node is FunctionNode} + */ +export function isFunctionCall(node, name) { + return node instanceof FunctionNode && node.name === name; +} + +/** + * Create a new function call AST node. + * @param {string} name The function name. + * @param {...any} args The function arguments. + * @returns {FunctionNode} + */ +export function fn(name, ...args) { + return new FunctionNode(name, argsList(args).map(asNode)); +} + +/** + * Create a new aggregate function AST node. + * @param {string} name The function name. + * @param {...any} args The function arguments. + * @returns {AggregateNode} + */ +export function aggFn(name, ...args) { + return new AggregateNode(name, argsList(args).map(asNode)); +} + +/** + * Create a new window AST node. The output node has an empty window + * definition. Use chained calls such as `partitionby` and `orderby` + * to specify the window settings. + * @param {import('../types.js').WindowFunctionName} name The function name. + * @param {...any} args The function arguments. + * @returns {WindowNode} + */ +export function winFn(name, ...args) { + return new WindowNode( + new WindowFunctionNode(name, argsList(args).map(asNode)) + ); +} + +/** + * Process a list of expression inputs. Nested arrays are flattened, + * null results are removed, and each input is cast (as needed) to + * be a proper SQL AST node. By default, strings are assumed to be + * column names, while other primitive values map to SQL literals. + * Use an alternative *cast* function to change this behavior. + * @param {any[]} list The list of expression inputs. + * @param {function} [cast] A function that casts an input value + * to a desired type. By default, `asNode` is used to coerce + * inputs to AST nodes as needed. + * @returns {ReturnType[]} + */ +export function exprList(list, cast = asNode) { + return list.flat().filter(x => x != null).map(x => cast(x)); +} + +/** + * Process a list of function arguments, stripping any undefined + * values from the end of the list. + * @template T + * @param {T[]} list The input function arguments. + * @returns {T[]} The prepared argument list. + */ +export function argsList(list) { + const n = list.length; + let i = n; + for (; i > 0 && list[i - 1] === undefined; --i); + return i < n ? list.slice(0, i) : list; +} diff --git a/packages/sql/src/util/string.js b/packages/sql/src/util/string.js new file mode 100644 index 00000000..6c869665 --- /dev/null +++ b/packages/sql/src/util/string.js @@ -0,0 +1,16 @@ +// TODO: handle case where identifiers are already quoted? +export function parseIdentifier(id) { + return id.split('.'); +} + +export function quoteIdentifier(value) { + return `"${value}"`; +} + +export function unquote(s) { + return s && isDoubleQuoted(s) ? s.slice(1, -1) : s; +} + +function isDoubleQuoted(s) { + return s[0] === '"' && s[s.length-1] === '"'; +} diff --git a/packages/sql/src/util/type-check.js b/packages/sql/src/util/type-check.js new file mode 100644 index 00000000..c26200e3 --- /dev/null +++ b/packages/sql/src/util/type-check.js @@ -0,0 +1,29 @@ +/** + * Check if a value is a string. + * @param {*} value The value to check. + * @returns {value is string} + */ +export function isString(value) { + return typeof value === 'string'; +} + +/** + * Check if a value is an array. + * @param {*} value The value to check. + * @returns {value is Array} + */ +export function isArray(value) { + return Array.isArray(value); +} + +/** + * Check if a value is a dynamic parameter. + * @param {*} value The value to check. + * @returns {value is import('../types.js').ParamLike} + */ +export function isParamLike(value) { + return value + && typeof value.addEventListener === 'function' + && value.dynamic !== false + && 'value' in value; +} diff --git a/packages/sql/src/visit/recurse.js b/packages/sql/src/visit/recurse.js new file mode 100644 index 00000000..000fbeab --- /dev/null +++ b/packages/sql/src/visit/recurse.js @@ -0,0 +1,55 @@ +import { + AGGREGATE, + BETWEEN_OPERATOR, + BINARY_OPERATOR, + CASE, + CAST, + COLUMN_REF, + DESCRIBE_QUERY, + EXPRESSION, + FRAGMENT, + FROM_CLAUSE, + FUNCTION, + IN_OPERATOR, + LOGICAL_OPERATOR, + NOT_BETWEEN_OPERATOR, + ORDER_BY, + PARAM, + SELECT_CLAUSE, + SELECT_QUERY, + SET_OPERATION, + UNARY_OPERATOR, + WHEN, + WINDOW, + WINDOW_CLAUSE, + WINDOW_DEF, + WINDOW_FRAME +} from '../constants.js'; + +export const recurse = { + [AGGREGATE]: ['args', 'filter'], + [BETWEEN_OPERATOR]: ['expr', 'extent'], + [BINARY_OPERATOR]: ['left', 'right'], + [CASE]: ['expr', '_when', '_else'], + [CAST]: ['expr'], + [COLUMN_REF]: ['table'], + [DESCRIBE_QUERY]: ['query'], + [EXPRESSION]: ['node'], + [FRAGMENT]: ['spans'], + [FROM_CLAUSE]: ['expr'], + [FUNCTION]: ['args'], + [IN_OPERATOR]: ['expr', 'values'], + [LOGICAL_OPERATOR]: ['clauses'], + [NOT_BETWEEN_OPERATOR]: ['expr', 'extent'], + [ORDER_BY]: ['expr'], + [PARAM]: ['value'], + [SELECT_CLAUSE]: ['expr'], + [SELECT_QUERY]: ['_with', '_select', '_from', '_where', '_sample', '_groupby', '_having', '_window', '_qualify', '_orderby'], + [SET_OPERATION]: ['subqueries', '_orderby'], + [UNARY_OPERATOR]: ['expr'], + [WHEN]: ['when', 'then'], + [WINDOW]: ['func', 'def'], + [WINDOW_CLAUSE]: ['def'], + [WINDOW_DEF]: ['partition', 'order', 'frame'], + [WINDOW_FRAME]: ['extent'] +}; diff --git a/packages/sql/src/visit/rewrite.js b/packages/sql/src/visit/rewrite.js new file mode 100644 index 00000000..b3319659 --- /dev/null +++ b/packages/sql/src/visit/rewrite.js @@ -0,0 +1,32 @@ +import { ExprNode, isNode } from '../ast/node.js'; +import { recurse } from './recurse.js'; + +/** + * Rewrite a SQL expression, based on a map of nodes to replace. + * This method updates the expression in place. + * @param {ExprNode} node The root AST node of the expression. + * @param {Map} map The rewrite map. + * When encountered, key nodes are replaced by value nodes. + * @returns {ExprNode} + */ +export function rewrite(node, map) { + if (map.has(node)) { + return map.get(node); + } else if (isNode(node)) { + const props = recurse[node.type]; + const n = props?.length ?? 0; + for (let i = 0; i < n; ++i) { + const prop = props[i]; + const child = node[prop]; + if (Array.isArray(child)) { + const m = child.length; + for (let j = 0; j < m; ++j) { + child[j] = rewrite(child[j], map); + } + } else if (child) { + node[prop] = rewrite(child, map); + } + } + } + return node; +} diff --git a/packages/sql/src/visit/visitors.js b/packages/sql/src/visit/visitors.js new file mode 100644 index 00000000..e5d09f9e --- /dev/null +++ b/packages/sql/src/visit/visitors.js @@ -0,0 +1,103 @@ +import { AGGREGATE, COLUMN_REF, FRAGMENT, PARAM, VERBATIM, WINDOW } from '../constants.js'; +import { aggregateNames, AggregateNode } from '../ast/aggregate.js'; +import { ColumnRefNode } from '../ast/column-ref.js'; +import { SQLNode } from '../ast/node.js'; +import { walk } from './walk.js'; + +const aggrRegExp = new RegExp(`^(${aggregateNames.join('|')})`); +const funcRegExp = /(\\'|\\"|"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\$\w+|\w+\()/g; + +function hasVerbatimAggregate(s) { + return s + .split(funcRegExp) + .some(tok => tok.endsWith('(') && aggrRegExp.test(tok)); +} + +/** + * Indicate if the input AST contains an aggregate expression. + * The string content of verbatim nodes is analyzed to try to identify + * unparsed aggregate functions calls within SQL strings. + * @param {SQLNode} root The root of the AST to search. + * @returns {number} Return 0 if no aggregate functions are found. + * Sets bit 1 if an AggregateFunction instance is found. + * Sets bit 2 if an aggregate embedded in verbatim text is found. + */ +export function isAggregateExpression(root) { + let agg = 0; + walk(root, (node) => { + switch (node.type) { + case WINDOW: + return -1; // aggs can't include windows + case AGGREGATE: + agg |= 1; + return -1; + case FRAGMENT: + case VERBATIM: { + let s = `${node}`.toLowerCase(); + + // strip away scalar subquery content + const sub = s.indexOf('(select '); + if (sub >= 0) s = s.slice(0, sub); + + // exit if expression includes windowing + if (s.includes(') over ')) return -1; + if (hasVerbatimAggregate(s)) { + agg |= 2; + return -1; + } + return 1; // don't recurse + } + } + }); + return agg; +} + +/** + * Collect all aggregate function nodes. + * @param {SQLNode} root The root of the AST to search. + * @returns {AggregateNode[]} + */ +export function collectAggregates(root) { + const aggs = new Set(); + walk(root, (node) => { + if (node.type === AGGREGATE) { + aggs.add(node); + } + }); + return Array.from(aggs); +} + +/** + * Collect all unique column references. + * Multiple references to the same column are de-duplicated, even if + * they are not object-equal node instances. + * @param {SQLNode} root The root of the AST to search. + * @returns {ColumnRefNode[]} + */ +export function collectColumns(root) { + const cols = {}; + walk(root, (node) => { + if (node.type === COLUMN_REF) { + cols[node] = node; // key on string-coerced node + } + }); + return Object.values(cols); +} + +/** + * Collect all unique dynamic parameter instances. + * @param {SQLNode} root The root of the AST to search. + * @returns {import('../types.js').ParamLike[]} + */ +export function collectParams(root) { + const params = new Set; + walk(root, (node) => { + if (node.type === PARAM) { + params.add( + /** @type {import('../ast/param.js').ParamNode} */ + (node).param + ); + } + }); + return Array.from(params); +} diff --git a/packages/sql/src/visit/walk.js b/packages/sql/src/visit/walk.js new file mode 100644 index 00000000..9fb62a3d --- /dev/null +++ b/packages/sql/src/visit/walk.js @@ -0,0 +1,30 @@ +import { isNode } from '../ast/node.js'; +import { recurse } from './recurse.js'; + +/** + * Perform a traversal of a SQL expression AST. + * @param {import('../ast/node.js').SQLNode} node Root node for AST traversal. + * @param {import('../types.js').VisitorCallback} visit Visitor callback function. + * @return {import('../types.js').VisitorResult} + */ +export function walk(node, visit) { + if (!isNode(node)) return; + const result = visit(node); + if (result) return result; + + const props = recurse[node.type]; + const n = props?.length ?? 0; + for (let i = 0; i < n; ++i) { + const value = node[props[i]]; + if (Array.isArray(value)) { + const m = value.length; + for (let j = 0; j < m; ++j) { + if (value[j] && +walk(value[j], visit) < 0) { + return result; + } + } + } else if (value && +walk(value, visit) < 0) { + return -1; + } + } +} diff --git a/packages/sql/src/windows.js b/packages/sql/src/windows.js deleted file mode 100644 index c1a0ed07..00000000 --- a/packages/sql/src/windows.js +++ /dev/null @@ -1,239 +0,0 @@ -import { SQLExpression, isParamLike, sql } from './expression.js'; -import { functionCall } from './functions.js'; -import { asColumn } from './ref.js'; -import { repeat } from './repeat.js'; - -/** - * Base class for individual window functions. - * Most callers should use a dedicated window function - * rather than instantiate this class. - */ -export class WindowFunction extends SQLExpression { - /** - * Create a new WindowFunction instance. - * @param {string} op The window operation indicator. - * @param {*} func The window function expression. - * @param {*} [type] The SQL data type to cast to. - * @param {string} [name] The window definition name. - * @param {*} [group] Grouping (partition by) expressions. - * @param {*} [order] Sorting (order by) expressions. - * @param {*} [frame] The window frame definition. - */ - constructor(op, func, type, name, group = '', order = '', frame = '') { - // build and parse expression - let expr; - const noWindowParams = !(group || order || frame); - if (name && noWindowParams) { - expr = name ? sql`${func} OVER "${name}"` : sql`${func} OVER ()`; - } else { - const s1 = group && order ? ' ' : ''; - const s2 = (group || order) && frame ? ' ' : ''; - expr = sql`${func} OVER (${name ? `"${name}" ` : ''}${group}${s1}${order}${s2}${frame})`; - } - if (type) { - expr = sql`(${expr})::${type}`; - } - const { _expr, _deps } = expr; - super(_expr, _deps); - this.window = op; - this.func = func; - this.type = type; - this.name = name; - this.group = group; - this.order = order; - this.frame = frame; - } - - get basis() { - return this.column; - } - - get label() { - const { func } = this; - return func.label ?? func.toString(); - } - - /** - * Return an updated window function over a named window definition. - * @param {string} name The window definition name. - * @returns {WindowFunction} A new window function. - */ - over(name) { - const { window: op, func, type, group, order, frame } = this; - return new WindowFunction(op, func, type, name, group, order, frame); - } - - /** - * Return an updated window function with the given partitioning. - * @param {*} expr The grouping (partition by) criteria for the window function. - * @returns {WindowFunction} A new window function. - */ - partitionby(...expr) { - const exprs = expr.flat().filter(x => x).map(asColumn); - const group = sql( - ['PARTITION BY ', repeat(exprs.length - 1, ', '), ''], - ...exprs - ); - const { window: op, func, type, name, order, frame } = this; - return new WindowFunction(op, func, type, name, group, order, frame); - } - - /** - * Return an updated window function with the given ordering. - * @param {*} expr The sorting (order by) criteria for the window function. - * @returns {WindowFunction} A new window function. - */ - orderby(...expr) { - const exprs = expr.flat().filter(x => x).map(asColumn); - const order = sql( - ['ORDER BY ', repeat(exprs.length - 1, ', '), ''], - ...exprs - ); - const { window: op, func, type, name, group, frame } = this; - return new WindowFunction(op, func, type, name, group, order, frame); - } - - /** - * Return an updated window function with the given rows frame. - * @param {(number|null)[] | import('./expression.js').ParamLike} expr The row-based window frame. - * @returns {WindowFunction} A new window function. - */ - rows(expr) { - const frame = windowFrame('ROWS', expr); - const { window: op, func, type, name, group, order } = this; - return new WindowFunction(op, func, type, name, group, order, frame); - } - - /** - * Return an updated window function with the given range frame. - * @param {(number|null)[] | import('./expression.js').ParamLike} expr The range-based window frame. - * @returns {WindowFunction} A new window function. - */ - range(expr) { - const frame = windowFrame('RANGE', expr); - const { window: op, func, type, name, group, order } = this; - return new WindowFunction(op, func, type, name, group, order, frame); - } -} - -function windowFrame(type, frame) { - if (isParamLike(frame)) { - const expr = sql`${frame}`; - expr.toString = () => `${type} ${frameToSQL(frame.value)}`; - return expr; - } - return `${type} ${frameToSQL(frame)}`; -} - -function frameToSQL(frame) { - const [prev, next] = frame; - const a = prev === 0 ? 'CURRENT ROW' - : Number.isFinite(prev) ? `${Math.abs(prev)} PRECEDING` - : 'UNBOUNDED PRECEDING'; - const b = next === 0 ? 'CURRENT ROW' - : Number.isFinite(next) ? `${Math.abs(next)} FOLLOWING` - : 'UNBOUNDED FOLLOWING'; - return `BETWEEN ${a} AND ${b}`; -} - -export function winf(op, type) { - return (...values) => { - const func = functionCall(op)(...values); - return new WindowFunction(op, func, type); - }; -} - -/** - * Create a window function that returns the number of the current row - * within the partition, counting from 1. - * @returns {WindowFunction} The generated window function. - */ -export const row_number = winf('ROW_NUMBER', 'INTEGER'); - -/** - * Create a window function that returns the rank of the current row with gaps. - * This is the same as the row_number of its first peer. - * @returns {WindowFunction} The generated window function. - */ -export const rank = winf('RANK', 'INTEGER'); - -/** - * Create a window function that returns the rank of the current row without gaps, - * The function counts peer groups. - * @returns {WindowFunction} The generated window function. - */ -export const dense_rank = winf('DENSE_RANK', 'INTEGER'); - -/** - * Create a window function that returns the relative rank of the current row. - * (rank() - 1) / (total partition rows - 1). - * @returns {WindowFunction} The generated window function. - */ -export const percent_rank = winf('PERCENT_RANK'); - -/** - * Create a window function that returns the cumulative distribution. - * (number of preceding or peer partition rows) / total partition rows. - * @returns {WindowFunction} The generated window function. - */ -export const cume_dist = winf('CUME_DIST'); - -/** - * Create a window function that r an integer ranging from 1 to the argument - * value, dividing the partition as equally as possible. - * @param {number|SQLExpression} num_buckets The number of quantile buckets. - * @returns {WindowFunction} The generated window function. - */ -export const ntile = winf('NTILE'); - -/** - * Create a window function that returns the expression evaluated at the row - * that is offset rows before the current row within the partition. - * If there is no such row, instead return default (which must be of the same - * type as the expression). Both offset and default are evaluated with respect - * to the current row. If omitted, offset defaults to 1 and default to null. - * @param {string|SQLExpression} expr The expression to evaluate. - * @param {number|SQLExpression} offset The row offset. - * @param {*} default The default value. - * @returns {WindowFunction} The generated window function. - */ -export const lag = winf('LAG'); - -/** - * Create a window function that returns the expression evaluated at the row - * that is offset rows after the current row within the partition. - * If there is no such row, instead return default (which must be of the same - * type as the expression). Both offset and default are evaluated with respect - * to the current row. If omitted, offset defaults to 1 and default to null. - * @param {string|SQLExpression} expr The expression to evaluate. - * @param {number|SQLExpression} offset The row offset. - * @param {*} default The default value. - * @returns {WindowFunction} The generated window function. - */ -export const lead = winf('LEAD'); - -/** - * Create a window function that returns the expression evaluated at the row - * that is the first row of the window frame. - * @param {string|SQLExpression} expr The expression to evaluate. - * @returns {WindowFunction} The generated window function. - */ -export const first_value = winf('FIRST_VALUE'); - -/** - * Create a window function that returns the expression evaluated at the row - * that is the last row of the window frame. - * @param {string|SQLExpression} expr The expression to evaluate. - * @returns {WindowFunction} The generated window function. - */ - -export const last_value = winf('LAST_VALUE'); - -/** - * Create a window function that returns the expression evaluated at the - * nth row of the window frame (counting from 1), or null if no such row. - * @param {string|SQLExpression} expr The expression to evaluate. - * @param {number|SQLExpression} nth The 1-based window frame index. - * @returns {WindowFunction} The generated window function. - */ -export const nth_value = winf('NTH_VALUE'); diff --git a/packages/sql/test/aggregate.test.js b/packages/sql/test/aggregate.test.js index 378d137c..5395d80d 100644 --- a/packages/sql/test/aggregate.test.js +++ b/packages/sql/test/aggregate.test.js @@ -1,166 +1,165 @@ import { expect, describe, it } from 'vitest'; -import { stubParam } from './stub-param.js'; -import { - agg, column, isSQLExpression, isParamLike, - argmax, argmin, arrayAgg, avg, corr, count, covarPop, entropy, first, - kurtosis, mean, mad, max, median, min, mode, last, product, quantile, - regrAvgX, regrAvgY, regrCount, regrIntercept, regrR2, regrSXX, regrSXY, - regrSYY, regrSlope, skewness, stddev, stddevPop, stringAgg, sum, - variance, varPop, sql -} from '../src/index.js'; - -describe('agg template tag', () => { - it('creates aggregate SQL expressions', () => { - const expr = agg`SUM(${column('foo')})`; - expect(isSQLExpression(expr)).toBe(true); - expect(isParamLike(expr)).toBe(false); - expect(String(expr)).toBe('SUM("foo")'); - expect(expr.column).toBe('foo'); - expect(expr.columns).toEqual(['foo']); - }); - - it('creates parameterized aggregate SQL expressions', () => { - const col = stubParam(column('bar')); - expect(isParamLike(col)).toBe(true); - - const expr = agg`SUM(${col})`; - expect(isSQLExpression(expr)).toBe(true); - expect(isParamLike(expr)).toBe(true); - expect(String(expr)).toBe('SUM("bar")'); - expect(expr.column).toBe('bar'); - expect(expr.columns).toEqual(['bar']); - - expr.addEventListener('value', value => { - expect(isSQLExpression(value)).toBe(true); - expect(String(expr)).toBe(`${value}`); - }); - col.update(column('baz')); - expect(String(expr)).toBe('SUM("baz")'); - expect(expr.column).toBe('baz'); - expect(expr.columns).toEqual(['baz']); - }); -}); +import { columns } from './util/columns.js'; +import { argmax, argmin, arrayAgg, avg, column, corr, count, covariance, covarPop, entropy, first, gt, kurtosis, last, mad, max, median, min, mode, product, quantile, regrAvgX, regrAvgY, regrCount, regrIntercept, regrR2, regrSlope, regrSXX, regrSXY, regrSYY, skewness, stddev, stddevPop, stringAgg, sum, variance, varPop } from '../src/index.js'; describe('Aggregate functions', () => { - it('expose metadata', () => { - const expr = sum(column('foo')); - expect(expr.aggregate).toBe('SUM'); - expect(expr.column).toBe('foo'); - expect(expr.columns).toEqual(['foo']); + it('include accessible metadata', () => { + const expr = sum('foo'); + expect(expr.name).toBe('sum'); + expect(columns(expr)).toStrictEqual(['foo']); }); + it('support distinct', () => { - const expr = count(column('foo')).distinct(); - expect(expr.aggregate).toBe('COUNT'); + const expr = count('foo').distinct(); + expect(expr.name).toBe('count'); expect(expr.isDistinct).toBe(true); - expect(expr.type).toBe('INTEGER'); - expect(String(expr)).toBe('COUNT(DISTINCT "foo")::INTEGER'); + expect(String(expr)).toBe('count(DISTINCT "foo")'); }); + it('support filter', () => { const foo = column('foo'); - const expr = avg(foo).where(sql`${foo} > 5`); - expect(String(expr)).toBe('AVG("foo") FILTER (WHERE "foo" > 5)'); + expect(String(avg(foo).where(gt(foo, 5)))) + .toBe('avg("foo") FILTER (WHERE ("foo" > 5))'); + }); + + it('include arg_max', () => { + expect(String(argmax('foo', 'bar'))).toBe('arg_max("foo", "bar")'); }); - it('include ARG_MAX', () => { - expect(String(argmax('foo', 'bar'))).toBe('ARG_MAX("foo", "bar")'); + + it('include arg_min', () => { + expect(String(argmin('foo', 'bar'))).toBe('arg_min("foo", "bar")'); }); - it('include ARG_MIN', () => { - expect(String(argmin('foo', 'bar'))).toBe('ARG_MIN("foo", "bar")'); + + it('include array_agg', () => { + expect(String(arrayAgg('foo'))).toBe('array_agg("foo")'); }); - it('include ARRAY_AGG', () => { - expect(String(arrayAgg('foo'))).toBe('ARRAY_AGG("foo")'); + + it('include avg', () => { + expect(String(avg('foo'))).toBe('avg("foo")'); }); - it('include AVG', () => { - expect(String(avg('foo'))).toBe('AVG("foo")'); - expect(String(mean('foo'))).toBe('AVG("foo")'); + + it('include corr', () => { + expect(String(corr('foo', 'bar'))).toBe('corr("foo", "bar")'); }); - it('include CORR', () => { - expect(String(corr('foo', 'bar'))).toBe('CORR("foo", "bar")'); + + it('include count', () => { + expect(String(count())).toBe('count(*)'); + expect(String(count('foo'))).toBe('count("foo")'); }); - it('include COUNT', () => { - expect(String(count())).toBe('COUNT(*)::INTEGER'); + + it('include covar_pop', () => { + expect(String(covarPop('foo', 'bar'))).toBe('covar_pop("foo", "bar")'); }); - it('include COVAR_POP', () => { - expect(String(covarPop('foo', 'bar'))).toBe('COVAR_POP("foo", "bar")'); + + it('include covar_samp', () => { + expect(String(covariance('foo', 'bar'))).toBe('covar_samp("foo", "bar")'); }); - it('include ENTROPY', () => { - expect(String(entropy('foo'))).toBe('ENTROPY("foo")'); + + it('include entropy', () => { + expect(String(entropy('foo'))).toBe('entropy("foo")'); }); - it('include FIRST', () => { - expect(String(first('foo'))).toBe('FIRST("foo")'); + + it('include first', () => { + expect(String(first('foo'))).toBe('first("foo")'); }); - it('include KURTOSIS', () => { - expect(String(kurtosis('foo'))).toBe('KURTOSIS("foo")'); + + it('include kurtosis', () => { + expect(String(kurtosis('foo'))).toBe('kurtosis("foo")'); }); - it('include MAD', () => { - expect(String(mad('foo'))).toBe('MAD("foo")'); + + it('include mad', () => { + expect(String(mad('foo'))).toBe('mad("foo")'); }); - it('include MAX', () => { - expect(String(max('foo'))).toBe('MAX("foo")'); + + it('include max', () => { + expect(String(max('foo'))).toBe('max("foo")'); }); - it('include MEDIAN', () => { - expect(String(median('foo'))).toBe('MEDIAN("foo")'); + + it('include median', () => { + expect(String(median('foo'))).toBe('median("foo")'); }); - it('include MIN', () => { - expect(String(min('foo'))).toBe('MIN("foo")'); + + it('include min', () => { + expect(String(min('foo'))).toBe('min("foo")'); }); - it('include MODE', () => { - expect(String(mode('foo'))).toBe('MODE("foo")'); + + it('include mode', () => { + expect(String(mode('foo'))).toBe('mode("foo")'); }); - it('include LAST', () => { - expect(String(last('foo'))).toBe('LAST("foo")'); + + it('include last', () => { + expect(String(last('foo'))).toBe('last("foo")'); }); - it('include PRODUCT', () => { - expect(String(product('foo'))).toBe('PRODUCT("foo")'); + + it('include product', () => { + expect(String(product('foo'))).toBe('product("foo")'); }); - it('include QUANTILE', () => { - expect(String(quantile('foo', 0.25))).toBe('QUANTILE("foo", 0.25)'); + + it('include quantile', () => { + expect(String(quantile('foo', 0.25))).toBe('quantile("foo", 0.25)'); }); - it('include REGR_AVGX', () => { - expect(String(regrAvgX('x', 'y'))).toBe('REGR_AVGX("x", "y")'); + + it('include regr_avgx', () => { + expect(String(regrAvgX('x', 'y'))).toBe('regr_avgx("x", "y")'); }); - it('include REGR_AVGY', () => { - expect(String(regrAvgY('x', 'y'))).toBe('REGR_AVGY("x", "y")'); + + it('include regr_avgy', () => { + expect(String(regrAvgY('x', 'y'))).toBe('regr_avgy("x", "y")'); }); - it('include REGR_COUNT', () => { - expect(String(regrCount('x', 'y'))).toBe('REGR_COUNT("x", "y")'); + + it('include regr_count', () => { + expect(String(regrCount('x', 'y'))).toBe('regr_count("x", "y")'); }); - it('include REGR_INTERCEPT', () => { - expect(String(regrIntercept('x', 'y'))).toBe('REGR_INTERCEPT("x", "y")'); + + it('include regr_intercept', () => { + expect(String(regrIntercept('x', 'y'))).toBe('regr_intercept("x", "y")'); }); - it('include REGR_R2', () => { - expect(String(regrR2('x', 'y'))).toBe('REGR_R2("x", "y")'); + + it('include regr_r2', () => { + expect(String(regrR2('x', 'y'))).toBe('regr_r2("x", "y")'); }); - it('include REGR_SXX', () => { - expect(String(regrSXX('x', 'y'))).toBe('REGR_SXX("x", "y")'); + + it('include regr_sxx', () => { + expect(String(regrSXX('x', 'y'))).toBe('regr_sxx("x", "y")'); }); - it('include REGR_SXY', () => { - expect(String(regrSXY('x', 'y'))).toBe('REGR_SXY("x", "y")'); + + it('include regr_sxy', () => { + expect(String(regrSXY('x', 'y'))).toBe('regr_sxy("x", "y")'); }); - it('include REGR_SYY', () => { - expect(String(regrSYY('x', 'y'))).toBe('REGR_SYY("x", "y")'); + + it('include regr_syy', () => { + expect(String(regrSYY('x', 'y'))).toBe('regr_syy("x", "y")'); }); - it('include REGR_SLOPE', () => { - expect(String(regrSlope('x', 'y'))).toBe('REGR_SLOPE("x", "y")'); + + it('include regr_slope', () => { + expect(String(regrSlope('x', 'y'))).toBe('regr_slope("x", "y")'); }); - it('include SKEWNESS', () => { - expect(String(skewness('foo'))).toBe('SKEWNESS("foo")'); + + it('include skewness', () => { + expect(String(skewness('foo'))).toBe('skewness("foo")'); }); - it('include STDDEV', () => { - expect(String(stddev('foo'))).toBe('STDDEV("foo")'); + + it('include stddev', () => { + expect(String(stddev('foo'))).toBe('stddev("foo")'); }); - it('include STDDEV_POP', () => { - expect(String(stddevPop('foo'))).toBe('STDDEV_POP("foo")'); + + it('include stddev_pop', () => { + expect(String(stddevPop('foo'))).toBe('stddev_pop("foo")'); }); - it('include STRING_AGG', () => { - expect(String(stringAgg('foo'))).toBe('STRING_AGG("foo")'); + + it('include string_agg', () => { + expect(String(stringAgg('foo'))).toBe('string_agg("foo")'); }); - it('include SUM', () => { - expect(String(sum('foo'))).toBe('SUM("foo")::DOUBLE'); + + it('include sum', () => { + expect(String(sum('foo'))).toBe('sum("foo")'); }); - it('include VARIANCE', () => { - expect(String(variance('foo'))).toBe('VARIANCE("foo")'); + + it('include var_samp', () => { + expect(String(variance('foo'))).toBe('var_samp("foo")'); }); - it('include VAR_POP', () => { - expect(String(varPop('foo'))).toBe('VAR_POP("foo")'); + + it('include var_pop', () => { + expect(String(varPop('foo'))).toBe('var_pop("foo")'); }); }); diff --git a/packages/sql/test/cast.test.js b/packages/sql/test/cast.test.js index 0ca4c9e9..b81e7d4d 100644 --- a/packages/sql/test/cast.test.js +++ b/packages/sql/test/cast.test.js @@ -1,38 +1,33 @@ import { expect, describe, it } from 'vitest'; -import { avg, cast, castDouble, castInteger, column } from '../src/index.js'; +import { add, cast, column, int32, float32, float64 } from '../src/index.js'; describe('cast', () => { it('performs type casts', () => { - expect(String(cast('foo', 'DOUBLE'))).toBe('CAST("foo" AS DOUBLE)'); - expect(String(cast(column('foo'), 'DOUBLE'))).toBe('CAST("foo" AS DOUBLE)'); + expect(String(cast('foo', 'DOUBLE'))).toBe('("foo")::DOUBLE'); + expect(String(cast(column('foo'), 'DOUBLE'))).toBe('("foo")::DOUBLE'); - const expr = cast(avg('bar'), 'DOUBLE'); - expect(String(expr)).toBe('CAST(AVG("bar") AS DOUBLE)'); - expect(expr.aggregate).toBe('AVG'); - expect(expr.column).toBe('bar'); - expect(expr.columns).toEqual(['bar']); - expect(expr.label).toBe('avg(bar)'); + const expr = cast(add('bar', 'baz'), 'INTEGER'); + expect(String(expr)).toBe('(("bar" + "baz"))::INTEGER'); }); - it('performs double casts', () => { - expect(String(castDouble('foo'))).toBe('CAST("foo" AS DOUBLE)'); - expect(String(castDouble(column('foo')))).toBe('CAST("foo" AS DOUBLE)'); - const expr = castDouble(avg('bar')); - expect(String(expr)).toBe('CAST(AVG("bar") AS DOUBLE)'); - expect(expr.aggregate).toBe('AVG'); - expect(expr.column).toBe('bar'); - expect(expr.columns).toEqual(['bar']); - expect(expr.label).toBe('avg(bar)'); + describe('int32', () => { + it('casts to 32-bit integer', () => { + expect(String(int32('foo'))).toBe('("foo")::INTEGER'); + expect(String(int32(column('foo')))).toBe('("foo")::INTEGER'); + }); }); - it('performs integer casts', () => { - expect(String(castInteger('foo'))).toBe('CAST("foo" AS INTEGER)'); - expect(String(castInteger(column('foo')))).toBe('CAST("foo" AS INTEGER)'); - const expr = castInteger(avg('bar')); - expect(String(expr)).toBe('CAST(AVG("bar") AS INTEGER)'); - expect(expr.aggregate).toBe('AVG'); - expect(expr.column).toBe('bar'); - expect(expr.columns).toEqual(['bar']); - expect(expr.label).toBe('avg(bar)'); + describe('float32', () => { + it('casts to 32-bit floating point number', () => { + expect(String(float32('foo'))).toBe('("foo")::FLOAT'); + expect(String(float32(column('foo')))).toBe('("foo")::FLOAT'); + }); + }); + + describe('float64', () => { + it('casts to 64-bit floating point number', () => { + expect(String(float64('foo'))).toBe('("foo")::DOUBLE'); + expect(String(float64(column('foo')))).toBe('("foo")::DOUBLE'); + }); }); }); diff --git a/packages/sql/test/create.test.js b/packages/sql/test/create.test.js index ad44aa12..f348a18c 100644 --- a/packages/sql/test/create.test.js +++ b/packages/sql/test/create.test.js @@ -1,5 +1,5 @@ import { expect, describe, it } from 'vitest'; -import { createSchema, createTable } from '../src/load/create.js'; +import { createSchema, createTable } from '../src/index.js'; describe('createTable', () => { it('creates a table', () => { diff --git a/packages/sql/test/desc.test.js b/packages/sql/test/desc.test.js deleted file mode 100644 index d1686258..00000000 --- a/packages/sql/test/desc.test.js +++ /dev/null @@ -1,27 +0,0 @@ -import { expect, describe, it } from 'vitest'; -import { stubParam } from './stub-param.js'; -import { column, desc, isSQLExpression, isParamLike } from '../src/index.js'; - -describe('desc', () => { - it('creates descending order annotations', () => { - const expr = desc('foo'); - expect(isSQLExpression(expr)).toBe(true); - expect(isParamLike(expr)).toBe(false); - expect(String(expr)).toBe('"foo" DESC NULLS LAST'); - expect(expr.column).toBe('foo'); - expect(expr.columns).toEqual(['foo']); - - const param = stubParam(column('bar')); - const expr2 = desc(param); - expect(isSQLExpression(expr2)).toBe(true); - expect(isParamLike(expr2)).toBe(true); - expect(String(expr2)).toBe('"bar" DESC NULLS LAST'); - - expr2.addEventListener('value', value => { - expect(isSQLExpression(value)).toBe(true); - expect(String(expr2)).toBe(`${value}`); - }); - param.update(column('baz')); - expect(String(expr2)).toBe('"baz" DESC NULLS LAST'); - }); -}); diff --git a/packages/sql/test/function.test.js b/packages/sql/test/function.test.js deleted file mode 100644 index 92d6df40..00000000 --- a/packages/sql/test/function.test.js +++ /dev/null @@ -1,78 +0,0 @@ -import { expect, describe, it } from 'vitest'; -import { - contains, isFinite, isInfinite, isNaN, literal, length, - lower, prefix, regexp_matches, suffix, upper -} from '../src/index.js'; - -describe('Function calls', () => { - it('includes REGEXP_MATCHES', () => { - const expr = regexp_matches('foo', literal('(an)*')) - expect(String(expr)).toBe(`REGEXP_MATCHES("foo", '(an)*')`); - expect(expr.func).toBe('REGEXP_MATCHES'); - expect(expr.args.length).toBe(2); - expect(expr.columns).toEqual(['foo']); - }); - it('includes CONTAINS', () => { - const expr = contains('foo', literal('oo')) - expect(String(expr)).toBe(`CONTAINS("foo", 'oo')`); - expect(expr.func).toBe('CONTAINS'); - expect(expr.args.length).toBe(2); - expect(expr.columns).toEqual(['foo']); - }); - it('includes PREFIX', () => { - const expr = prefix('foo', literal('fo')) - expect(String(expr)).toBe(`PREFIX("foo", 'fo')`); - expect(expr.func).toBe('PREFIX'); - expect(expr.args.length).toBe(2); - expect(expr.columns).toEqual(['foo']); - }); - it('includes SUFFIX', () => { - const expr = suffix('foo', literal('oo')) - expect(String(expr)).toBe(`SUFFIX("foo", 'oo')`); - expect(expr.func).toBe('SUFFIX'); - expect(expr.args.length).toBe(2); - expect(expr.columns).toEqual(['foo']); - }); - it('includes LOWER', () => { - const expr = lower('foo') - expect(String(expr)).toBe(`LOWER("foo")`); - expect(expr.func).toBe('LOWER'); - expect(expr.args.length).toBe(1); - expect(expr.columns).toEqual(['foo']); - }); - it('includes UPPER', () => { - const expr = upper('foo') - expect(String(expr)).toBe(`UPPER("foo")`); - expect(expr.func).toBe('UPPER'); - expect(expr.args.length).toBe(1); - expect(expr.columns).toEqual(['foo']); - }); - it('includes LENGTH', () => { - const expr = length('foo') - expect(String(expr)).toBe(`LENGTH("foo")`); - expect(expr.func).toBe('LENGTH'); - expect(expr.args.length).toBe(1); - expect(expr.columns).toEqual(['foo']); - }); - it('includes ISNAN', () => { - const expr = isNaN('foo') - expect(String(expr)).toBe(`ISNAN("foo")`); - expect(expr.func).toBe('ISNAN'); - expect(expr.args.length).toBe(1); - expect(expr.columns).toEqual(['foo']); - }); - it('includes ISFINITE', () => { - const expr = isFinite('foo') - expect(String(expr)).toBe(`ISFINITE("foo")`); - expect(expr.func).toBe('ISFINITE'); - expect(expr.args.length).toBe(1); - expect(expr.columns).toEqual(['foo']); - }); - it('includes ISINF', () => { - const expr = isInfinite('foo') - expect(String(expr)).toBe(`ISINF("foo")`); - expect(expr.func).toBe('ISINF'); - expect(expr.args.length).toBe(1); - expect(expr.columns).toEqual(['foo']); - }); -}); diff --git a/packages/sql/test/literal/test.js b/packages/sql/test/literal/test.js deleted file mode 100644 index 45398016..00000000 --- a/packages/sql/test/literal/test.js +++ /dev/null @@ -1,65 +0,0 @@ -import assert from 'node:assert'; -import { literal } from '../src/index.js'; - -describe('literal', () => { - it('handles booleans', () => { - const trueExpr = literal(true); - assert.strictEqual(trueExpr.value, true); - assert.strictEqual(String(trueExpr), `TRUE`); - - const falseExpr = literal(false); - assert.strictEqual(falseExpr.value, false); - assert.strictEqual(String(falseExpr), `FALSE`); - }); - it('handles dates', () => { - const date = new Date('2012-01-01'); - const dateExpr = literal(date); - assert.strictEqual(dateExpr.value, date); - assert.strictEqual(String(dateExpr), 'MAKE_DATE(2012, 1, 1)'); - - const timestamp = new Date('2012-01-01T17:51:12.833Z'); - const timestampExpr = literal(timestamp); - assert.strictEqual(timestampExpr.value, timestamp); - assert.strictEqual(String(timestampExpr), `EPOCH_MS(${+timestamp})`); - - const badDate = new Date('foobar'); - const badDateExpr = literal(badDate); - assert.strictEqual(badDateExpr.value, badDate); - assert.strictEqual(String(badDateExpr), 'NULL'); - }); - it('handles nulls', () => { - const nullExpr = literal(null); - assert.strictEqual(nullExpr.value, null); - assert.strictEqual(String(nullExpr), `NULL`); - - const undefinedExpr = literal(undefined); - assert.strictEqual(undefinedExpr.value, undefined); - assert.strictEqual(String(undefinedExpr), `NULL`); - }); - it('handles numbers', () => { - const numberExpr = literal(1); - assert.strictEqual(numberExpr.value, 1); - assert.strictEqual(String(numberExpr), `1`); - - const nanExpr = literal(NaN); - assert.strictEqual(nanExpr.value, NaN); - assert.strictEqual(String(nanExpr), `NULL`); - - const infinityExpr = literal(Infinity); - assert.strictEqual(infinityExpr.value, Infinity); - assert.strictEqual(String(infinityExpr), `NULL`); - }); - it('handles strings', () => { - const stringExpr = literal('str'); - assert.strictEqual(stringExpr.value, 'str'); - assert.strictEqual(String(stringExpr), `'str'`); - - const emptyExpr = literal(''); - assert.strictEqual(emptyExpr.value, ''); - assert.strictEqual(String(emptyExpr), `''`); - - const stringWithQuotes = literal(`don't`); - assert.strictEqual(stringWithQuotes.value, `don't`); - assert.strictEqual(String(stringWithQuotes), `'don''t'`); - }); -}); diff --git a/packages/sql/test/numeric.test.js b/packages/sql/test/numeric.test.js new file mode 100644 index 00000000..b1fe499b --- /dev/null +++ b/packages/sql/test/numeric.test.js @@ -0,0 +1,27 @@ +import { expect, describe, it } from 'vitest'; +import { isFinite, isInfinite, isNaN } from '../src/index.js'; +import { columns } from './util/columns.js'; + +describe('Number functions', () => { + it('include isNaN', () => { + const expr = isNaN('foo') + expect(String(expr)).toBe(`isnan("foo")`); + expect(expr.name).toBe('isnan'); + expect(expr.args.length).toBe(1); + expect(columns(expr)).toEqual(['foo']); + }); + it('include isFinite', () => { + const expr = isFinite('foo') + expect(String(expr)).toBe(`isfinite("foo")`); + expect(expr.name).toBe('isfinite'); + expect(expr.args.length).toBe(1); + expect(columns(expr)).toEqual(['foo']); + }); + it('include isInfinite', () => { + const expr = isInfinite('foo') + expect(String(expr)).toBe(`isinf("foo")`); + expect(expr.name).toBe('isinf'); + expect(expr.args.length).toBe(1); + expect(columns(expr)).toEqual(['foo']); + }); +}); diff --git a/packages/sql/test/operator.test.js b/packages/sql/test/operators.test.js similarity index 63% rename from packages/sql/test/operator.test.js rename to packages/sql/test/operators.test.js index 2c7280af..136866f9 100644 --- a/packages/sql/test/operator.test.js +++ b/packages/sql/test/operators.test.js @@ -1,11 +1,5 @@ import { expect, describe, it } from 'vitest'; -import { - column, and, or, not, - isNull, isNotNull, - eq, neq, lt, gt, lte, gte, - isDistinct, isNotDistinct, - isBetween, isNotBetween -} from '../src/index.js'; +import { add, and, column, div, eq, gt, gte, idiv, isBetween, isDistinct, isNotBetween, isNotDistinct, isNotNull, isNull, lt, lte, mod, mul, neq, not, or, pow, sub } from '../src/index.js'; describe('Logical operators', () => { it('include AND expressions', () => { @@ -15,10 +9,10 @@ describe('Logical operators', () => { expect(String(and(true, true))).toBe('(TRUE AND TRUE)'); expect(String(and(true, null, false))).toBe('(TRUE AND FALSE)'); expect(and().op).toBe('AND'); - expect(and().children.length).toBe(0); - expect(and('foo').children.length).toBe(1); - expect(and(null, true).children.length).toBe(1); - expect(and(true, true).children.length).toBe(2); + expect(and().clauses.length).toBe(0); + expect(and('foo').clauses.length).toBe(1); + expect(and(null, true).clauses.length).toBe(1); + expect(and(true, true).clauses.length).toBe(2); }); it('include OR expressions', () => { expect(String(or())).toBe(''); @@ -27,10 +21,10 @@ describe('Logical operators', () => { expect(String(or(false, true))).toBe('(FALSE OR TRUE)'); expect(String(or(false, null, false))).toBe('(FALSE OR FALSE)'); expect(or().op).toBe('OR'); - expect(or().children.length).toBe(0); - expect(or('foo').children.length).toBe(1); - expect(or(null, true).children.length).toBe(1); - expect(or(false, true).children.length).toBe(2); + expect(or().clauses.length).toBe(0); + expect(or('foo').clauses.length).toBe(1); + expect(or(null, true).clauses.length).toBe(1); + expect(or(false, true).clauses.length).toBe(2); }); }); @@ -50,6 +44,34 @@ describe('Unary operators', () => { }); describe('Binary operators', () => { + it('include addition operator', () => { + expect(String(add(column('foo'), 1))).toBe('("foo" + 1)'); + expect(String(add('foo', 1))).toBe('("foo" + 1)'); + }); + it('include subtraction operator', () => { + expect(String(sub(column('foo'), 1))).toBe('("foo" - 1)'); + expect(String(sub('foo', 1))).toBe('("foo" - 1)'); + }); + it('include multiplication operator', () => { + expect(String(mul(column('foo'), 1))).toBe('("foo" * 1)'); + expect(String(mul('foo', 1))).toBe('("foo" * 1)'); + }); + it('include division operator', () => { + expect(String(div(column('foo'), 2))).toBe('("foo" / 2)'); + expect(String(div('foo', 2))).toBe('("foo" / 2)'); + }); + it('include integer division operator', () => { + expect(String(idiv(column('foo'), 2))).toBe('("foo" // 2)'); + expect(String(idiv('foo', 2))).toBe('("foo" // 2)'); + }); + it('include modulo operator', () => { + expect(String(mod(column('foo'), 2))).toBe('("foo" % 2)'); + expect(String(mod('foo', 2))).toBe('("foo" % 2)'); + }); + it('include exponentiation operator', () => { + expect(String(pow(column('foo'), 2))).toBe('("foo" ** 2)'); + expect(String(pow('foo', 2))).toBe('("foo" ** 2)'); + }); it('include equality comparisons', () => { expect(String(eq(column('foo'), 1))).toBe('("foo" = 1)'); expect(String(eq('foo', 1))).toBe('("foo" = 1)'); @@ -85,20 +107,12 @@ describe('Binary operators', () => { }); describe('Range operators', () => { - it('test within inclusive ranges', () => { + it('include BETWEEN operator', () => { expect(String(isBetween('a', null))).toBe(''); expect(String(isBetween('a', [0, 1]))).toBe('("a" BETWEEN 0 AND 1)'); }); - it('test within exclusive ranges', () => { - expect(String(isBetween('a', null, true))).toBe(''); - expect(String(isBetween('a', [0, 1], true))).toBe('(0 <= "a" AND "a" < 1)'); - }); - it('test not within inclusive ranges', () => { + it('include NOT BETWEEN operator', () => { expect(String(isNotBetween('a', null))).toBe(''); expect(String(isNotBetween('a', [0, 1]))).toBe('("a" NOT BETWEEN 0 AND 1)'); }); - it('test not within exclusive ranges', () => { - expect(String(isNotBetween('a', null, true))).toBe(''); - expect(String(isNotBetween('a', [0, 1], true))).toBe('NOT (0 <= "a" AND "a" < 1)'); - }); }); diff --git a/packages/sql/test/order-by.test.js b/packages/sql/test/order-by.test.js new file mode 100644 index 00000000..ab4e29b7 --- /dev/null +++ b/packages/sql/test/order-by.test.js @@ -0,0 +1,60 @@ +import { expect, describe, it } from 'vitest'; +import { columns } from './util/columns.js'; +import { stubParam } from './util/stub-param.js'; +import { asc, column, desc, isParamLike } from '../src/index.js'; + +describe('asc', () => { + it('specifies ascending order', () => { + const expr = asc('foo'); + expect(isParamLike(expr)).toBe(false); + expect(String(expr)).toBe('"foo" ASC'); + expect(columns(expr)).toEqual(['foo']); + + const param = stubParam(column('bar')); + const expr2 = asc(param); + expect(String(expr2)).toBe('"bar" ASC'); + + param.update(column('baz')); + expect(String(expr2)).toBe('"baz" ASC'); + }); + + it('specifies ascending order with nulls first', () => { + const expr = asc('foo', true); + expect(String(expr)).toBe('"foo" ASC NULLS FIRST'); + expect(columns(expr)).toEqual(['foo']); + }); + + it('specifies ascending order with nulls last', () => { + const expr = asc('foo', false); + expect(String(expr)).toBe('"foo" ASC NULLS LAST'); + expect(columns(expr)).toEqual(['foo']); + }); +}); + +describe('desc', () => { + it('specifies descending order', () => { + const expr = desc('foo'); + expect(isParamLike(expr)).toBe(false); + expect(String(expr)).toBe('"foo" DESC'); + expect(columns(expr)).toEqual(['foo']); + + const param = stubParam(column('bar')); + const expr2 = desc(param); + expect(String(expr2)).toBe('"bar" DESC'); + + param.update(column('baz')); + expect(String(expr2)).toBe('"baz" DESC'); + }); + + it('specifies descending order with nulls first', () => { + const expr = desc('foo', true); + expect(String(expr)).toBe('"foo" DESC NULLS FIRST'); + expect(columns(expr)).toEqual(['foo']); + }); + + it('specifies descending order with nulls last', () => { + const expr = desc('foo', false); + expect(String(expr)).toBe('"foo" DESC NULLS LAST'); + expect(columns(expr)).toEqual(['foo']); + }); +}); diff --git a/packages/sql/test/query.test.js b/packages/sql/test/query.test.js index 5cc870b3..50ab7e4a 100644 --- a/packages/sql/test/query.test.js +++ b/packages/sql/test/query.test.js @@ -1,7 +1,5 @@ import { expect, describe, it } from 'vitest'; -import { - column, desc, gt, lt, max, min, relation, sql, Query -} from '../src/index.js'; +import { asNode, asTableRef, column, desc, gt, lt, max, min, sql, Query, sum, lead, over } from '../src/index.js'; describe('Query', () => { it('selects column name strings', () => { @@ -17,7 +15,7 @@ describe('Query', () => { expect( Query .select('foo', 'bar', 'baz') - .from(relation('data')) + .from(asTableRef('data')) .toString() ).toBe(query); @@ -111,21 +109,60 @@ describe('Query', () => { .select({ min: min('foo'), max: max('foo') }) .from('data') .toString() - ).toBe('SELECT MIN("foo") AS "min", MAX("foo") AS "max" FROM "data"'); + ).toBe('SELECT min("foo") AS "min", max("foo") AS "max" FROM "data"'); expect( Query .select({ min: min(foo), max: max(foo) }) .from('data') .toString() - ).toBe('SELECT MIN("foo") AS "min", MAX("foo") AS "max" FROM "data"'); + ).toBe('SELECT min("foo") AS "min", max("foo") AS "max" FROM "data"'); expect( Query .select({ min: min('foo').where(gt('bar', 5)) }) .from('data') .toString() - ).toBe('SELECT MIN("foo") FILTER (WHERE ("bar" > 5)) AS "min" FROM "data"'); + ).toBe('SELECT min("foo") FILTER (WHERE ("bar" > 5)) AS "min" FROM "data"'); + }); + + it('selects windowed aggregates', () => { + const foo = column('foo'); + + expect( + Query + .select({ csum: sum('foo').window() }) + .from('data') + .toString() + ).toBe('SELECT sum("foo") OVER () AS "csum" FROM "data"'); + + expect( + Query + .select({ csum: sum(foo).window() }) + .from('data') + .toString() + ).toBe('SELECT sum("foo") OVER () AS "csum" FROM "data"'); + + expect( + Query + .select({ csum: sum(foo).partitionby('baz') }) + .from('data') + .toString() + ).toBe('SELECT sum("foo") OVER (PARTITION BY "baz") AS "csum" FROM "data"'); + + expect( + Query + .select({ csum: sum(foo).orderby('bop') }) + .from('data') + .toString() + ).toBe('SELECT sum("foo") OVER (ORDER BY "bop") AS "csum" FROM "data"'); + + expect( + Query + .select({ csum: sum(foo).partitionby('baz').orderby('bop') }) + .from('data') + .toString() + ).toBe('SELECT sum("foo") OVER (PARTITION BY "baz" ORDER BY "bop") AS "csum" FROM "data"'); }); it('selects grouped aggregates', () => { @@ -134,7 +171,7 @@ describe('Query', () => { const baz = column('baz'); const query = [ - 'SELECT MIN("foo") AS "min", MAX("foo") AS "max", "bar", "baz"', + 'SELECT min("foo") AS "min", max("foo") AS "max", "bar", "baz"', 'FROM "data"', 'GROUP BY "bar", "baz"' ].join(' '); @@ -178,7 +215,7 @@ describe('Query', () => { const bar = column('bar'); const query = [ - 'SELECT MIN("foo") AS "min", "bar"', + 'SELECT min("foo") AS "min", "bar"', 'FROM "data"', 'GROUP BY "bar"', 'HAVING ("min" > 50) AND ("min" < 100)' @@ -273,7 +310,7 @@ describe('Query', () => { const query = [ 'SELECT *', 'FROM "data"', - 'ORDER BY "bar", "baz" DESC NULLS LAST' + 'ORDER BY "bar", "baz" DESC' ].join(' '); expect( @@ -305,7 +342,7 @@ describe('Query', () => { Query .select('*') .from('data') - .orderby(sql`"bar", "baz" DESC NULLS LAST`) + .orderby(sql`"bar", "baz" DESC`) .toString() ).toBe(query); }); @@ -319,45 +356,29 @@ describe('Query', () => { .toString() ).toBe('SELECT * FROM "data" USING SAMPLE 10 ROWS'); - expect( - Query - .select('*') - .from('data') - .sample({ rows: 10 }) - .toString() - ).toBe('SELECT * FROM "data" USING SAMPLE 10 ROWS'); - expect( Query .select('*') .from('data') .sample(0.3) .toString() - ).toBe('SELECT * FROM "data" USING SAMPLE 30 PERCENT'); + ).toBe('SELECT * FROM "data" USING SAMPLE 30%'); expect( Query .select('*') .from('data') - .sample({ perc: 30 }) + .sample(0.1, 'bernoulli') .toString() - ).toBe('SELECT * FROM "data" USING SAMPLE 30 PERCENT'); + ).toBe('SELECT * FROM "data" USING SAMPLE 10% (bernoulli)'); expect( Query .select('*') .from('data') - .sample({ rows: 100, method: 'bernoulli' }) + .sample(0.1, 'bernoulli', 12345) .toString() - ).toBe('SELECT * FROM "data" USING SAMPLE 100 ROWS (bernoulli)'); - - expect( - Query - .select('*') - .from('data') - .sample({ rows: 100, method: 'bernoulli', seed: 12345 }) - .toString() - ).toBe('SELECT * FROM "data" USING SAMPLE 100 ROWS (bernoulli, 12345)'); + ).toBe('SELECT * FROM "data" USING SAMPLE 10% (bernoulli, 12345)'); }); it('selects from multiple relations', () => { @@ -369,8 +390,8 @@ describe('Query', () => { expect( Query .select({ - foo: column('a', 'foo'), - bar: column('b', 'bar') + foo: asNode('a.foo'), + bar: asNode('b.bar') }) .from({ a: 'data1', b: 'data2' }) .toString() @@ -380,11 +401,27 @@ describe('Query', () => { it('selects over windows', () => { expect( Query - .select({ lead: sql`lead("foo") OVER "win"` }) + .select({ lead: lead('foo').over('win') }) .from('data') - .window({ win: sql`ORDER BY "foo" ASC` }) + .window({ win: sql`(ORDER BY "foo" ASC)` }) .toString() ).toBe('SELECT lead("foo") OVER "win" AS "lead" FROM "data" WINDOW "win" AS (ORDER BY "foo" ASC)'); + + expect( + Query + .select({ lead: lead('foo').over('win') }) + .from('data') + .window({ win: over().orderby('foo') }) + .toString() + ).toBe('SELECT lead("foo") OVER "win" AS "lead" FROM "data" WINDOW "win" AS (ORDER BY "foo")'); + + expect( + Query + .select({ lead: lead('foo').over('win') }) + .from('data') + .window({ win: over().orderby(desc('foo')) }) + .toString() + ).toBe('SELECT lead("foo") OVER "win" AS "lead" FROM "data" WINDOW "win" AS (ORDER BY "foo" DESC)'); }); it('selects from subqueries', () => { diff --git a/packages/sql/test/expression.test.js b/packages/sql/test/sql-template.test.js similarity index 50% rename from packages/sql/test/expression.test.js rename to packages/sql/test/sql-template.test.js index 57c4280d..50b526a3 100644 --- a/packages/sql/test/expression.test.js +++ b/packages/sql/test/sql-template.test.js @@ -1,34 +1,29 @@ import { expect, describe, it } from 'vitest'; -import { stubParam } from './stub-param.js'; -import { column, isSQLExpression, isParamLike, sql } from '../src/index.js'; +import { stubParam } from './util/stub-param.js'; +import { column, isParamLike, sql } from '../src/index.js'; +import { columns, params } from './util/columns.js'; -describe('sql template tag', () => { +describe('sql expression', () => { it('creates basic SQL expressions', () => { const expr = sql`1 + 1`; - expect(isSQLExpression(expr)).toBe(true); - expect(isParamLike(expr)).toBe(false); expect(String(expr)).toBe('1 + 1'); - expect(expr.column).toBeUndefined(); - expect(expr.columns).toEqual([]); + expect(columns(expr)).toEqual([]); + expect(params(expr)).toEqual([]); }); it('creates interpolated SQL expressions', () => { const expr = sql`${column('foo')} * ${column('bar')}`; - expect(isSQLExpression(expr)).toBe(true); - expect(isParamLike(expr)).toBe(false); expect(String(expr)).toBe('"foo" * "bar"'); - expect(expr.column).toBe('foo'); - expect(expr.columns).toEqual(['foo', 'bar']); + expect(columns(expr)).toEqual(['foo', 'bar']); + expect(params(expr)).toEqual([]); }); it('creates nested SQL expressions', () => { const base = sql`${column('foo')} * 4`; const expr = sql`${base} + 1`; - expect(isSQLExpression(expr)).toBe(true); expect(String(expr)).toBe('"foo" * 4 + 1'); - expect(isParamLike(expr)).toBe(false); - expect(expr.column).toBe('foo'); - expect(expr.columns).toEqual(['foo']); + expect(columns(expr)).toEqual(['foo']); + expect(params(expr)).toEqual([]); }); it('creates parameterized SQL expressions', () => { @@ -36,16 +31,11 @@ describe('sql template tag', () => { expect(isParamLike(param)).toBe(true); const expr = sql`${column('foo')} * ${param}`; - expect(isSQLExpression(expr)).toBe(true); expect(String(expr)).toBe('"foo" * 4'); - expect(isParamLike(expr)).toBe(true); - expect(expr.column).toBe('foo'); - expect(expr.columns).toEqual(['foo']); + expect(isParamLike(expr)).toBe(false); + expect(columns(expr)).toEqual(['foo']); + expect(params(expr)).toEqual([param]); - expr.addEventListener('value', value => { - expect(isSQLExpression(value)).toBe(true); - expect(String(expr)).toBe(`${value}`); - }); param.update(5); expect(String(expr)).toBe('"foo" * 5'); }); @@ -56,16 +46,11 @@ describe('sql template tag', () => { const base = sql`${column('foo')} * ${param}`; const expr = sql`${base} + 1`; - expect(isSQLExpression(expr)).toBe(true); expect(String(expr)).toBe('"foo" * 4 + 1'); - expect(isParamLike(expr)).toBe(true); - expect(expr.column).toBe('foo'); - expect(expr.columns).toEqual(['foo']); + expect(isParamLike(expr)).toBe(false); + expect(columns(expr)).toEqual(['foo']); + expect(params(expr)).toEqual([param]); - expr.addEventListener('value', value => { - expect(isSQLExpression(value)).toBe(true); - expect(String(expr)).toBe(`${value}`); - }); param.update(5); expect(String(expr)).toBe('"foo" * 5 + 1'); }); diff --git a/packages/sql/test/string.test.js b/packages/sql/test/string.test.js new file mode 100644 index 00000000..3b3bd362 --- /dev/null +++ b/packages/sql/test/string.test.js @@ -0,0 +1,61 @@ +import { expect, describe, it } from 'vitest'; +import { contains, literal, length, lower, prefix, regexp_matches, suffix, upper } from '../src/index.js'; +import { columns } from './util/columns.js'; + +describe('String functions', () => { + it('include regexp_matches', () => { + const expr = regexp_matches('foo', literal('(an)*')) + expect(String(expr)).toBe(`regexp_matches("foo", '(an)*')`); + expect(expr.name).toBe('regexp_matches'); + expect(expr.args.length).toBe(2); + expect(columns(expr)).toStrictEqual(['foo']); + }); + + it('include contains', () => { + const expr = contains('foo', literal('oo')) + expect(String(expr)).toBe(`contains("foo", 'oo')`); + expect(expr.name).toBe('contains'); + expect(expr.args.length).toBe(2); + expect(columns(expr)).toStrictEqual(['foo']); + }); + + it('include prefix', () => { + const expr = prefix('foo', literal('fo')) + expect(String(expr)).toBe(`starts_with("foo", 'fo')`); + expect(expr.name).toBe('starts_with'); + expect(expr.args.length).toBe(2); + expect(columns(expr)).toStrictEqual(['foo']); + }); + + it('include suffix', () => { + const expr = suffix('foo', literal('oo')) + expect(String(expr)).toBe(`ends_with("foo", 'oo')`); + expect(expr.name).toBe('ends_with'); + expect(expr.args.length).toBe(2); + expect(columns(expr)).toStrictEqual(['foo']); + }); + + it('include lower', () => { + const expr = lower('foo') + expect(String(expr)).toBe(`lower("foo")`); + expect(expr.name).toBe('lower'); + expect(expr.args.length).toBe(1); + expect(columns(expr)).toStrictEqual(['foo']); + }); + + it('include upper', () => { + const expr = upper('foo') + expect(String(expr)).toBe(`upper("foo")`); + expect(expr.name).toBe('upper'); + expect(expr.args.length).toBe(1); + expect(columns(expr)).toStrictEqual(['foo']); + }); + + it('include length', () => { + const expr = length('foo') + expect(String(expr)).toBe(`length("foo")`); + expect(expr.name).toBe('length'); + expect(expr.args.length).toBe(1); + expect(columns(expr)).toStrictEqual(['foo']); + }); +}); diff --git a/packages/sql/test/util/columns.js b/packages/sql/test/util/columns.js new file mode 100644 index 00000000..33752f2f --- /dev/null +++ b/packages/sql/test/util/columns.js @@ -0,0 +1,9 @@ +import { collectColumns, collectParams } from '../../src/index.js'; + +export function columns(node) { + return collectColumns(node).map(c => c.column); +} + +export function params(node) { + return collectParams(node); +} diff --git a/packages/sql/test/stub-param.js b/packages/sql/test/util/stub-param.js similarity index 89% rename from packages/sql/test/stub-param.js rename to packages/sql/test/util/stub-param.js index 1f1cc749..13486ecd 100644 --- a/packages/sql/test/stub-param.js +++ b/packages/sql/test/util/stub-param.js @@ -1,5 +1,5 @@ export function stubParam(value) { - let cb; + let cb = x => x; return { value, addEventListener(type, callback) { cb = callback; }, diff --git a/packages/sql/test/visitors.test.js b/packages/sql/test/visitors.test.js new file mode 100644 index 00000000..03f7cb03 --- /dev/null +++ b/packages/sql/test/visitors.test.js @@ -0,0 +1,43 @@ +import { expect, describe, it } from 'vitest'; +import { abs, add, asVerbatim, collectColumns, collectParams, column, count, isAggregateExpression, sql } from '../src/index.js'; +import { stubParam } from './util/stub-param.js'; + +describe('Visitor functions', () => { + it('include column collection', () => { + const a = column('a'); + const b = column('b'); + + const expr1 = sql`(${a} + ${b}) / ${a}`; + expect(collectColumns(expr1)).toStrictEqual([a, b]); + + const expr2 = sql`(${'a'} + ${'b'}) ${'a'}`; + expect(collectColumns(expr2)).toStrictEqual([]); + }); + + it('include param collection', () => { + const a = stubParam(1); + const b = stubParam(2); + + const expr1 = sql`(${a} + ${b}) / ${a}`; + expect(collectParams(expr1)).toStrictEqual([a, b]); + + const expr2 = sql`(${'a'} + ${'b'}) ${'a'}`; + expect(collectParams(expr2)).toStrictEqual([]); + }); + + it('include aggregate function detection', () => { + expect(isAggregateExpression(column('a'))).toBe(0); + expect(isAggregateExpression(add(1, 2))).toBe(0); + expect(isAggregateExpression(abs(-1))).toBe(0); + + expect(isAggregateExpression(count())).toBe(1); + expect(isAggregateExpression(asVerbatim('count(*)'))).toBe(2); + expect(isAggregateExpression(sql`count(*)`)).toBe(2); + expect(isAggregateExpression(sql`count(${column('a')})`)).toBe(2); + + expect(isAggregateExpression(count().orderby('a'))).toBe(0); + expect(isAggregateExpression(asVerbatim('count(*) OVER (ORDER BY a)'))).toBe(0); + expect(isAggregateExpression(sql`count(*) OVER (ORDER BY a)`)).toBe(0); + expect(isAggregateExpression(sql`count(${column('a')}) OVER (ORDER BY a)`)).toBe(0); + }); +}); diff --git a/packages/sql/test/window.test.js b/packages/sql/test/window.test.js index e5e8e484..511a7643 100644 --- a/packages/sql/test/window.test.js +++ b/packages/sql/test/window.test.js @@ -1,116 +1,122 @@ import { expect, describe, it } from 'vitest'; -import { stubParam } from './stub-param.js'; -import { - column, desc, isParamLike, isSQLExpression, - row_number, rank, dense_rank, percent_rank, cume_dist, - ntile, lag, lead, first_value, last_value, nth_value -} from '../src/index.js'; +import { stubParam } from './util/stub-param.js'; +import { column, cume_dist, dense_rank, desc, first_value, isParamLike, lag, last_value, lead, nth_value, ntile, percent_rank, rank, row_number } from '../src/index.js'; +import { columns } from './util/columns.js'; describe('Window functions', () => { - it('expose metadata', () => { + it('include accessible metadata', () => { const expr = lead('foo'); - expect(expr.window).toBe('LEAD'); - expect(expr.column).toBe('foo'); - expect(expr.columns).toEqual(['foo']); + expect(expr.func.name).toBe('lead'); + expect(columns(expr)).toStrictEqual(['foo']); }); - it('support window name', () => { + + it('support named window definition', () => { const expr = cume_dist().over('win'); - expect(expr.window).toBe('CUME_DIST'); - expect(expr.name).toBe('win'); - expect(String(expr)).toBe('CUME_DIST() OVER "win"'); + expect(expr.func.name).toBe('cume_dist'); + expect(expr.def.name).toBe('win'); + expect(String(expr)).toBe('cume_dist() OVER "win"'); }); + it('support partition by', () => { const expr = cume_dist().partitionby('foo', 'bar'); - expect(String(expr)).toBe('CUME_DIST() OVER (PARTITION BY "foo", "bar")'); + expect(String(expr)).toBe('cume_dist() OVER (PARTITION BY "foo", "bar")'); }); + it('support order by', () => { const expr = cume_dist().orderby('a', desc('b')); - expect(String(expr)).toBe('CUME_DIST() OVER (ORDER BY "a", "b" DESC NULLS LAST)'); + expect(String(expr)).toBe('cume_dist() OVER (ORDER BY "a", "b" DESC)'); }); + it('support rows frame', () => { expect(String(first_value('foo').rows([0, null]))) - .toBe('FIRST_VALUE("foo") OVER (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)'); + .toBe('first_value("foo") OVER (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)'); expect(String(first_value('foo').rows([null, null]))) - .toBe('FIRST_VALUE("foo") OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)'); + .toBe('first_value("foo") OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)'); expect(String(first_value('foo').rows([0, 2]))) - .toBe('FIRST_VALUE("foo") OVER (ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING)'); + .toBe('first_value("foo") OVER (ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING)'); expect(String(first_value('foo').rows([2, 0]))) - .toBe('FIRST_VALUE("foo") OVER (ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)'); + .toBe('first_value("foo") OVER (ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)'); }); + it('support range frame', () => { expect(String(first_value('foo').range([0, null]))) - .toBe('FIRST_VALUE("foo") OVER (RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)'); + .toBe('first_value("foo") OVER (RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)'); expect(String(first_value('foo').range([null, null]))) - .toBe('FIRST_VALUE("foo") OVER (RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)'); + .toBe('first_value("foo") OVER (RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)'); expect(String(first_value('foo').range([0, 2]))) - .toBe('FIRST_VALUE("foo") OVER (RANGE BETWEEN CURRENT ROW AND 2 FOLLOWING)'); + .toBe('first_value("foo") OVER (RANGE BETWEEN CURRENT ROW AND 2 FOLLOWING)'); expect(String(first_value('foo').range([2, 0]))) - .toBe('FIRST_VALUE("foo") OVER (RANGE BETWEEN 2 PRECEDING AND CURRENT ROW)'); + .toBe('first_value("foo") OVER (RANGE BETWEEN 2 PRECEDING AND CURRENT ROW)'); }); + it('support window name, partition by, order by, and frame', () => { const expr = cume_dist() .over('base') .partitionby('foo', 'bar') .orderby('a', desc('b')) .rows([0, +Infinity]); - expect(String(expr)).toBe('CUME_DIST() OVER (' + expect(String(expr)).toBe('cume_dist() OVER (' + '"base" ' + 'PARTITION BY "foo", "bar" ' - + 'ORDER BY "a", "b" DESC NULLS LAST ' + + 'ORDER BY "a", "b" DESC ' + 'ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)' ); }); + it('support parameterized expressions', () => { const col = stubParam(column('bar')); expect(isParamLike(col)).toBe(true); - const expr = cume_dist(col); - expect(isSQLExpression(expr)).toBe(true); - expect(isParamLike(expr)).toBe(true); - expect(String(expr)).toBe('CUME_DIST("bar") OVER ()'); - expect(expr.column).toBe('bar'); - expect(expr.columns).toEqual(['bar']); - - expr.addEventListener('value', value => { - expect(isSQLExpression(value)).toBe(true); - expect(String(expr)).toBe(`${value}`); - }); + const expr = first_value(col); + expect(String(expr)).toBe('first_value("bar") OVER ()'); + expect(columns(expr)).toStrictEqual(['bar']); + col.update(column('baz')); - expect(String(expr)).toBe('CUME_DIST("baz") OVER ()'); - expect(expr.column).toBe('baz'); - expect(expr.columns).toEqual(['baz']); + expect(String(expr)).toBe('first_value("baz") OVER ()'); + expect(columns(expr)).toStrictEqual(['baz']); }); - it('include ROW_NUMBER', () => { - expect(String(row_number())).toBe('(ROW_NUMBER() OVER ())::INTEGER'); + + it('include row_number', () => { + expect(String(row_number())).toBe('row_number() OVER ()'); }); - it('include RANK', () => { - expect(String(rank())).toBe('(RANK() OVER ())::INTEGER'); + + it('include rank', () => { + expect(String(rank())).toBe('rank() OVER ()'); }); - it('include DENSE_RANK', () => { - expect(String(dense_rank())).toBe('(DENSE_RANK() OVER ())::INTEGER'); + + it('include dense_rank', () => { + expect(String(dense_rank())).toBe('dense_rank() OVER ()'); }); - it('include PERCENT_RANK', () => { - expect(String(percent_rank())).toBe('PERCENT_RANK() OVER ()'); + + it('include percent_rank', () => { + expect(String(percent_rank())).toBe('percent_rank() OVER ()'); }); - it('include CUME_DIST', () => { - expect(String(cume_dist())).toBe('CUME_DIST() OVER ()'); + + it('include cume_dist', () => { + expect(String(cume_dist())).toBe('cume_dist() OVER ()'); }); - it('include NTILE', () => { - expect(String(ntile(5))).toBe('NTILE(5) OVER ()'); + + it('include ntile', () => { + expect(String(ntile(5))).toBe('ntile(5) OVER ()'); }); - it('include LAG', () => { - expect(String(lag('foo', 2))).toBe('LAG("foo", 2) OVER ()'); + + it('include lag', () => { + expect(String(lag('foo', 2))).toBe('lag("foo", 2) OVER ()'); }); - it('include LEAD', () => { - expect(String(lead('foo', 2))).toBe('LEAD("foo", 2) OVER ()'); + + it('include lead', () => { + expect(String(lead('foo', 2))).toBe('lead("foo", 2) OVER ()'); }); - it('include FIRST_VALUE', () => { - expect(String(first_value('foo'))).toBe('FIRST_VALUE("foo") OVER ()'); + + it('include first_value', () => { + expect(String(first_value('foo'))).toBe('first_value("foo") OVER ()'); }); - it('include LAST_VALUE', () => { - expect(String(last_value('foo'))).toBe('LAST_VALUE("foo") OVER ()'); + + it('include last_value', () => { + expect(String(last_value('foo'))).toBe('last_value("foo") OVER ()'); }); - it('include NTH_VALUE', () => { - expect(String(nth_value('foo', 2))).toBe('NTH_VALUE("foo", 2) OVER ()'); + + it('include nth_value', () => { + expect(String(nth_value('foo', 2))).toBe('nth_value("foo", 2) OVER ()'); }); }); diff --git a/packages/sql/tsconfig.json b/packages/sql/tsconfig.json index a5bc162c..f474f1c0 100644 --- a/packages/sql/tsconfig.json +++ b/packages/sql/tsconfig.json @@ -1,11 +1,12 @@ { - "include": ["src/**/*.js", "src/**/*.ts"], - "compilerOptions": { - "allowJs": true, - "declaration": true, - "emitDeclarationOnly": true, - "outDir": "dist/types", - "module": "node16", - "skipLibCheck": true - } - } \ No newline at end of file + "include": ["src/index-types.ts"], + "compilerOptions": { + "allowJs": true, + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "dist/types", + "module": "node16", + "skipLibCheck": true, + "types": [] + } +} \ No newline at end of file diff --git a/packages/vgplot/src/api.js b/packages/vgplot/src/api.js index 568f3ab5..9b791317 100644 --- a/packages/vgplot/src/api.js +++ b/packages/vgplot/src/api.js @@ -11,13 +11,13 @@ export { export { Query, - agg, sql, column, literal, cast, - castDouble, - castInteger, + float32, + float64, + int32, argmax, argmin, arrayAgg, From b0b16cdf6beaaf6629edd54e315c661aad5b12b0 Mon Sep 17 00:00:00 2001 From: jheer Date: Fri, 8 Nov 2024 14:17:14 -0800 Subject: [PATCH 02/12] chore: Clean up DuckDB load methods. --- packages/duckdb/src/load/csv.js | 5 ++--- packages/duckdb/src/load/json.js | 5 ++--- packages/duckdb/src/load/parquet.js | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/duckdb/src/load/csv.js b/packages/duckdb/src/load/csv.js index 5b5479b1..b296d2f4 100644 --- a/packages/duckdb/src/load/csv.js +++ b/packages/duckdb/src/load/csv.js @@ -1,6 +1,5 @@ -import { loadCSV as loadCSVSQL } from "@uwdata/mosaic-sql"; +import { loadCSV as loadCSVSQL } from '@uwdata/mosaic-sql'; export function loadCSV(db, tableName, fileName, options = {}) { - const query = loadCSVSQL(tableName, fileName, options) - return db.exec(query) + return db.exec(loadCSVSQL(tableName, fileName, options)); } diff --git a/packages/duckdb/src/load/json.js b/packages/duckdb/src/load/json.js index cb252e39..301c6c8b 100644 --- a/packages/duckdb/src/load/json.js +++ b/packages/duckdb/src/load/json.js @@ -1,6 +1,5 @@ -import { loadJSON as loadJSONSQL } from "@uwdata/mosaic-sql"; +import { loadJSON as loadJSONSQL } from '@uwdata/mosaic-sql'; export function loadJSON(db, tableName, fileName, options = {}) { - const query = loadJSONSQL(tableName, fileName, options) - return db.exec(query) + return db.exec(loadJSONSQL(tableName, fileName, options)); } diff --git a/packages/duckdb/src/load/parquet.js b/packages/duckdb/src/load/parquet.js index 12466b6e..4f92220b 100644 --- a/packages/duckdb/src/load/parquet.js +++ b/packages/duckdb/src/load/parquet.js @@ -1,6 +1,5 @@ -import { loadParquet as loadParquetSQL } from "@uwdata/mosaic-sql"; +import { loadParquet as loadParquetSQL } from '@uwdata/mosaic-sql'; export function loadParquet(db, tableName, fileName, options = {}) { - const query = loadParquetSQL(tableName, fileName, options) - return db.exec(query) + return db.exec(loadParquetSQL(tableName, fileName, options)); } From 8cffb36bdfac608215664d7c0ab15eb5baf080ad Mon Sep 17 00:00:00 2001 From: jheer Date: Fri, 8 Nov 2024 15:08:35 -0800 Subject: [PATCH 03/12] chore: Remove extraneous bin filters. --- packages/sql/src/transforms/bin-linear-1d.js | 5 ++--- packages/sql/src/transforms/bin-linear-2d.js | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/sql/src/transforms/bin-linear-1d.js b/packages/sql/src/transforms/bin-linear-1d.js index a4f08139..9a0e02e2 100644 --- a/packages/sql/src/transforms/bin-linear-1d.js +++ b/packages/sql/src/transforms/bin-linear-1d.js @@ -2,7 +2,7 @@ import { Query } from '../ast/query.js'; import { sum } from '../functions/aggregate.js'; import { int32 } from '../functions/cast.js'; import { floor } from '../functions/numeric.js'; -import { add, gt, mul, sub } from '../functions/operators.js'; +import { add, mul, sub } from '../functions/operators.js'; /** * Perform linear binning in one dimension. @@ -21,6 +21,5 @@ export function binLinear1d(query, x, weight) { query.clone().select({ i: int32(p1), w: w(sub(x, p0)) }) )) .select({ index: 'i', density: sum('w') }) - .groupby('index') - .having(gt('density', 0)); + .groupby('index'); } diff --git a/packages/sql/src/transforms/bin-linear-2d.js b/packages/sql/src/transforms/bin-linear-2d.js index 63891c33..e07edb29 100644 --- a/packages/sql/src/transforms/bin-linear-2d.js +++ b/packages/sql/src/transforms/bin-linear-2d.js @@ -2,7 +2,7 @@ import { Query, SelectQuery } from '../ast/query.js'; import { sum } from '../functions/aggregate.js'; import { int32 } from '../functions/cast.js'; import { floor } from '../functions/numeric.js'; -import { add, mul, neq, sub } from '../functions/operators.js'; +import { add, mul, sub } from '../functions/operators.js'; /** * Identity function. @@ -66,6 +66,5 @@ export function binLinear2d(q, xp, yp, weight, xn, groupby) { subq(index(xv, yv), w(mul(xpu, ypu))) )) .select({ index: 'i', density: sum('w') }, groupby) - .groupby('index', groupby) - .having(neq('density', 0)); + .groupby('index', groupby); } From 4988285a72a231ce964f1c49092d9c263672d1e3 Mon Sep 17 00:00:00 2001 From: jheer Date: Fri, 8 Nov 2024 15:08:48 -0800 Subject: [PATCH 04/12] chore: Appease TypeScript. --- packages/plot/src/interactors/util/get-field.js | 2 ++ packages/plot/src/marks/Grid2DMark.js | 4 +++- packages/plot/src/marks/HexbinMark.js | 16 ++++++++-------- packages/plot/src/marks/Mark.js | 4 ++-- packages/plot/src/marks/util/extent.js | 7 ++++--- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/packages/plot/src/interactors/util/get-field.js b/packages/plot/src/interactors/util/get-field.js index c6b37c1f..8f181f87 100644 --- a/packages/plot/src/interactors/util/get-field.js +++ b/packages/plot/src/interactors/util/get-field.js @@ -3,8 +3,10 @@ import { isNode } from '@uwdata/mosaic-sql'; function extractField(field) { if (isNode(field)) { if (field.type === 'COLUMN_REF') { + // @ts-ignore return field.column; } else if (field.type === 'AGGREGATE') { + // @ts-ignore return field.args[0] ?? field; } } diff --git a/packages/plot/src/marks/Grid2DMark.js b/packages/plot/src/marks/Grid2DMark.js index ef3b7cda..664c8ae5 100644 --- a/packages/plot/src/marks/Grid2DMark.js +++ b/packages/plot/src/marks/Grid2DMark.js @@ -1,6 +1,6 @@ import { interpolatorBarycentric, interpolateNearest, interpolatorRandomWalk } from '@observablehq/plot'; import { toDataColumns } from '@uwdata/mosaic-core'; -import { Query, bin2d, binLinear2d, collectColumns, count, isAggregateExpression, isBetween, lt, lte, sum } from '@uwdata/mosaic-sql'; +import { ExprNode, Query, bin2d, binLinear2d, collectColumns, count, isAggregateExpression, isBetween, lt, lte, sum } from '@uwdata/mosaic-sql'; import { Transient } from '../symbols.js'; import { binExpr } from './util/bin-expr.js'; import { dericheConfig, dericheConv2d } from './util/density.js'; @@ -94,7 +94,9 @@ export class Grid2DMark extends Mark { .from(source.table) .where(filter.concat(bounds)); + /** @type {string[]} */ const groupby = this.groupby = []; + /** @type {Record} */ const aggrMap = {}; for (const c of channels) { if (Object.hasOwn(c, 'field')) { diff --git a/packages/plot/src/marks/HexbinMark.js b/packages/plot/src/marks/HexbinMark.js index bcf91ea4..c0e9aa1f 100644 --- a/packages/plot/src/marks/HexbinMark.js +++ b/packages/plot/src/marks/HexbinMark.js @@ -1,4 +1,4 @@ -import { Query, abs, add, and, bitAnd, column, cond, div, float64, gt, int32, isAggregateExpression, isNotNull, lt, mul, neq, pow, round, sub } from '@uwdata/mosaic-sql'; +import { Query, abs, add, and, bitAnd, cond, div, float64, gt, int32, isAggregateExpression, isNotNull, lt, mul, neq, pow, round, sub } from '@uwdata/mosaic-sql'; import { Transient } from '../symbols.js'; import { extentX, extentY, xyext } from './util/extent.js'; import { Mark } from './Mark.js'; @@ -60,13 +60,13 @@ export class HexbinMark extends Mark { const yr = float64(plot.innerHeight() / (y2 - y1)); // column references - const x = column('_x'); - const y = column('_y'); - const px = column('_px'); - const py = column('_py'); - const pi = column('_pi'); - const pj = column('_pj'); - const tt = column('_tt'); + const x ='_x'; + const y = '_y'; + const px = '_px'; + const py = '_py'; + const pi = '_pi'; + const pj = '_pj'; + const tt = '_tt'; // Top-level query maps from screen space back to data values. // Doing so ensures that Plot generates correct data-driven scales. diff --git a/packages/plot/src/marks/Mark.js b/packages/plot/src/marks/Mark.js index 36347c2a..e22b8510 100644 --- a/packages/plot/src/marks/Mark.js +++ b/packages/plot/src/marks/Mark.js @@ -1,5 +1,5 @@ import { MosaicClient, toDataColumns } from '@uwdata/mosaic-core'; -import { Query, collectParams, column, isAggregateExpression, isColumnRef, isNode, isParamLike } from '@uwdata/mosaic-sql'; +import { Query, SelectQuery, collectParams, column, isAggregateExpression, isColumnRef, isNode, isParamLike } from '@uwdata/mosaic-sql'; import { isColor } from './util/is-color.js'; import { isConstantOption } from './util/is-constant-option.js'; import { isSymbol } from './util/is-symbol.js'; @@ -207,7 +207,7 @@ export function channelOption(c, columns) { * @param {*} table the table to query. * @param {*} skip an optional array of channels to skip. * Mark subclasses can skip channels that require special handling. - * @returns {Query} a Query instance + * @returns {SelectQuery} a Query instance */ export function markQuery(channels, table, skip = []) { const q = Query.from({ source: table }); diff --git a/packages/plot/src/marks/util/extent.js b/packages/plot/src/marks/util/extent.js index 08695e24..fcc008d8 100644 --- a/packages/plot/src/marks/util/extent.js +++ b/packages/plot/src/marks/util/extent.js @@ -1,6 +1,6 @@ import { scaleLinear } from 'd3'; import { Fixed, Transient } from '../../symbols.js'; -import { isNode, walk } from '@uwdata/mosaic-sql'; +import { BetweenOpNode, walk } from '@uwdata/mosaic-sql'; export const xext = { x: ['min', 'max'] }; export const yext = { y: ['min', 'max'] }; @@ -39,8 +39,9 @@ export function filteredExtent(filter, column) { let hi; [filter].flat().forEach(p => walk(p, (node) => { - if (node.type === 'BETWEEN' && `${node.expr}` === column) { - const extent = (node.extent ?? []).map(v => isNode(v) ? v.value : v); + if (node instanceof BetweenOpNode && `${node.expr}` === column) { + // @ts-ignore + const extent = (node.extent ?? []).map(v => v?.value ?? v); if (lo == null || extent[0] < lo) lo = extent[0]; if (hi == null || extent[1] > hi) hi = extent[1]; } From e27b715d9ac2ed7fcdfea68438a66706721d8448 Mon Sep 17 00:00:00 2001 From: jheer Date: Fri, 8 Nov 2024 15:13:48 -0800 Subject: [PATCH 05/12] fix: Use avg, not mean, in vega-example. --- packages/vega-example/src/vega.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vega-example/src/vega.js b/packages/vega-example/src/vega.js index 9ace9ef3..eecb638d 100644 --- a/packages/vega-example/src/vega.js +++ b/packages/vega-example/src/vega.js @@ -1,5 +1,5 @@ import { MosaicClient } from '@uwdata/mosaic-core'; -import { Query, dateMonth, isBetween, literal, mean } from '@uwdata/mosaic-sql'; +import { Query, avg, dateMonth, isBetween, literal } from '@uwdata/mosaic-sql'; /** @type {import('vega-lite').TopLevelSpec} */ export const spec = { @@ -95,7 +95,7 @@ export class SelectionVegaClient extends MosaicClient { query(filter = []) { return Query.select({ date: dateMonth("date"), - precipitation: mean("precipitation"), + precipitation: avg("precipitation"), }) .from(this.table) .groupby(dateMonth("date")) @@ -118,7 +118,7 @@ export class FilteredVegaClient extends MosaicClient { } query(filter = []) { - return Query.select({ precipitation: mean("precipitation") }) + return Query.select({ precipitation: avg("precipitation") }) .from(this.table) .where(filter); } From e76e788066e7e94ac45e1c459f57ebd376658c40 Mon Sep 17 00:00:00 2001 From: jheer Date: Fri, 8 Nov 2024 15:23:13 -0800 Subject: [PATCH 06/12] chore: Clean up field label inference. --- packages/plot/src/plot-renderer.js | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/packages/plot/src/plot-renderer.js b/packages/plot/src/plot-renderer.js index 5aba7f3b..94ff57d5 100644 --- a/packages/plot/src/plot-renderer.js +++ b/packages/plot/src/plot-renderer.js @@ -1,4 +1,3 @@ -// import { isColumnRef, isNode } from '@uwdata/mosaic-sql'; import * as Plot from '@observablehq/plot'; import { setAttributes } from './plot-attributes.js'; import { Fixed } from './symbols.js'; @@ -103,25 +102,18 @@ function inferLabel(key, spec, marks) { const fields = marks.map(mark => mark.channelField(key)?.field); if (fields.every(x => x == null)) return; // no columns found - // check for consistent columns / labels - let candCol; - let candLabel; + // check for consistent label + let candidate; for (let i = 0; i < fields.length; ++i) { - // const { column, label = `${fields[i]}` } = fields[i] || {}; - let column; const label = fieldLabel(fields[i]); - if (column === undefined && label === undefined) { + if (label === undefined) { continue; - } else if (candCol === undefined && candLabel === undefined) { - candCol = column; - candLabel = label; - } else if (candLabel !== label) { - candLabel = undefined; - } else if (candCol !== column) { - candCol = undefined; + } else if (candidate === undefined) { + candidate = label; + } else if (candidate !== label) { + candidate = undefined; } } - let candidate = candLabel || candCol; if (candidate === undefined) return; // add label to spec From f246b881d4f0d930c104582a3c730792a3be63d9 Mon Sep 17 00:00:00 2001 From: jheer Date: Sat, 9 Nov 2024 10:35:32 -0800 Subject: [PATCH 07/12] fix: Push HAVING clause to output query when pre-aggregating. --- packages/core/src/preagg/PreAggregator.js | 5 ++++- packages/sql/src/transforms/bin-linear-1d.js | 5 +++-- packages/sql/src/transforms/bin-linear-2d.js | 5 +++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/core/src/preagg/PreAggregator.js b/packages/core/src/preagg/PreAggregator.js index 985da608..2cfc9ebf 100644 --- a/packages/core/src/preagg/PreAggregator.js +++ b/packages/core/src/preagg/PreAggregator.js @@ -307,8 +307,10 @@ function preaggregateInfo(clientQuery, active, preaggCols, schema) { subqueryPushdown(subq, cols); } - // push orderby criteria to later queries + // push any having or orderby criteria to output queries + const having = query._having; const order = query._orderby; + query._having = []; query._orderby = []; // generate creation query string and hash id @@ -321,6 +323,7 @@ function preaggregateInfo(clientQuery, active, preaggCols, schema) { .select(group, output) .from(table) .groupby(group) + .having(having) .orderby(order); return new PreAggregateInfo({ table, create, active, select }); diff --git a/packages/sql/src/transforms/bin-linear-1d.js b/packages/sql/src/transforms/bin-linear-1d.js index 9a0e02e2..26996544 100644 --- a/packages/sql/src/transforms/bin-linear-1d.js +++ b/packages/sql/src/transforms/bin-linear-1d.js @@ -2,7 +2,7 @@ import { Query } from '../ast/query.js'; import { sum } from '../functions/aggregate.js'; import { int32 } from '../functions/cast.js'; import { floor } from '../functions/numeric.js'; -import { add, mul, sub } from '../functions/operators.js'; +import { add, mul, neq, sub } from '../functions/operators.js'; /** * Perform linear binning in one dimension. @@ -21,5 +21,6 @@ export function binLinear1d(query, x, weight) { query.clone().select({ i: int32(p1), w: w(sub(x, p0)) }) )) .select({ index: 'i', density: sum('w') }) - .groupby('index'); + .groupby('index') + .having(neq('density', 0)); } diff --git a/packages/sql/src/transforms/bin-linear-2d.js b/packages/sql/src/transforms/bin-linear-2d.js index e07edb29..63891c33 100644 --- a/packages/sql/src/transforms/bin-linear-2d.js +++ b/packages/sql/src/transforms/bin-linear-2d.js @@ -2,7 +2,7 @@ import { Query, SelectQuery } from '../ast/query.js'; import { sum } from '../functions/aggregate.js'; import { int32 } from '../functions/cast.js'; import { floor } from '../functions/numeric.js'; -import { add, mul, sub } from '../functions/operators.js'; +import { add, mul, neq, sub } from '../functions/operators.js'; /** * Identity function. @@ -66,5 +66,6 @@ export function binLinear2d(q, xp, yp, weight, xn, groupby) { subq(index(xv, yv), w(mul(xpu, ypu))) )) .select({ index: 'i', density: sum('w') }, groupby) - .groupby('index', groupby); + .groupby('index', groupby) + .having(neq('density', 0)); } From 9cb25ffcb35a4b1c175ab33557a4b86221751464 Mon Sep 17 00:00:00 2001 From: jheer Date: Thu, 14 Nov 2024 13:06:37 -0800 Subject: [PATCH 08/12] docs: Minor example spec updates. --- docs/public/specs/esm/bias.js | 2 +- docs/public/specs/esm/moving-average.js | 1 + docs/public/specs/esm/normalize.js | 4 ++-- docs/public/specs/json/bias.json | 4 ++-- docs/public/specs/json/moving-average.json | 1 + docs/public/specs/json/normalize.json | 4 ++-- docs/public/specs/yaml/bias.yaml | 4 ++-- docs/public/specs/yaml/moving-average.yaml | 1 + docs/public/specs/yaml/normalize.yaml | 4 ++-- specs/esm/bias.js | 2 +- specs/esm/moving-average.js | 1 + specs/esm/normalize.js | 4 ++-- specs/json/bias.json | 4 ++-- specs/json/moving-average.json | 1 + specs/json/normalize.json | 4 ++-- specs/ts/bias.ts | 4 ++-- specs/ts/moving-average.ts | 1 + specs/ts/normalize.ts | 4 ++-- specs/yaml/bias.yaml | 4 ++-- specs/yaml/moving-average.yaml | 1 + specs/yaml/normalize.yaml | 4 ++-- 21 files changed, 33 insertions(+), 26 deletions(-) diff --git a/docs/public/specs/esm/bias.js b/docs/public/specs/esm/bias.js index 2c58c94c..fc5f49ef 100644 --- a/docs/public/specs/esm/bias.js +++ b/docs/public/specs/esm/bias.js @@ -7,7 +7,7 @@ await vg.coordinator().exec([ const $point = vg.Param.value(0); export default vg.vconcat( - vg.slider({label: "Bias", as: $point, min: 1, max: 1000, step: 0.1}), + vg.slider({label: "Bias", as: $point, min: 0, max: 1000, step: 1}), vg.plot( vg.areaY( vg.from("walk"), diff --git a/docs/public/specs/esm/moving-average.js b/docs/public/specs/esm/moving-average.js index c491508a..0d2e6da3 100644 --- a/docs/public/specs/esm/moving-average.js +++ b/docs/public/specs/esm/moving-average.js @@ -21,6 +21,7 @@ export default vg.vconcat( stroke: "currentColor" } ), + vg.xLabel("day"), vg.width(680), vg.height(300) ), diff --git a/docs/public/specs/esm/normalize.js b/docs/public/specs/esm/normalize.js index 555953ab..3450e1af 100644 --- a/docs/public/specs/esm/normalize.js +++ b/docs/public/specs/esm/normalize.js @@ -14,7 +14,7 @@ export default vg.plot( vg.from("labels"), { x: "Date", - y: vg.sql`Close / (SELECT MAX(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = ${$point})`, + y: vg.sql`Close / (SELECT max(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = ${$point})`, dx: 2, text: "Symbol", fill: "Symbol", @@ -25,7 +25,7 @@ export default vg.plot( vg.from("stocks"), { x: "Date", - y: vg.sql`Close / (SELECT MAX(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = ${$point})`, + y: vg.sql`Close / (SELECT max(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = ${$point})`, stroke: "Symbol" } ), diff --git a/docs/public/specs/json/bias.json b/docs/public/specs/json/bias.json index a5110966..fededf33 100644 --- a/docs/public/specs/json/bias.json +++ b/docs/public/specs/json/bias.json @@ -16,9 +16,9 @@ "input": "slider", "label": "Bias", "as": "$point", - "min": 1, + "min": 0, "max": 1000, - "step": 0.1 + "step": 1 }, { "plot": [ diff --git a/docs/public/specs/json/moving-average.json b/docs/public/specs/json/moving-average.json index abd94d32..530d698b 100644 --- a/docs/public/specs/json/moving-average.json +++ b/docs/public/specs/json/moving-average.json @@ -48,6 +48,7 @@ "stroke": "currentColor" } ], + "xLabel": "day", "width": 680, "height": 300 }, diff --git a/docs/public/specs/json/normalize.json b/docs/public/specs/json/normalize.json index 65ba81ae..d1a68961 100644 --- a/docs/public/specs/json/normalize.json +++ b/docs/public/specs/json/normalize.json @@ -34,7 +34,7 @@ }, "x": "Date", "y": { - "sql": "Close / (SELECT MAX(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point)" + "sql": "Close / (SELECT max(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point)" }, "dx": 2, "text": "Symbol", @@ -48,7 +48,7 @@ }, "x": "Date", "y": { - "sql": "Close / (SELECT MAX(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point)" + "sql": "Close / (SELECT max(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point)" }, "stroke": "Symbol" }, diff --git a/docs/public/specs/yaml/bias.yaml b/docs/public/specs/yaml/bias.yaml index 8bd72225..cc691dcc 100644 --- a/docs/public/specs/yaml/bias.yaml +++ b/docs/public/specs/yaml/bias.yaml @@ -11,9 +11,9 @@ vconcat: - input: slider label: Bias as: $point - min: 1 + min: 0 max: 1000 - step: 0.1 + step: 1 - plot: - mark: areaY data: { from: walk } diff --git a/docs/public/specs/yaml/moving-average.yaml b/docs/public/specs/yaml/moving-average.yaml index f818639f..5d2481ec 100644 --- a/docs/public/specs/yaml/moving-average.yaml +++ b/docs/public/specs/yaml/moving-average.yaml @@ -24,6 +24,7 @@ vconcat: y: { avg: cases, orderby: day, rows: $frame } curve: monotone-x stroke: currentColor + xLabel: day width: 680 height: 300 - input: menu diff --git a/docs/public/specs/yaml/normalize.yaml b/docs/public/specs/yaml/normalize.yaml index eeba8414..bff51adc 100644 --- a/docs/public/specs/yaml/normalize.yaml +++ b/docs/public/specs/yaml/normalize.yaml @@ -24,7 +24,7 @@ plot: - mark: text data: { from: labels } x: Date - y: { sql: Close / (SELECT MAX(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point) } + y: { sql: Close / (SELECT max(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point) } dx: 2 text: Symbol fill: Symbol @@ -32,7 +32,7 @@ plot: - mark: lineY data: { from: stocks } x: Date - y: { sql: Close / (SELECT MAX(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point) } + y: { sql: Close / (SELECT max(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point) } stroke: Symbol - select: nearestX as: $point diff --git a/specs/esm/bias.js b/specs/esm/bias.js index 2c58c94c..fc5f49ef 100644 --- a/specs/esm/bias.js +++ b/specs/esm/bias.js @@ -7,7 +7,7 @@ await vg.coordinator().exec([ const $point = vg.Param.value(0); export default vg.vconcat( - vg.slider({label: "Bias", as: $point, min: 1, max: 1000, step: 0.1}), + vg.slider({label: "Bias", as: $point, min: 0, max: 1000, step: 1}), vg.plot( vg.areaY( vg.from("walk"), diff --git a/specs/esm/moving-average.js b/specs/esm/moving-average.js index c491508a..0d2e6da3 100644 --- a/specs/esm/moving-average.js +++ b/specs/esm/moving-average.js @@ -21,6 +21,7 @@ export default vg.vconcat( stroke: "currentColor" } ), + vg.xLabel("day"), vg.width(680), vg.height(300) ), diff --git a/specs/esm/normalize.js b/specs/esm/normalize.js index 555953ab..3450e1af 100644 --- a/specs/esm/normalize.js +++ b/specs/esm/normalize.js @@ -14,7 +14,7 @@ export default vg.plot( vg.from("labels"), { x: "Date", - y: vg.sql`Close / (SELECT MAX(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = ${$point})`, + y: vg.sql`Close / (SELECT max(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = ${$point})`, dx: 2, text: "Symbol", fill: "Symbol", @@ -25,7 +25,7 @@ export default vg.plot( vg.from("stocks"), { x: "Date", - y: vg.sql`Close / (SELECT MAX(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = ${$point})`, + y: vg.sql`Close / (SELECT max(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = ${$point})`, stroke: "Symbol" } ), diff --git a/specs/json/bias.json b/specs/json/bias.json index a4ddbefa..80642a2f 100644 --- a/specs/json/bias.json +++ b/specs/json/bias.json @@ -17,9 +17,9 @@ "input": "slider", "label": "Bias", "as": "$point", - "min": 1, + "min": 0, "max": 1000, - "step": 0.1 + "step": 1 }, { "plot": [ diff --git a/specs/json/moving-average.json b/specs/json/moving-average.json index ff889e0f..9ecb609c 100644 --- a/specs/json/moving-average.json +++ b/specs/json/moving-average.json @@ -49,6 +49,7 @@ "stroke": "currentColor" } ], + "xLabel": "day", "width": 680, "height": 300 }, diff --git a/specs/json/normalize.json b/specs/json/normalize.json index 08a6a033..eed4e5d3 100644 --- a/specs/json/normalize.json +++ b/specs/json/normalize.json @@ -38,7 +38,7 @@ }, "x": "Date", "y": { - "sql": "Close / (SELECT MAX(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point)" + "sql": "Close / (SELECT max(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point)" }, "dx": 2, "text": "Symbol", @@ -52,7 +52,7 @@ }, "x": "Date", "y": { - "sql": "Close / (SELECT MAX(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point)" + "sql": "Close / (SELECT max(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point)" }, "stroke": "Symbol" }, diff --git a/specs/ts/bias.ts b/specs/ts/bias.ts index c611e47a..0f13bd60 100644 --- a/specs/ts/bias.ts +++ b/specs/ts/bias.ts @@ -18,9 +18,9 @@ export const spec : Spec = { "input": "slider", "label": "Bias", "as": "$point", - "min": 1, + "min": 0, "max": 1000, - "step": 0.1 + "step": 1 }, { "plot": [ diff --git a/specs/ts/moving-average.ts b/specs/ts/moving-average.ts index 2ecb6258..4ec9dc4b 100644 --- a/specs/ts/moving-average.ts +++ b/specs/ts/moving-average.ts @@ -50,6 +50,7 @@ export const spec : Spec = { "stroke": "currentColor" } ], + "xLabel": "day", "width": 680, "height": 300 }, diff --git a/specs/ts/normalize.ts b/specs/ts/normalize.ts index 254b1861..0b29f29c 100644 --- a/specs/ts/normalize.ts +++ b/specs/ts/normalize.ts @@ -36,7 +36,7 @@ export const spec : Spec = { }, "x": "Date", "y": { - "sql": "Close / (SELECT MAX(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point)" + "sql": "Close / (SELECT max(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point)" }, "dx": 2, "text": "Symbol", @@ -50,7 +50,7 @@ export const spec : Spec = { }, "x": "Date", "y": { - "sql": "Close / (SELECT MAX(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point)" + "sql": "Close / (SELECT max(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point)" }, "stroke": "Symbol" }, diff --git a/specs/yaml/bias.yaml b/specs/yaml/bias.yaml index 8bd72225..cc691dcc 100644 --- a/specs/yaml/bias.yaml +++ b/specs/yaml/bias.yaml @@ -11,9 +11,9 @@ vconcat: - input: slider label: Bias as: $point - min: 1 + min: 0 max: 1000 - step: 0.1 + step: 1 - plot: - mark: areaY data: { from: walk } diff --git a/specs/yaml/moving-average.yaml b/specs/yaml/moving-average.yaml index f818639f..5d2481ec 100644 --- a/specs/yaml/moving-average.yaml +++ b/specs/yaml/moving-average.yaml @@ -24,6 +24,7 @@ vconcat: y: { avg: cases, orderby: day, rows: $frame } curve: monotone-x stroke: currentColor + xLabel: day width: 680 height: 300 - input: menu diff --git a/specs/yaml/normalize.yaml b/specs/yaml/normalize.yaml index eeba8414..bff51adc 100644 --- a/specs/yaml/normalize.yaml +++ b/specs/yaml/normalize.yaml @@ -24,7 +24,7 @@ plot: - mark: text data: { from: labels } x: Date - y: { sql: Close / (SELECT MAX(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point) } + y: { sql: Close / (SELECT max(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point) } dx: 2 text: Symbol fill: Symbol @@ -32,7 +32,7 @@ plot: - mark: lineY data: { from: stocks } x: Date - y: { sql: Close / (SELECT MAX(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point) } + y: { sql: Close / (SELECT max(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point) } stroke: Symbol - select: nearestX as: $point From ea8e3a5c7e36c2cb21d04e25121fff1239b00bfd Mon Sep 17 00:00:00 2001 From: jheer Date: Thu, 14 Nov 2024 18:57:49 -0800 Subject: [PATCH 09/12] fix: Update package-lock.json. --- package-lock.json | 4945 ++++++++++++++++----------------------------- 1 file changed, 1714 insertions(+), 3231 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4e22f976..939fb8a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,44 +26,35 @@ "yaml": "^2.6.0" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@algolia/autocomplete-core": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", - "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", + "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", "dev": true, "dependencies": { - "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", - "@algolia/autocomplete-shared": "1.9.3" + "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", + "@algolia/autocomplete-shared": "1.17.7" } }, "node_modules/@algolia/autocomplete-plugin-algolia-insights": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", - "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", + "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", "dev": true, "dependencies": { - "@algolia/autocomplete-shared": "1.9.3" + "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "node_modules/@algolia/autocomplete-preset-algolia": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", - "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", + "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", "dev": true, "dependencies": { - "@algolia/autocomplete-shared": "1.9.3" + "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", @@ -71,280 +62,195 @@ } }, "node_modules/@algolia/autocomplete-shared": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", - "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", + "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", "dev": true, "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, - "node_modules/@algolia/cache-browser-local-storage": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.24.0.tgz", - "integrity": "sha512-t63W9BnoXVrGy9iYHBgObNXqYXM3tYXCjDSHeNwnsc324r4o5UiVKUiAB4THQ5z9U5hTj6qUvwg/Ez43ZD85ww==", - "dev": true, - "dependencies": { - "@algolia/cache-common": "4.24.0" - } - }, - "node_modules/@algolia/cache-common": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.24.0.tgz", - "integrity": "sha512-emi+v+DmVLpMGhp0V9q9h5CdkURsNmFC+cOS6uK9ndeJm9J4TiqSvPYVu+THUP8P/S08rxf5x2P+p3CfID0Y4g==", - "dev": true - }, - "node_modules/@algolia/cache-in-memory": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.24.0.tgz", - "integrity": "sha512-gDrt2so19jW26jY3/MkFg5mEypFIPbPoXsQGQWAi6TrCPsNOSEYepBMPlucqWigsmEy/prp5ug2jy/N3PVG/8w==", - "dev": true, - "dependencies": { - "@algolia/cache-common": "4.24.0" - } - }, - "node_modules/@algolia/client-account": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.24.0.tgz", - "integrity": "sha512-adcvyJ3KjPZFDybxlqnf+5KgxJtBjwTPTeyG2aOyoJvx0Y8dUQAEOEVOJ/GBxX0WWNbmaSrhDURMhc+QeevDsA==", - "dev": true, - "dependencies": { - "@algolia/client-common": "4.24.0", - "@algolia/client-search": "4.24.0", - "@algolia/transporter": "4.24.0" - } - }, - "node_modules/@algolia/client-account/node_modules/@algolia/client-common": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", - "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", - "dev": true, - "dependencies": { - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" - } - }, - "node_modules/@algolia/client-account/node_modules/@algolia/client-search": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", - "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "node_modules/@algolia/client-abtesting": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.14.0.tgz", + "integrity": "sha512-HR4kbCmq4RO8vhafLrVcR11q3BvuPYA4o+Nn8hzJRgpDu2fauIlgIBgVDsoxaK90xuaPLSNdoT5tWXag+L8vCw==", "dev": true, "dependencies": { - "@algolia/client-common": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/client-analytics": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.24.0.tgz", - "integrity": "sha512-y8jOZt1OjwWU4N2qr8G4AxXAzaa8DBvyHTWlHzX/7Me1LX8OayfgHexqrsL4vSBcoMmVw2XnVW9MhL+Y2ZDJXg==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.14.0.tgz", + "integrity": "sha512-EnmouGUQdIvwmI8plglt3HP9hXwNNwCJshszfU/Hqi2n21//iwmWLmMb5gXDfiLhyMa6u8eya8c03QT79s3/tQ==", "dev": true, "dependencies": { - "@algolia/client-common": "4.24.0", - "@algolia/client-search": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" + }, + "engines": { + "node": ">= 14.0.0" } }, - "node_modules/@algolia/client-analytics/node_modules/@algolia/client-common": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", - "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", + "node_modules/@algolia/client-common": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.14.0.tgz", + "integrity": "sha512-xYaswEqv+mTeazOJV0PELs4LYXaETYGwlntQxvOTHsICaj1e+ylKeMr+C+ZvN74RpCRDoEN3a2n33bRU9/MHTw==", "dev": true, - "dependencies": { - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" + "engines": { + "node": ">= 14.0.0" } }, - "node_modules/@algolia/client-analytics/node_modules/@algolia/client-search": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", - "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "node_modules/@algolia/client-insights": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.14.0.tgz", + "integrity": "sha512-1dWxjTmpNCgLWLl6GSAaOACs55JvioAIdno7jvq7KVfpLLXehHaSaiij8ssbbIM8HqHZPwC8ShaUHtSt2jLdBg==", "dev": true, "dependencies": { - "@algolia/client-common": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" - } - }, - "node_modules/@algolia/client-common": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.10.1.tgz", - "integrity": "sha512-WsMBmO92GMWBvhS2RHwdbgxwbEh3sEymSMMXiO48ayyR8Crvr+KvcccDCrpA/Ek2XOpMZYVkCvvfpr50yEBMAA==", - "dev": true, - "peer": true, + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" + }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-personalization": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.24.0.tgz", - "integrity": "sha512-l5FRFm/yngztweU0HdUzz1rC4yoWCFo3IF+dVIVTfEPg906eZg5BOd1k0K6rZx5JzyyoP4LdmOikfkfGsKVE9w==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.14.0.tgz", + "integrity": "sha512-HDOYm38nUwflxaemKrxlV91pYg3L9JkmLnuSQCJ7bzivqP+aBTZ8mGRvanFzwayNMRZWLuGsstJMpGET6FYaDQ==", "dev": true, "dependencies": { - "@algolia/client-common": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" + }, + "engines": { + "node": ">= 14.0.0" } }, - "node_modules/@algolia/client-personalization/node_modules/@algolia/client-common": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", - "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", + "node_modules/@algolia/client-query-suggestions": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.14.0.tgz", + "integrity": "sha512-yDPf3E3MS2RUg1br7r1+PEqKOxUftxjLLtD35yW9voZ9oV45XZnAPnHCqgmyzjcK5/dM1dzXHhmZGf4VbjYn7Q==", "dev": true, "dependencies": { - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/client-search": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.10.1.tgz", - "integrity": "sha512-Zogjyw/evyywK9XqQ5iDsTsB5rJ0kEVdIQvw0sEV5KHq0XX7JcLdCRbMz22f5NPwVoiZkxWRcsHN9FgWMkS7lg==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.14.0.tgz", + "integrity": "sha512-x5/GVLDyGad8aiWA/vfj8X4NXOZ3FlwXw/gb7t+Mxo3O0g3VxSFQdyrZ8Oduv/Y/Y8cxMVEOx1u3Azs6tlSZbg==", "dev": true, - "peer": true, "dependencies": { - "@algolia/client-common": "5.10.1", - "@algolia/requester-browser-xhr": "5.10.1", - "@algolia/requester-fetch": "5.10.1", - "@algolia/requester-node-http": "5.10.1" + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" }, "engines": { "node": ">= 14.0.0" } }, - "node_modules/@algolia/logger-common": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.24.0.tgz", - "integrity": "sha512-LLUNjkahj9KtKYrQhFKCzMx0BY3RnNP4FEtO+sBybCjJ73E8jNdaKJ/Dd8A/VA4imVHP5tADZ8pn5B8Ga/wTMA==", - "dev": true - }, - "node_modules/@algolia/logger-console": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.24.0.tgz", - "integrity": "sha512-X4C8IoHgHfiUROfoRCV+lzSy+LHMgkoEEU1BbKcsfnV0i0S20zyy0NLww9dwVHUWNfPPxdMU+/wKmLGYf96yTg==", - "dev": true, - "dependencies": { - "@algolia/logger-common": "4.24.0" - } - }, - "node_modules/@algolia/recommend": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.24.0.tgz", - "integrity": "sha512-P9kcgerfVBpfYHDfVZDvvdJv0lEoCvzNlOy2nykyt5bK8TyieYyiD0lguIJdRZZYGre03WIAFf14pgE+V+IBlw==", - "dev": true, - "dependencies": { - "@algolia/cache-browser-local-storage": "4.24.0", - "@algolia/cache-common": "4.24.0", - "@algolia/cache-in-memory": "4.24.0", - "@algolia/client-common": "4.24.0", - "@algolia/client-search": "4.24.0", - "@algolia/logger-common": "4.24.0", - "@algolia/logger-console": "4.24.0", - "@algolia/requester-browser-xhr": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/requester-node-http": "4.24.0", - "@algolia/transporter": "4.24.0" - } - }, - "node_modules/@algolia/recommend/node_modules/@algolia/client-common": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", - "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", - "dev": true, - "dependencies": { - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" - } - }, - "node_modules/@algolia/recommend/node_modules/@algolia/client-search": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", - "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "node_modules/@algolia/ingestion": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.14.0.tgz", + "integrity": "sha512-HU9AoZDFMEIT/+xzIa9l1XkPRTH7S0jWbYWrNkeb/62TxQFvL5x/XYEa6Yf/WCFU6Qa0W+ivua8NDzxL15NVGQ==", "dev": true, "dependencies": { - "@algolia/client-common": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" + }, + "engines": { + "node": ">= 14.0.0" } }, - "node_modules/@algolia/recommend/node_modules/@algolia/requester-browser-xhr": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.24.0.tgz", - "integrity": "sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==", + "node_modules/@algolia/monitoring": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.14.0.tgz", + "integrity": "sha512-tGKip5Dvusw8z4ajIJBBYxdPUOGIqV1CGat55eCaAmX97Oko2adIOq9MKvdC3d7SMuQt3j28QIHpV6wvihnsKA==", "dev": true, "dependencies": { - "@algolia/requester-common": "4.24.0" + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" + }, + "engines": { + "node": ">= 14.0.0" } }, - "node_modules/@algolia/recommend/node_modules/@algolia/requester-node-http": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz", - "integrity": "sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==", + "node_modules/@algolia/recommend": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.14.0.tgz", + "integrity": "sha512-wXOWFG4L0Y/EyWKuDXQA7FoB7Ukuss+O8zaxZSlla4h19UGWak+22RcZ2eDFoAhVOJxC8RoLg9opMfDbZtPW9Q==", "dev": true, "dependencies": { - "@algolia/requester-common": "4.24.0" + "@algolia/client-common": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.10.1.tgz", - "integrity": "sha512-HAPq25Vd2eocTiNbTV9QpI2jXZYWoq9fB4tE4EWlLPCnx6P1C7bx6kXzHkBuRE6szbMyIZW47YlsYsDU/Vslhg==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.14.0.tgz", + "integrity": "sha512-5zk1sol+WTDskAx1AMBGGDChCVBHuPTmclGZO844/ljqH7AcJpkFnfUeAMXfx2m4tW3Ax+M+uaC+XjVoQRb9Hg==", "dev": true, - "peer": true, "dependencies": { - "@algolia/client-common": "5.10.1" + "@algolia/client-common": "5.14.0" }, "engines": { "node": ">= 14.0.0" } }, - "node_modules/@algolia/requester-common": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.24.0.tgz", - "integrity": "sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==", - "dev": true - }, "node_modules/@algolia/requester-fetch": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.10.1.tgz", - "integrity": "sha512-SwmfnWdtShlXWnOZniGgWKRweRdKWRdN+kF/HM6UJE3G+2RNZKUHYzpJCWuxGkWHX9hcukBRrx8JRHj2m6+sRA==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.14.0.tgz", + "integrity": "sha512-B9grYSKH34UlJPkUdds14I/m8Yp7/a4PbqRuZsrP1L4kBW2FGinMtpQOK3N6gEy8YkVNA1iKlTC24yro8z8a8A==", "dev": true, - "peer": true, "dependencies": { - "@algolia/client-common": "5.10.1" + "@algolia/client-common": "5.14.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-node-http": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.10.1.tgz", - "integrity": "sha512-lCk2y8J2Xs812BCymenscKs+BddHYnln/NIlqC4yU5Xa7EEyU/yd68jHxBRt75cIkgaNdNW5C4WzzcNpWc44vg==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.14.0.tgz", + "integrity": "sha512-2EPhRqbxWzrsSXX0/70jIGtjQTj8VILi+uqmgBweyQIzCNlGoNbyMs+E7iwHVtUSrE/9IDd8rrewkVHOI6h2IQ==", "dev": true, - "peer": true, "dependencies": { - "@algolia/client-common": "5.10.1" + "@algolia/client-common": "5.14.0" }, "engines": { "node": ">= 14.0.0" } }, - "node_modules/@algolia/transporter": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.24.0.tgz", - "integrity": "sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA==", - "dev": true, - "dependencies": { - "@algolia/cache-common": "4.24.0", - "@algolia/logger-common": "4.24.0", - "@algolia/requester-common": "4.24.0" - } - }, "node_modules/@anywidget/types": { "version": "0.1.9", "resolved": "https://registry.npmjs.org/@anywidget/types/-/types-0.1.9.tgz", @@ -387,12 +293,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.9.tgz", - "integrity": "sha512-aI3jjAAO1fh7vY/pBGsn1i9LDbRP43+asrRlkPuTXW5yHXtd1NgTEMudbBoDDxrf1daEEfPJqR+JBMakzrR4Dg==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dev": true, "dependencies": { - "@babel/types": "^7.25.9" + "@babel/types": "^7.26.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -402,9 +308,9 @@ } }, "node_modules/@babel/types": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.9.tgz", - "integrity": "sha512-OwS2CM5KocvQ/k7dFJa8i5bNGJP0hXWfVCfDkqRFP1IreH1JDC7wG6eCYCi0+McbfT8OR/kNqsI0UU0xP9H6PQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -415,31 +321,31 @@ } }, "node_modules/@docsearch/css": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.2.tgz", - "integrity": "sha512-vKNZepO2j7MrYBTZIGXvlUOIR+v9KRf70FApRgovWrj3GTs1EITz/Xb0AOlm1xsQBp16clVZj1SY/qaOJbQtZw==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.0.tgz", + "integrity": "sha512-pieeipSOW4sQ0+bE5UFC51AOZp9NGxg89wAlZ1BAQFaiRAGK1IKUaPQ0UGZeNctJXyqZ1UvBtOQh2HH+U5GtmA==", "dev": true }, "node_modules/@docsearch/js": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.6.2.tgz", - "integrity": "sha512-pS4YZF+VzUogYrkblCucQ0Oy2m8Wggk8Kk7lECmZM60hTbaydSIhJTTiCrmoxtBqV8wxORnOqcqqOfbmkkQEcA==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.0.tgz", + "integrity": "sha512-PVuV629f5UcYRtBWqK7ID6vNL5647+2ADJypwTjfeBIrJfwPuHtzLy39hMGMfFK+0xgRyhTR0FZ83EkdEraBlg==", "dev": true, "dependencies": { - "@docsearch/react": "3.6.2", + "@docsearch/react": "3.8.0", "preact": "^10.0.0" } }, "node_modules/@docsearch/react": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.2.tgz", - "integrity": "sha512-rtZce46OOkVflCQH71IdbXSFK+S8iJZlUF56XBW5rIgx/eG5qoomC7Ag3anZson1bBac/JFQn7XOBfved/IMRA==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.0.tgz", + "integrity": "sha512-WnFK720+iwTVt94CxY3u+FgX6exb3BfN5kE9xUY6uuAH/9W/UFboBZFLlrw/zxFRHoHZCOXRtOylsXF+6LHI+Q==", "dev": true, "dependencies": { - "@algolia/autocomplete-core": "1.9.3", - "@algolia/autocomplete-preset-algolia": "1.9.3", - "@docsearch/css": "3.6.2", - "algoliasearch": "^4.19.1" + "@algolia/autocomplete-core": "1.17.7", + "@algolia/autocomplete-preset-algolia": "1.17.7", + "@docsearch/css": "3.8.0", + "algoliasearch": "^5.12.0" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 19.0.0", @@ -897,26 +803,40 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, - "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -949,7 +869,6 @@ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -973,7 +892,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -989,13 +907,12 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@eslint/js": { - "version": "9.13.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz", - "integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==", + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.14.0.tgz", + "integrity": "sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1011,9 +928,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.1.tgz", - "integrity": "sha512-HFZ4Mp26nbWk9d/BpvP0YNL6W4UoZF0VFcTw/aPPA8RpOxeFQgK+ClABGgAUXs9Y/RGX/l1vOmrqz1MQt9MNuw==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", + "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", "dev": true, "dependencies": { "levn": "^0.4.1" @@ -1028,27 +945,40 @@ "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==" }, "node_modules/@humanfs/core": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", - "integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.5", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.5.tgz", - "integrity": "sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==", + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", "dev": true, "dependencies": { - "@humanfs/core": "^0.19.0", + "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" }, "engines": { "node": ">=18.18.0" } }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -1063,9 +993,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", "dev": true, "engines": { "node": ">=18.18" @@ -1101,9 +1031,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "engines": { "node": ">=12" }, @@ -1198,9 +1128,9 @@ "dev": true }, "node_modules/@jupyter-widgets/base": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/@jupyter-widgets/base/-/base-6.0.7.tgz", - "integrity": "sha512-a4VoUtL+90mGH6VE6m78D2J9aNy9Q1JrPP91HAQTXBysVpCLTtq0Ie8EupN5Su7V8eFwl/wz91J2m0p4jY4h0g==", + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/@jupyter-widgets/base/-/base-6.0.10.tgz", + "integrity": "sha512-iJvBT4drhwd3kpfMXaIFoD+FZTqbm1pKNi8Gvv+Wggnefyw6SHugZ0hjHoBxZD362wEUM8fpHQmdj59KvXWg0g==", "dev": true, "dependencies": { "@jupyterlab/services": "^6.0.0 || ^7.0.0", @@ -1215,9 +1145,9 @@ } }, "node_modules/@jupyter/ydoc": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@jupyter/ydoc/-/ydoc-1.1.1.tgz", - "integrity": "sha512-fXx9CbUwUlXBsJo83tBQL3T0MgWT4YYz2ozcSFj0ymZSohAnI1uo7N9CPpVe4/nmc9uG1lFdlXC4XQBevi2jSA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@jupyter/ydoc/-/ydoc-3.0.0.tgz", + "integrity": "sha512-oWTSBPifD81I1oRNyKkMJF14FzNvBpJxiYHXaC1XeFXk67KNiqDepjVpYJ1E2QYThZhZGGtdNc6TC1XCQAJVKA==", "dev": true, "dependencies": { "@jupyterlab/nbformat": "^3.0.0 || ^4.0.0-alpha.21 || ^4.0.0", @@ -1229,59 +1159,59 @@ } }, "node_modules/@jupyterlab/coreutils": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/@jupyterlab/coreutils/-/coreutils-6.1.6.tgz", - "integrity": "sha512-4rTO/O2zZNRCY28+cA4wv0/mp3/KWl4kKbOUObsH2elzO9FLjPEtKOxWm/WCn/VtlkQG+/XF2J0bS5Qz/WeW7w==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/coreutils/-/coreutils-6.3.0.tgz", + "integrity": "sha512-zsoMx18JXfVEvMR4OVb+GR/AirXYEUBveySoY6/Z4Kv6vLZh2ZC+JZKgnlpPvql7D7Aa7tCUbSJdV33+fYELIQ==", "dev": true, "dependencies": { - "@lumino/coreutils": "^2.1.2", - "@lumino/disposable": "^2.1.2", - "@lumino/signaling": "^2.1.2", + "@lumino/coreutils": "^2.2.0", + "@lumino/disposable": "^2.1.3", + "@lumino/signaling": "^2.1.3", "minimist": "~1.2.0", "path-browserify": "^1.0.0", "url-parse": "~1.5.4" } }, "node_modules/@jupyterlab/nbformat": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@jupyterlab/nbformat/-/nbformat-4.1.6.tgz", - "integrity": "sha512-wh/lw8+WciC/8kB25A8Ma27VvR6aR/TcrO4pL4nqCRI61YV0s/HbVL/0xug+qnGLRCNu6AfVYNEWPY2yTQguAw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/nbformat/-/nbformat-4.3.0.tgz", + "integrity": "sha512-7XfYrCN3eF00tJq3Z+fJd+d9AmoJIRvXEcjmcwRdddUkb44jVEKxZ9LGCRZ0m4QPDCMticyrqbXQpVMJIrNDeg==", "dev": true, "dependencies": { - "@lumino/coreutils": "^2.1.2" + "@lumino/coreutils": "^2.2.0" } }, "node_modules/@jupyterlab/services": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/@jupyterlab/services/-/services-7.1.6.tgz", - "integrity": "sha512-7i/Cp03qmjQ4cS2DGe8QMTFMGnhxxty5UHEywiov+qBKB45tGnFiT830hc+ncOMR3LMOmmzyfF9t3SxMbjvTlw==", - "dev": true, - "dependencies": { - "@jupyter/ydoc": "^1.1.1", - "@jupyterlab/coreutils": "^6.1.6", - "@jupyterlab/nbformat": "^4.1.6", - "@jupyterlab/settingregistry": "^4.1.6", - "@jupyterlab/statedb": "^4.1.6", - "@lumino/coreutils": "^2.1.2", - "@lumino/disposable": "^2.1.2", - "@lumino/polling": "^2.1.2", - "@lumino/properties": "^2.0.1", - "@lumino/signaling": "^2.1.2", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/services/-/services-7.3.0.tgz", + "integrity": "sha512-u9GWFMTEUJvDszz98tIUpaBOsUGSybQjwv+263obtCjaceezy87SReIsQefoI1Dh8SGfngGW7IOvThM/LNoYGw==", + "dev": true, + "dependencies": { + "@jupyter/ydoc": "^3.0.0", + "@jupyterlab/coreutils": "^6.3.0", + "@jupyterlab/nbformat": "^4.3.0", + "@jupyterlab/settingregistry": "^4.3.0", + "@jupyterlab/statedb": "^4.3.0", + "@lumino/coreutils": "^2.2.0", + "@lumino/disposable": "^2.1.3", + "@lumino/polling": "^2.1.3", + "@lumino/properties": "^2.0.2", + "@lumino/signaling": "^2.1.3", "ws": "^8.11.0" } }, "node_modules/@jupyterlab/settingregistry": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@jupyterlab/settingregistry/-/settingregistry-4.1.6.tgz", - "integrity": "sha512-hxUM1n3O5F4/10XU6r9MnIw6Q06dWiXzWqsGCbaWaPrjiXZALV9ATBgG7ygeXK69x3KnyCq282uyT2woDtL5Fg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/settingregistry/-/settingregistry-4.3.0.tgz", + "integrity": "sha512-+/1IOaANMI35CVO67yTKAo0cVau04MH0QFeJUv9DtY88CU50O6vtfh9+gPGQnl/dTwgHZNiZbfapyLXvLuASig==", "dev": true, "dependencies": { - "@jupyterlab/nbformat": "^4.1.6", - "@jupyterlab/statedb": "^4.1.6", - "@lumino/commands": "^2.2.0", - "@lumino/coreutils": "^2.1.2", - "@lumino/disposable": "^2.1.2", - "@lumino/signaling": "^2.1.2", + "@jupyterlab/nbformat": "^4.3.0", + "@jupyterlab/statedb": "^4.3.0", + "@lumino/commands": "^2.3.1", + "@lumino/coreutils": "^2.2.0", + "@lumino/disposable": "^2.1.3", + "@lumino/signaling": "^2.1.3", "@rjsf/utils": "^5.13.4", "ajv": "^8.12.0", "json5": "^2.2.3" @@ -1291,16 +1221,16 @@ } }, "node_modules/@jupyterlab/statedb": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@jupyterlab/statedb/-/statedb-4.1.6.tgz", - "integrity": "sha512-pFFmdHWARYX/s2Uv/pz16UMQwLwrAaGqXzdTa58Z7J8V9Uwu9V6aTJmew8I1LudM/3GSJJ0Lo31/TP60V2bbwg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/statedb/-/statedb-4.3.0.tgz", + "integrity": "sha512-NyME5GIHmTwV2MLIqtxV9hMxKa0v9AjAasN6xtDqhlyFwsPd4kI1dUAlYjxJ9Cbcc+z5K3/XNoFZyErOe/JQPQ==", "dev": true, "dependencies": { - "@lumino/commands": "^2.2.0", - "@lumino/coreutils": "^2.1.2", - "@lumino/disposable": "^2.1.2", - "@lumino/properties": "^2.0.1", - "@lumino/signaling": "^2.1.2" + "@lumino/commands": "^2.3.1", + "@lumino/coreutils": "^2.2.0", + "@lumino/disposable": "^2.1.3", + "@lumino/properties": "^2.0.2", + "@lumino/signaling": "^2.1.3" } }, "node_modules/@lerna/create": { @@ -1443,30 +1373,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@lerna/create/node_modules/glob/node_modules/minipass": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", - "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@lerna/create/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@lerna/create/node_modules/minimatch": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", @@ -1479,6 +1385,15 @@ "node": "*" } }, + "node_modules/@lerna/create/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@lerna/create/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -1506,31 +1421,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@lerna/create/node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@lerna/create/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@lukeed/csprng": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", @@ -1553,9 +1443,9 @@ } }, "node_modules/@lumino/algorithm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@lumino/algorithm/-/algorithm-2.0.1.tgz", - "integrity": "sha512-iA+uuvA7DeNFB0/cQpIWNgO1c6z4pOSigifjstLy+rxf1U5ZzxIq+xudnEuTbWgKSTviG02j4cKwCyx1PO6rzA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lumino/algorithm/-/algorithm-2.0.2.tgz", + "integrity": "sha512-cI8yJ2+QK1yM5ZRU3Kuaw9fJ/64JEDZEwWWp7+U0cd/mvcZ44BGdJJ29w+tIet1QXxPAvnsUleWyQ5qm4qUouA==", "dev": true }, "node_modules/@lumino/collections": { @@ -1574,55 +1464,58 @@ "dev": true }, "node_modules/@lumino/commands": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@lumino/commands/-/commands-2.3.0.tgz", - "integrity": "sha512-qOF9p9W54IWjyXrbd9QKr0d5XIn5ZTh6PBFO4UBGvEJJPO477tDm0f36HUxMMRtdJvp5ArgTj5/Khd3L3BFayg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@lumino/commands/-/commands-2.3.1.tgz", + "integrity": "sha512-DpX1kkE4PhILpvK1T4ZnaFb6UP4+YTkdZifvN3nbiomD64O2CTd+wcWIBpZMgy6MMgbVgrE8dzHxHk1EsKxNxw==", "dev": true, "dependencies": { - "@lumino/algorithm": "^2.0.1", - "@lumino/coreutils": "^2.1.2", - "@lumino/disposable": "^2.1.2", - "@lumino/domutils": "^2.0.1", - "@lumino/keyboard": "^2.0.1", - "@lumino/signaling": "^2.1.2", - "@lumino/virtualdom": "^2.0.1" + "@lumino/algorithm": "^2.0.2", + "@lumino/coreutils": "^2.2.0", + "@lumino/disposable": "^2.1.3", + "@lumino/domutils": "^2.0.2", + "@lumino/keyboard": "^2.0.2", + "@lumino/signaling": "^2.1.3", + "@lumino/virtualdom": "^2.0.2" } }, "node_modules/@lumino/coreutils": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@lumino/coreutils/-/coreutils-2.1.2.tgz", - "integrity": "sha512-vyz7WzchTO4HQ8iVAxvSUmb5o/8t3cz1vBo8V4ZIaPGada0Jx0xe3tKQ8bXp4pjHc+AEhMnkCnlUyVYMWbnj4A==", - "dev": true + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@lumino/coreutils/-/coreutils-2.2.0.tgz", + "integrity": "sha512-x5wnQ/GjWBayJ6vXVaUi6+Q6ETDdcUiH9eSfpRZFbgMQyyM6pi6baKqJBK2CHkCc/YbAEl6ipApTgm3KOJ/I3g==", + "dev": true, + "dependencies": { + "@lumino/algorithm": "^2.0.2" + } }, "node_modules/@lumino/disposable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@lumino/disposable/-/disposable-2.1.2.tgz", - "integrity": "sha512-0qmB6zPt9+uj4SVMTfISn0wUOjYHahtKotwxDD5flfcscj2gsXaFCXO4Oqot1zcsZbg8uJmTUhEzAvFW0QhFNA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@lumino/disposable/-/disposable-2.1.3.tgz", + "integrity": "sha512-k5KXy/+T3UItiWHY4WwQawnsJnGo3aNtP5CTRKqo4+tbTNuhc3rTSvygJlNKIbEfIZXW2EWYnwfFDozkYx95eA==", "dev": true, "dependencies": { - "@lumino/signaling": "^2.1.2" + "@lumino/signaling": "^2.1.3" } }, "node_modules/@lumino/domutils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@lumino/domutils/-/domutils-2.0.1.tgz", - "integrity": "sha512-tbcfhsdKH04AMjSgYAYGD2xE80YcjrqKnfMTeU2NHt4J294Hzxs1GvEmSMk5qJ3Bbgwx6Z4BbQ7apnFg8Gc6cA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lumino/domutils/-/domutils-2.0.2.tgz", + "integrity": "sha512-2Kp6YHaMNI1rKB0PrALvOsZBHPy2EvVVAvJLWjlCm8MpWOVETjFp0MA9QpMubT9I76aKbaI5s1o1NJyZ8Y99pQ==", "dev": true }, "node_modules/@lumino/dragdrop": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@lumino/dragdrop/-/dragdrop-2.1.4.tgz", - "integrity": "sha512-/ckaYPHIZC1Ff0pU2H3WDI/Xm7V3i0XnyYG4PeZvG1+ovc0I0zeZtlb6qZXne0Vi2r8L2a0624FjF2CwwgNSnA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@lumino/dragdrop/-/dragdrop-2.1.5.tgz", + "integrity": "sha512-zqwR4GakrQBKZOW6S5pj2nfrQDurOErAoe9x3HS3BKLa1AzWA+t9PD5NESOKd81NqXFHjiMirSyFkTUs6pw+uA==", "dev": true, "dependencies": { - "@lumino/coreutils": "^2.1.2", - "@lumino/disposable": "^2.1.2" + "@lumino/coreutils": "^2.2.0", + "@lumino/disposable": "^2.1.3" } }, "node_modules/@lumino/keyboard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@lumino/keyboard/-/keyboard-2.0.1.tgz", - "integrity": "sha512-R2mrH9HCEcv/0MSAl7bEUbjCNOnhrg49nXZBEVckg//TEG+sdayCsyrbJNMPcZ07asIPKc6mq3v7DpAmDKqh+w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lumino/keyboard/-/keyboard-2.0.2.tgz", + "integrity": "sha512-icRUpvswDaFjqmAJNbQRb/aTu6Iugo6Y2oC08TiIwhQtLS9W+Ee9VofdqvbPSvCm6DkyP+DCWMuA3KXZ4V4g4g==", "dev": true }, "node_modules/@lumino/messaging": { @@ -1642,77 +1535,77 @@ "dev": true }, "node_modules/@lumino/polling": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@lumino/polling/-/polling-2.1.2.tgz", - "integrity": "sha512-hv6MT7xuSrw2gW4VIoiz3L366ZdZz4oefht+7HIW/VUB6seSDp0kVyZ4P9P4I4s/LauuzPqru3eWr7QAsFZyGA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@lumino/polling/-/polling-2.1.3.tgz", + "integrity": "sha512-WEZk96ddK6eHEhdDkFUAAA40EOLit86QVbqQqnbPmhdGwFogek26Kq9b1U273LJeirv95zXCATOJAkjRyb7D+w==", "dev": true, "dependencies": { - "@lumino/coreutils": "^2.1.2", - "@lumino/disposable": "^2.1.2", - "@lumino/signaling": "^2.1.2" + "@lumino/coreutils": "^2.2.0", + "@lumino/disposable": "^2.1.3", + "@lumino/signaling": "^2.1.3" } }, "node_modules/@lumino/properties": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@lumino/properties/-/properties-2.0.1.tgz", - "integrity": "sha512-RPtHrp8cQqMnTC915lOIdrmsbPDCC7PhPOZb2YY7/Jj6dEdwmGhoMthc2tBEYWoHP+tU/hVm8UR/mEQby22srQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lumino/properties/-/properties-2.0.2.tgz", + "integrity": "sha512-b312oA3Bh97WFK8efXejYmC3DVJmvzJk72LQB7H3fXhfqS5jUWvL7MSnNmgcQvGzl9fIhDWDWjhtSTi0KGYYBg==", "dev": true }, "node_modules/@lumino/signaling": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@lumino/signaling/-/signaling-2.1.2.tgz", - "integrity": "sha512-KtwKxx+xXkLOX/BdSqtvnsqBTPKDIENFBKeYkMTxstQc3fHRmyTzmaVoeZES+pr1EUy3e8vM4pQFVQpb8VsDdA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@lumino/signaling/-/signaling-2.1.3.tgz", + "integrity": "sha512-9Wd4iMk8F1i6pYjy65bqKuPlzQMicyL9xy1/ccS20kovPcfD074waneL/7BVe+3M8i+fGa3x2qjbWrBzOdTdNw==", "dev": true, "dependencies": { - "@lumino/algorithm": "^2.0.1", - "@lumino/coreutils": "^2.1.2" + "@lumino/algorithm": "^2.0.2", + "@lumino/coreutils": "^2.2.0" } }, "node_modules/@lumino/virtualdom": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@lumino/virtualdom/-/virtualdom-2.0.1.tgz", - "integrity": "sha512-WNM+uUZX7vORhlDRN9NmhEE04Tz1plDjtbwsX+i/51pQj2N2r7+gsVPY/gR4w+I5apmC3zG8/BojjJYIwi8ogA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lumino/virtualdom/-/virtualdom-2.0.2.tgz", + "integrity": "sha512-HYZThOtZSoknjdXA102xpy5CiXtTFCVz45EXdWeYLx3NhuEwuAIX93QBBIhupalmtFlRg1yhdDNV40HxJ4kcXg==", "dev": true, "dependencies": { - "@lumino/algorithm": "^2.0.1" + "@lumino/algorithm": "^2.0.2" } }, "node_modules/@lumino/widgets": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@lumino/widgets/-/widgets-2.3.2.tgz", - "integrity": "sha512-IUx4VNplRS9V+6RqG7K46QAnf5OzhcjZ3Us6WcZzcEO9K5FD73BK914rnFAat4BnWScdTAdZGUGKOvLPT9kuNA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@lumino/widgets/-/widgets-2.5.0.tgz", + "integrity": "sha512-RSRpc6aIEiuw79jqWUHYWXLJ2GBy7vhwuqgo94UVzg6oeh3XBECX0OvXGjK2k7N2BhmRrIs9bXky7Dm861S6mQ==", "dev": true, "dependencies": { - "@lumino/algorithm": "^2.0.1", - "@lumino/commands": "^2.3.0", - "@lumino/coreutils": "^2.1.2", - "@lumino/disposable": "^2.1.2", - "@lumino/domutils": "^2.0.1", - "@lumino/dragdrop": "^2.1.4", - "@lumino/keyboard": "^2.0.1", - "@lumino/messaging": "^2.0.1", - "@lumino/properties": "^2.0.1", - "@lumino/signaling": "^2.1.2", - "@lumino/virtualdom": "^2.0.1" + "@lumino/algorithm": "^2.0.2", + "@lumino/commands": "^2.3.1", + "@lumino/coreutils": "^2.2.0", + "@lumino/disposable": "^2.1.3", + "@lumino/domutils": "^2.0.2", + "@lumino/dragdrop": "^2.1.5", + "@lumino/keyboard": "^2.0.2", + "@lumino/messaging": "^2.0.2", + "@lumino/properties": "^2.0.2", + "@lumino/signaling": "^2.1.3", + "@lumino/virtualdom": "^2.0.2" } }, "node_modules/@lumino/widgets/node_modules/@lumino/collections": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@lumino/collections/-/collections-2.0.1.tgz", - "integrity": "sha512-8TbAU/48XVPKc/FOhGHLuugf2Gmx6vhVEx867KGG5fLwDOI8EW4gTno78yJUk8G0QpgNa+sdpB/LwbJFNIratg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lumino/collections/-/collections-2.0.2.tgz", + "integrity": "sha512-o0QmfV1D3WhAeA8GI1/YmEPaK89JtHVa764rQ5T0LdbDEwUtUDbjavHs1E/+y66tNTXz9RUJ4D2rcSb9tysYsg==", "dev": true, "dependencies": { - "@lumino/algorithm": "^2.0.1" + "@lumino/algorithm": "^2.0.2" } }, "node_modules/@lumino/widgets/node_modules/@lumino/messaging": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@lumino/messaging/-/messaging-2.0.1.tgz", - "integrity": "sha512-Z1b9Sq7i2yw7BN/u9ezoBUMYK06CsQXO7BqpczSnEO0PfwFf9dWi7y9VcIySOBz9uogsT1uczZMIMtLefk+xPQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lumino/messaging/-/messaging-2.0.2.tgz", + "integrity": "sha512-2sUF07cYA0f3mDil41Eh5sfBk0aGAH/mOh1I4+vyRUsKyBqp4WTUtpJFd8xVJGAntygxwnebIygkIaXXTIQvxA==", "dev": true, "dependencies": { - "@lumino/algorithm": "^2.0.1", - "@lumino/collections": "^2.0.1" + "@lumino/algorithm": "^2.0.2", + "@lumino/collections": "^2.0.2" } }, "node_modules/@mapbox/node-pre-gyp": { @@ -1734,41 +1627,27 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, - "node_modules/@mapbox/node-pre-gyp/node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } + "node_modules/@mapbox/node-pre-gyp/node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, - "node_modules/@mapbox/node-pre-gyp/node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "node_modules/@mapbox/node-pre-gyp/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" + "debug": "4" }, "engines": { - "node": ">=10" + "node": ">= 6.0.0" } }, "node_modules/@mapbox/node-pre-gyp/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -1784,21 +1663,59 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@mapbox/node-pre-gyp/node_modules/npmlog": { + "node_modules/@mapbox/node-pre-gyp/node_modules/https-proxy-agent": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" } }, "node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dependencies": { "glob": "^7.1.3" }, @@ -1860,7 +1777,6 @@ "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", "dev": true, - "license": "ISC", "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", @@ -1872,62 +1788,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@npmcli/agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@npmcli/agent/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@npmcli/agent/node_modules/https-proxy-agent": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", - "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@npmcli/agent/node_modules/socks-proxy-agent": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz", - "integrity": "sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.1", - "debug": "^4.3.4", - "socks": "^2.7.1" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/@npmcli/arborist": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-7.5.4.tgz", @@ -1977,27 +1837,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@npmcli/arborist/node_modules/@npmcli/fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", - "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", - "dev": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/arborist/node_modules/abbrev": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", - "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/@npmcli/arborist/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -2007,76 +1846,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/@npmcli/arborist/node_modules/cacache": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", - "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", - "dev": true, - "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/arborist/node_modules/fs-minipass": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", - "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/arborist/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@npmcli/arborist/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/@npmcli/arborist/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -2092,79 +1861,16 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@npmcli/arborist/node_modules/minipass-collect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", - "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/@npmcli/arborist/node_modules/nopt": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", - "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", - "dev": true, - "dependencies": { - "abbrev": "^2.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/arborist/node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/arborist/node_modules/unique-filename": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", - "dev": true, - "dependencies": { - "unique-slug": "^4.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/arborist/node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/@npmcli/fs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", - "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", + "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "dev": true, "dependencies": { - "@gar/promisify": "^1.1.3", "semver": "^7.3.5" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/@npmcli/git": { @@ -2260,51 +1966,16 @@ "balanced-match": "^1.0.0" } }, - "node_modules/@npmcli/map-workspaces/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "node_modules/@npmcli/map-workspaces/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "brace-expansion": "^2.0.1" }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@npmcli/map-workspaces/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/@npmcli/map-workspaces/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" + "engines": { + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -2326,160 +1997,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@npmcli/metavuln-calculator/node_modules/@npmcli/fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", - "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", - "dev": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/metavuln-calculator/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@npmcli/metavuln-calculator/node_modules/cacache": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", - "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", - "dev": true, - "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/metavuln-calculator/node_modules/fs-minipass": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", - "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/metavuln-calculator/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@npmcli/metavuln-calculator/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/@npmcli/metavuln-calculator/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@npmcli/metavuln-calculator/node_modules/minipass-collect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", - "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/@npmcli/metavuln-calculator/node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/metavuln-calculator/node_modules/unique-filename": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", - "dev": true, - "dependencies": { - "unique-slug": "^4.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/metavuln-calculator/node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/@npmcli/move-file": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", @@ -2497,6 +2014,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2516,6 +2034,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dependencies": { "glob": "^7.1.3" }, @@ -2562,65 +2081,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@npmcli/package-json/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@npmcli/package-json/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@npmcli/package-json/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/@npmcli/package-json/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@npmcli/promise-spawn": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz", @@ -2674,7 +2134,6 @@ "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.1.tgz", "integrity": "sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==", "dev": true, - "license": "ISC", "engines": { "node": "^16.14.0 || >=18.0.0" } @@ -2696,276 +2155,34 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@npmcli/run-script/node_modules/@npmcli/fs": { + "node_modules/@npmcli/run-script/node_modules/isexe": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", - "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, - "dependencies": { - "semver": "^7.3.5" - }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=16" } }, - "node_modules/@npmcli/run-script/node_modules/abbrev": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", - "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/@npmcli/run-script/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@npmcli/run-script/node_modules/cacache": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", - "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", - "dev": true, - "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/run-script/node_modules/fs-minipass": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", - "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/run-script/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@npmcli/run-script/node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", - "dev": true, - "engines": { - "node": ">=16" - } - }, - "node_modules/@npmcli/run-script/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/@npmcli/run-script/node_modules/make-fetch-happen": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", - "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", - "dev": true, - "dependencies": { - "@npmcli/agent": "^2.0.0", - "cacache": "^18.0.0", - "http-cache-semantics": "^4.1.1", - "is-lambda": "^1.0.1", - "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "proc-log": "^4.2.0", - "promise-retry": "^2.0.1", - "ssri": "^10.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/run-script/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@npmcli/run-script/node_modules/minipass-collect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", - "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/@npmcli/run-script/node_modules/minipass-fetch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", - "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/@npmcli/run-script/node_modules/node-gyp": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.2.0.tgz", - "integrity": "sha512-sp3FonBAaFe4aYTcFdZUn2NYkbP7xroPGYvQmP4Nl5PxamznItBnNCgjrVTKrEfQynInMsJvZrdmqUnysCJ8rw==", - "dev": true, - "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "glob": "^10.3.10", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^13.0.0", - "nopt": "^7.0.0", - "proc-log": "^4.1.0", - "semver": "^7.3.5", - "tar": "^6.2.1", - "which": "^4.0.0" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/run-script/node_modules/nopt": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", - "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", - "dev": true, - "dependencies": { - "abbrev": "^2.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/run-script/node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/run-script/node_modules/unique-filename": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", - "dev": true, - "dependencies": { - "unique-slug": "^4.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/run-script/node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/run-script/node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", - "dev": true, - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^16.13.0 || >=18.0.0" - } - }, - "node_modules/@nx/devkit": { - "version": "20.0.7", - "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-20.0.7.tgz", - "integrity": "sha512-h+B5S+tkHObtKj2pQYUkbiaiYdcim95iS27CaZgasq7FiIXQOoupQ6jrIKduQJKx+GfYbuCCd60zrAYbkyvxiA==", + "node_modules/@nx/devkit": { + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-20.1.1.tgz", + "integrity": "sha512-sqihJhJQERCTl0KmKmpRFxWxuTnH8yRqdo8T5uGGaHzTNiMdIp5smTF2dBs7/OMkZDxcJc4dKvcFWfreZr8XNw==", "dev": true, "dependencies": { "ejs": "^3.1.7", @@ -3006,9 +2223,9 @@ } }, "node_modules/@nx/nx-darwin-arm64": { - "version": "20.0.7", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-20.0.7.tgz", - "integrity": "sha512-QLD0DlyT343okCMHNg4EyM1s9HWU55RGiD36OxopaAmDcJ45j4p7IgmYlwbWCC5TyjIXSnLnZyIAs5DrqaKwrg==", + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-20.1.1.tgz", + "integrity": "sha512-Ah0ShPQaMfvzVfhsyuI6hNB0bmwLHJqqrWldZeF97SFPhv6vfKdcdlZmSnask+V4N5z9TOCUmCMu2asMQa7+kw==", "cpu": [ "arm64" ], @@ -3022,9 +2239,9 @@ } }, "node_modules/@nx/nx-darwin-x64": { - "version": "20.0.7", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-20.0.7.tgz", - "integrity": "sha512-Sc2h+eAunGKiqpumvjVrrt0LRtk/l6Fev/633WP55svSNuY9muB/MPcP9v/oLyAD1flDnzvIWeUT6eEw6oqvZw==", + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-20.1.1.tgz", + "integrity": "sha512-TmdX6pbzclvPGsttTTaZhdF46HV1vfvYSHJaSMsYJX68l3gcQnAJ1ZRDksEgkYeAy+O9KrPimD84NM5W/JvqcQ==", "cpu": [ "x64" ], @@ -3038,9 +2255,9 @@ } }, "node_modules/@nx/nx-freebsd-x64": { - "version": "20.0.7", - "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-20.0.7.tgz", - "integrity": "sha512-Sp0pMVGj4LuPaO6oL9R5gsIPjIm8Xt3IyP9f+5uwtqjipiPriw0IdD2uV9bDjPPs0QQc15ncz+eSk30p836qpA==", + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-20.1.1.tgz", + "integrity": "sha512-7/7f3GbUbdvtTFOb/8wcaSQYkhVIxcC4UzFJM5yEyXPJmIrglk+RX3SLuOFRBFJnO+Z7D6jLUnLOBHKCGfqLVw==", "cpu": [ "x64" ], @@ -3054,9 +2271,9 @@ } }, "node_modules/@nx/nx-linux-arm-gnueabihf": { - "version": "20.0.7", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-20.0.7.tgz", - "integrity": "sha512-hs15RudLvFkfBtUL20M9Hr0wn8FLije3EGn1j9iPmo8EiZBZn4mDAywwPZXmDiAuxKTU8LKBLT/xJczNe8gzbQ==", + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-20.1.1.tgz", + "integrity": "sha512-VxpMz5jCZ5gnk1gP2jDBCheYs7qOwQoJmzGbEB8hNy0CwRH/G8pL4RRo4Sz+4aiF6Z+9eax5RM2/Syh+bS0uJw==", "cpu": [ "arm" ], @@ -3070,9 +2287,9 @@ } }, "node_modules/@nx/nx-linux-arm64-gnu": { - "version": "20.0.7", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-20.0.7.tgz", - "integrity": "sha512-t1NSxBvWpyjb9VnbxAN2Oka3JXEKtbQv//aLOer8++8Y+e6INDOHmRADyyp5BcLwBpsaP/lWLKcDa6vlsMzXTg==", + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-20.1.1.tgz", + "integrity": "sha512-8T2+j4KvsWb6ljW1Y2s/uCSt4Drtlsr3GSrGdvcETW0IKaTfKZAJlxTLAWQHEF88hP6GAJRGxNrgmUHMr8HwUA==", "cpu": [ "arm64" ], @@ -3086,9 +2303,9 @@ } }, "node_modules/@nx/nx-linux-arm64-musl": { - "version": "20.0.7", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-20.0.7.tgz", - "integrity": "sha512-lLAzyxQeeALMKM2uBA9728gZ0bihy6rfhMe+fracV1xjGLfcHEa/hNmhXNMp9Vf80sZJ50EUeW6mUPluLROBNQ==", + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-20.1.1.tgz", + "integrity": "sha512-TI964w+HFUqG6elriKwQPRX7QRxVRMz5YKdNPgf4+ab4epQ379kwJQEHlyOHR72ir8Tl46z3BoPjvmaLylrT4Q==", "cpu": [ "arm64" ], @@ -3102,9 +2319,9 @@ } }, "node_modules/@nx/nx-linux-x64-gnu": { - "version": "20.0.7", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-20.0.7.tgz", - "integrity": "sha512-H9LfEoHEa0ZHnfifseY24RPErtGaXSoWTuW9JAPylUXeYOy66i/FwxwbjsG5BMFJCnL1LGXPN9Oirh442lcsbQ==", + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-20.1.1.tgz", + "integrity": "sha512-Sg2tQ0v3KP9cAqQST16YR+dT/NbirPts6by+A4vhOtaBrZFVqm9P89K9UdcJf4Aj1CaGbs84lotp2aM4E4bQPA==", "cpu": [ "x64" ], @@ -3118,9 +2335,9 @@ } }, "node_modules/@nx/nx-linux-x64-musl": { - "version": "20.0.7", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-20.0.7.tgz", - "integrity": "sha512-2VsTSLZZVGHmN2BkSaLoOp/Byj9j20so/Ne/TZg4Lo/HBp0iDSOmUtbPAnkJOS6UiAPvQtb9zqzRKPphhDhnzg==", + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-20.1.1.tgz", + "integrity": "sha512-ekKvuIMRJRhZnkWIWEr4TRVEAyKVDgEMwqk83ilB0Mqpj2RoOKbw7jZFvWcxJWI4kSeZjTea3xCWGNPa1GfCww==", "cpu": [ "x64" ], @@ -3134,9 +2351,9 @@ } }, "node_modules/@nx/nx-win32-arm64-msvc": { - "version": "20.0.7", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-20.0.7.tgz", - "integrity": "sha512-lmH7xTPHJe2q/P2tnHEjOTdwzNxnFV08Kp2z6sUU0lAfJ79mye2nydGBDtFq9CeFF1Q6vfCSDTRu5fbxAZ9/Xg==", + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-20.1.1.tgz", + "integrity": "sha512-JRycFkk6U8A1sXaDmSFA2HMKT2js3HK/+nI+auyITRqVbV79/r6ir/oFSgIjKth8j/vVbGDL8I4E3nEQ7leZYw==", "cpu": [ "arm64" ], @@ -3150,9 +2367,9 @@ } }, "node_modules/@nx/nx-win32-x64-msvc": { - "version": "20.0.7", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-20.0.7.tgz", - "integrity": "sha512-U8LY1O3XA1yD8FoCM0ozT0DpFJdei2NNSrp/5lBXn5KHb2nkZ8DQ1zh7RKvMhEMwDNfNGbM7JsaBTr+fP6eYJg==", + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-20.1.1.tgz", + "integrity": "sha512-VwxmJU7o8KqTZ+KYk7atoWOUykKd8D4hdgKqqltdq/UBfsAWD/JCFt5OB/VFvrGDbK6I6iKpMvXWlHy4gkXQiw==", "cpu": [ "x64" ], @@ -3377,9 +2594,9 @@ } }, "node_modules/@rjsf/utils": { - "version": "5.18.2", - "resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-5.18.2.tgz", - "integrity": "sha512-iDqwBTispZ7mAgwBuHIM0emK+Ft2xRgKD9TzB68VEUhr2hqlDRpwtH6/AgAWUKmJl4kUj3cRKVqqhIvamGLpXw==", + "version": "5.22.4", + "resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-5.22.4.tgz", + "integrity": "sha512-yQTdz5ryiYy258xCVthVPQ3DeaMzrRNrFcO8xvGHorp0/bLUxdTZ0iidXop49m3y8SaxxTZd398ZKWg2cqxiIA==", "dev": true, "dependencies": { "json-schema-merge-allof": "^0.8.1", @@ -3396,9 +2613,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", - "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.26.0.tgz", + "integrity": "sha512-gJNwtPDGEaOEgejbaseY6xMFu+CPltsc8/T+diUTTbOQLqD+bnrJq9ulH6WD69TqwqWmrfRAtUv30cCFZlbGTQ==", "cpu": [ "arm" ], @@ -3409,9 +2626,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", - "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.26.0.tgz", + "integrity": "sha512-YJa5Gy8mEZgz5JquFruhJODMq3lTHWLm1fOy+HIANquLzfIOzE9RA5ie3JjCdVb9r46qfAQY/l947V0zfGJ0OQ==", "cpu": [ "arm64" ], @@ -3422,9 +2639,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", - "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.26.0.tgz", + "integrity": "sha512-ErTASs8YKbqTBoPLp/kA1B1Um5YSom8QAc4rKhg7b9tyyVqDBlQxy7Bf2wW7yIlPGPg2UODDQcbkTlruPzDosw==", "cpu": [ "arm64" ], @@ -3435,9 +2652,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", - "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.26.0.tgz", + "integrity": "sha512-wbgkYDHcdWW+NqP2mnf2NOuEbOLzDblalrOWcPyY6+BRbVhliavon15UploG7PpBRQ2bZJnbmh8o3yLoBvDIHA==", "cpu": [ "x64" ], @@ -3447,10 +2664,36 @@ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.26.0.tgz", + "integrity": "sha512-Y9vpjfp9CDkAG4q/uwuhZk96LP11fBz/bYdyg9oaHYhtGZp7NrbkQrj/66DYMMP2Yo/QPAsVHkV891KyO52fhg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.26.0.tgz", + "integrity": "sha512-A/jvfCZ55EYPsqeaAt/yDAG4q5tt1ZboWMHEvKAH9Zl92DWvMIbnZe/f/eOXze65aJaaKbL+YeM0Hz4kLQvdwg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", - "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.26.0.tgz", + "integrity": "sha512-paHF1bMXKDuizaMODm2bBTjRiHxESWiIyIdMugKeLnjuS1TCS54MF5+Y5Dx8Ui/1RBPVRE09i5OUlaLnv8OGnA==", "cpu": [ "arm" ], @@ -3461,9 +2704,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", - "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.26.0.tgz", + "integrity": "sha512-cwxiHZU1GAs+TMxvgPfUDtVZjdBdTsQwVnNlzRXC5QzIJ6nhfB4I1ahKoe9yPmoaA/Vhf7m9dB1chGPpDRdGXg==", "cpu": [ "arm" ], @@ -3474,9 +2717,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", - "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.26.0.tgz", + "integrity": "sha512-4daeEUQutGRCW/9zEo8JtdAgtJ1q2g5oHaoQaZbMSKaIWKDQwQ3Yx0/3jJNmpzrsScIPtx/V+1AfibLisb3AMQ==", "cpu": [ "arm64" ], @@ -3487,9 +2730,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", - "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.26.0.tgz", + "integrity": "sha512-eGkX7zzkNxvvS05ROzJ/cO/AKqNvR/7t1jA3VZDi2vRniLKwAWxUr85fH3NsvtxU5vnUUKFHKh8flIBdlo2b3Q==", "cpu": [ "arm64" ], @@ -3500,9 +2743,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", - "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.26.0.tgz", + "integrity": "sha512-Odp/lgHbW/mAqw/pU21goo5ruWsytP7/HCC/liOt0zcGG0llYWKrd10k9Fj0pdj3prQ63N5yQLCLiE7HTX+MYw==", "cpu": [ "ppc64" ], @@ -3513,9 +2756,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", - "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.26.0.tgz", + "integrity": "sha512-MBR2ZhCTzUgVD0OJdTzNeF4+zsVogIR1U/FsyuFerwcqjZGvg2nYe24SAHp8O5sN8ZkRVbHwlYeHqcSQ8tcYew==", "cpu": [ "riscv64" ], @@ -3526,9 +2769,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", - "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.26.0.tgz", + "integrity": "sha512-YYcg8MkbN17fMbRMZuxwmxWqsmQufh3ZJFxFGoHjrE7bv0X+T6l3glcdzd7IKLiwhT+PZOJCblpnNlz1/C3kGQ==", "cpu": [ "s390x" ], @@ -3539,22 +2782,21 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", - "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.26.0.tgz", + "integrity": "sha512-ZuwpfjCwjPkAOxpjAEjabg6LRSfL7cAJb6gSQGZYjGhadlzKKywDkCUnJ+KEfrNY1jH5EEoSIKLCb572jSiglA==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", - "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.26.0.tgz", + "integrity": "sha512-+HJD2lFS86qkeF8kNu0kALtifMpPCZU80HvwztIKnYwym3KnA1os6nsX4BGSTLtS2QVAGG1P3guRgsYyMA0Yhg==", "cpu": [ "x64" ], @@ -3565,9 +2807,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", - "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.26.0.tgz", + "integrity": "sha512-WUQzVFWPSw2uJzX4j6YEbMAiLbs0BUysgysh8s817doAYhR5ybqTI1wtKARQKo6cGop3pHnrUJPFCsXdoFaimQ==", "cpu": [ "arm64" ], @@ -3578,9 +2820,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", - "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.26.0.tgz", + "integrity": "sha512-D4CxkazFKBfN1akAIY6ieyOqzoOoBV1OICxgUblWxff/pSjCA2khXlASUx7mK6W1oP4McqhgcCsu6QaLj3WMWg==", "cpu": [ "ia32" ], @@ -3591,9 +2833,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", - "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.26.0.tgz", + "integrity": "sha512-2x8MO1rm4PGEP0xWbubJW5RtbNLk3puzAMaLQd3B3JHVw4KcHlmXcO+Wewx9zCoo7EUFiMlu/aZbCJ7VjMzAag==", "cpu": [ "x64" ], @@ -3710,208 +2952,14 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@sigstore/sign/node_modules/@npmcli/fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", - "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "node_modules/@sigstore/tuf": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.4.tgz", + "integrity": "sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==", "dev": true, "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@sigstore/sign/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@sigstore/sign/node_modules/cacache": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", - "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", - "dev": true, - "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/@sigstore/sign/node_modules/fs-minipass": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", - "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@sigstore/sign/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@sigstore/sign/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/@sigstore/sign/node_modules/make-fetch-happen": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", - "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", - "dev": true, - "dependencies": { - "@npmcli/agent": "^2.0.0", - "cacache": "^18.0.0", - "http-cache-semantics": "^4.1.1", - "is-lambda": "^1.0.1", - "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "proc-log": "^4.2.0", - "promise-retry": "^2.0.1", - "ssri": "^10.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/@sigstore/sign/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@sigstore/sign/node_modules/minipass-collect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", - "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/@sigstore/sign/node_modules/minipass-fetch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", - "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/@sigstore/sign/node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@sigstore/sign/node_modules/unique-filename": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", - "dev": true, - "dependencies": { - "unique-slug": "^4.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@sigstore/sign/node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@sigstore/tuf": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.4.tgz", - "integrity": "sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==", - "dev": true, - "dependencies": { - "@sigstore/protobuf-specs": "^0.3.2", - "tuf-js": "^2.2.1" + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^2.2.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -3938,11 +2986,11 @@ "dev": true }, "node_modules/@swc/helpers": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.13.tgz", - "integrity": "sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==", + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", "dependencies": { - "tslib": "^2.4.0" + "tslib": "^2.8.0" } }, "node_modules/@tootallnate/once": { @@ -4036,8 +3084,7 @@ "node_modules/@types/geojson": { "version": "7946.0.4", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.4.tgz", - "integrity": "sha512-MHmwBtCb7OCv1DSivz2UNJXPGU/1btAWRKlqJ2saEhVJkpkvqHMMaOpKg0v4sAbDWSQekHGvPVMM8nQ+Jen03Q==", - "license": "MIT" + "integrity": "sha512-MHmwBtCb7OCv1DSivz2UNJXPGU/1btAWRKlqJ2saEhVJkpkvqHMMaOpKg0v4sAbDWSQekHGvPVMM8nQ+Jen03Q==" }, "node_modules/@types/hast": { "version": "3.0.4", @@ -4049,9 +3096,9 @@ } }, "node_modules/@types/jquery": { - "version": "3.5.29", - "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.29.tgz", - "integrity": "sha512-oXQQC9X9MOPRrMhPHHOsXqeQDnWeCDT3PelUIg/Oy8FAbzSZtFHRjc7IpbfFVmpLtJ+UOoywpRsuO5Jxjybyeg==", + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.32.tgz", + "integrity": "sha512-b9Xbf4CkMqS02YH8zACqN1xzdxc3cO735Qe5AbSUFmyOiaWAbcpqh9Wna+Uk0vgACvoQHpWDg2rGdHkYPLmCiQ==", "dev": true, "dependencies": { "@types/sizzle": "*" @@ -4069,9 +3116,9 @@ "dev": true }, "node_modules/@types/lodash": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz", - "integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==", + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.13.tgz", + "integrity": "sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==", "dev": true }, "node_modules/@types/markdown-it": { @@ -4112,9 +3159,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.16.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.1.tgz", - "integrity": "sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==", + "version": "20.17.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.6.tgz", + "integrity": "sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==", "dependencies": { "undici-types": "~6.19.2" } @@ -4126,15 +3173,15 @@ "dev": true }, "node_modules/@types/sizzle": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz", - "integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==", + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.9.tgz", + "integrity": "sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==", "dev": true }, "node_modules/@types/underscore": { - "version": "1.11.15", - "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.11.15.tgz", - "integrity": "sha512-HP38xE+GuWGlbSRq9WrZkousaQ7dragtZCruBVMi0oX1migFZavZ3OROKHSkNp/9ouq82zrWtZpg18jFnVN96g==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.13.0.tgz", + "integrity": "sha512-L6LBgy1f0EFQZ+7uSA57+n2g/s4Qs5r06Vwrwn0/nuK1de+adz00NWaztRQ30aEqw5qOaWbPI8u2cGQ52lj6VA==", "dev": true }, "node_modules/@types/unist": { @@ -4156,9 +3203,9 @@ "dev": true }, "node_modules/@uwdata/flechette": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@uwdata/flechette/-/flechette-1.1.0.tgz", - "integrity": "sha512-UG25ytXRsElGDSIsvCuEUUkX/XImdKe7jnZF2Y5Q9uYYQH6WO60F7yLJ/sNRBJqHdn4A0QhTAOuOJevDZZcF8Q==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@uwdata/flechette/-/flechette-1.1.1.tgz", + "integrity": "sha512-ysF6R5+ce79VwElHhOMRWsv+0jFpS2Us6I3t3w7X30F8RCbA1gRAx21GaJQWscDzibJvQHrcAiEBRpllpFypOg==" }, "node_modules/@uwdata/mosaic-core": { "resolved": "packages/core", @@ -4184,18 +3231,14 @@ "resolved": "packages/sql", "link": true }, - "node_modules/@uwdata/mosaic-sql-old": { - "resolved": "packages/sql-old", - "link": true - }, "node_modules/@uwdata/vgplot": { "resolved": "packages/vgplot", "link": true }, "node_modules/@vitejs/plugin-vue": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.1.4.tgz", - "integrity": "sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.0.tgz", + "integrity": "sha512-7n7KdUEtx/7Yl7I/WVAMZ1bEb0eVvXF3ummWTeLcs/9gvo9pJhuLdouSXGjdZ/MKD1acf1I272+X0RMua4/R3g==", "dev": true, "engines": { "node": "^18.0.0 || >=20.0.0" @@ -4206,13 +3249,13 @@ } }, "node_modules/@vitest/expect": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.4.tgz", - "integrity": "sha512-DOETT0Oh1avie/D/o2sgMHGrzYUFFo3zqESB2Hn70z6QB1HrS2IQ9z5DfyTqU8sg4Bpu13zZe9V4+UTNQlUeQA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.5.tgz", + "integrity": "sha512-nZSBTW1XIdpZvEJyoP/Sy8fUg0b8od7ZpGDkTUcfJ7wz/VoZAFzFfLyxVxGFhUjJzhYqSbIpfMtl/+k/dpWa3Q==", "dev": true, "dependencies": { - "@vitest/spy": "2.1.4", - "@vitest/utils": "2.1.4", + "@vitest/spy": "2.1.5", + "@vitest/utils": "2.1.5", "chai": "^5.1.2", "tinyrainbow": "^1.2.0" }, @@ -4221,12 +3264,12 @@ } }, "node_modules/@vitest/mocker": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.4.tgz", - "integrity": "sha512-Ky/O1Lc0QBbutJdW0rqLeFNbuLEyS+mIPiNdlVlp2/yhJ0SbyYqObS5IHdhferJud8MbbwMnexg4jordE5cCoQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.5.tgz", + "integrity": "sha512-XYW6l3UuBmitWqSUXTNXcVBUCRytDogBsWuNXQijc00dtnU/9OqpXWp4OJroVrad/gLIomAq9aW8yWDBtMthhQ==", "dev": true, "dependencies": { - "@vitest/spy": "2.1.4", + "@vitest/spy": "2.1.5", "estree-walker": "^3.0.3", "magic-string": "^0.30.12" }, @@ -4247,9 +3290,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.4.tgz", - "integrity": "sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.5.tgz", + "integrity": "sha512-4ZOwtk2bqG5Y6xRGHcveZVr+6txkH7M2e+nPFd6guSoN638v/1XQ0K06eOpi0ptVU/2tW/pIU4IoPotY/GZ9fw==", "dev": true, "dependencies": { "tinyrainbow": "^1.2.0" @@ -4259,12 +3302,12 @@ } }, "node_modules/@vitest/runner": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.4.tgz", - "integrity": "sha512-sKRautINI9XICAMl2bjxQM8VfCMTB0EbsBc/EDFA57V6UQevEKY/TOPOF5nzcvCALltiLfXWbq4MaAwWx/YxIA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.5.tgz", + "integrity": "sha512-pKHKy3uaUdh7X6p1pxOkgkVAFW7r2I818vHDthYLvUyjRfkKOU6P45PztOch4DZarWQne+VOaIMwA/erSSpB9g==", "dev": true, "dependencies": { - "@vitest/utils": "2.1.4", + "@vitest/utils": "2.1.5", "pathe": "^1.1.2" }, "funding": { @@ -4272,12 +3315,12 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.4.tgz", - "integrity": "sha512-3Kab14fn/5QZRog5BPj6Rs8dc4B+mim27XaKWFWHWA87R56AKjHTGcBFKpvZKDzC4u5Wd0w/qKsUIio3KzWW4Q==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.5.tgz", + "integrity": "sha512-zmYw47mhfdfnYbuhkQvkkzYroXUumrwWDGlMjpdUr4jBd3HZiV2w7CQHj+z7AAS4VOtWxI4Zt4bWt4/sKcoIjg==", "dev": true, "dependencies": { - "@vitest/pretty-format": "2.1.4", + "@vitest/pretty-format": "2.1.5", "magic-string": "^0.30.12", "pathe": "^1.1.2" }, @@ -4286,9 +3329,9 @@ } }, "node_modules/@vitest/spy": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.4.tgz", - "integrity": "sha512-4JOxa+UAizJgpZfaCPKK2smq9d8mmjZVPMt2kOsg/R8QkoRzydHH1qHxIYNvr1zlEaFj4SXiaaJWxq/LPLKaLg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.5.tgz", + "integrity": "sha512-aWZF3P0r3w6DiYTVskOYuhBc7EMc3jvn1TkBg8ttylFFRqNN2XGD7V5a4aQdk6QiUzZQ4klNBSpCLJgWNdIiNw==", "dev": true, "dependencies": { "tinyspy": "^3.0.2" @@ -4298,12 +3341,12 @@ } }, "node_modules/@vitest/utils": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.4.tgz", - "integrity": "sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.5.tgz", + "integrity": "sha512-yfj6Yrp0Vesw2cwJbP+cl04OC+IHFsuQsrsJBL9pyGeQXE56v1UAOQco+SR55Vf1nQzfV0QJg1Qum7AaWUwwYg==", "dev": true, "dependencies": { - "@vitest/pretty-format": "2.1.4", + "@vitest/pretty-format": "2.1.5", "loupe": "^3.1.2", "tinyrainbow": "^1.2.0" }, @@ -4374,21 +3417,21 @@ } }, "node_modules/@vue/devtools-api": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.6.2.tgz", - "integrity": "sha512-NCT0ujqlwAhoFvCsAG7G5qS8w/A/dhvFSt2BhmNxyqgpYDrf9CG1zYyWLQkE3dsZ+5lCT6ULUic2VKNaE07Vzg==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.6.4.tgz", + "integrity": "sha512-5AaJ5ELBIuevmFMZYYLuOO9HUuY/6OlkOELHE7oeDhy4XD/hSODIzktlsvBOsn+bto3aD0psj36LGzwVu5Ip8w==", "dev": true, "dependencies": { - "@vue/devtools-kit": "^7.6.2" + "@vue/devtools-kit": "^7.6.4" } }, "node_modules/@vue/devtools-kit": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.6.2.tgz", - "integrity": "sha512-k61BxHRmcTtIQZFouF9QWt9nCCNtSdw12lhg8VNtHq5/XOBGD+ewiK27a40UJ8UPYoCJvi80hbvbYr5E/Zeu1g==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.6.4.tgz", + "integrity": "sha512-Zs86qIXXM9icU0PiGY09PQCle4TI750IPLmAJzW5Kf9n9t5HzSYf6Rz6fyzSwmfMPiR51SUKJh9sXVZu78h2QA==", "dev": true, "dependencies": { - "@vue/devtools-shared": "^7.6.2", + "@vue/devtools-shared": "^7.6.4", "birpc": "^0.2.19", "hookable": "^5.5.3", "mitt": "^3.0.1", @@ -4398,9 +3441,9 @@ } }, "node_modules/@vue/devtools-shared": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.6.2.tgz", - "integrity": "sha512-lcjyJ7hCC0W0kNwnCGMLVTMvDLoZgjcq9BvboPgS+6jQyDul7fpzRSKTGtGhCHoxrDox7qBAKGbAl2Rcf7GE1A==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.6.4.tgz", + "integrity": "sha512-nD6CUvBEel+y7zpyorjiUocy0nh77DThZJ0k1GRnJeOmY3ATq2fWijEp7wk37gb023Cb0R396uYh5qMSBQ5WFg==", "dev": true, "dependencies": { "rfdc": "^1.4.1" @@ -4457,14 +3500,14 @@ "dev": true }, "node_modules/@vueuse/core": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-11.1.0.tgz", - "integrity": "sha512-P6dk79QYA6sKQnghrUz/1tHi0n9mrb/iO1WTMk/ElLmTyNqgDeSZ3wcDf6fRBGzRJbeG1dxzEOvLENMjr+E3fg==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-11.2.0.tgz", + "integrity": "sha512-JIUwRcOqOWzcdu1dGlfW04kaJhW3EXnnjJJfLTtddJanymTL7lF1C0+dVVZ/siLfc73mWn+cGP1PE1PKPruRSA==", "dev": true, "dependencies": { "@types/web-bluetooth": "^0.0.20", - "@vueuse/metadata": "11.1.0", - "@vueuse/shared": "11.1.0", + "@vueuse/metadata": "11.2.0", + "@vueuse/shared": "11.2.0", "vue-demi": ">=0.14.10" }, "funding": { @@ -4498,13 +3541,13 @@ } }, "node_modules/@vueuse/integrations": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-11.1.0.tgz", - "integrity": "sha512-O2ZgrAGPy0qAjpoI2YR3egNgyEqwG85fxfwmA9BshRIGjV4G6yu6CfOPpMHAOoCD+UfsIl7Vb1bXJ6ifrHYDDA==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-11.2.0.tgz", + "integrity": "sha512-zGXz3dsxNHKwiD9jPMvR3DAxQEOV6VWIEYTGVSB9PNpk4pTWR+pXrHz9gvXWcP2sTk3W2oqqS6KwWDdntUvNVA==", "dev": true, "dependencies": { - "@vueuse/core": "11.1.0", - "@vueuse/shared": "11.1.0", + "@vueuse/core": "11.2.0", + "@vueuse/shared": "11.2.0", "vue-demi": ">=0.14.10" }, "funding": { @@ -4590,18 +3633,18 @@ } }, "node_modules/@vueuse/metadata": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-11.1.0.tgz", - "integrity": "sha512-l9Q502TBTaPYGanl1G+hPgd3QX5s4CGnpXriVBR5fEZ/goI6fvDaVmIl3Td8oKFurOxTmbXvBPSsgrd6eu6HYg==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-11.2.0.tgz", + "integrity": "sha512-L0ZmtRmNx+ZW95DmrgD6vn484gSpVeRbgpWevFKXwqqQxW9hnSi2Ppuh2BzMjnbv4aJRiIw8tQatXT9uOB23dQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/@vueuse/shared": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-11.1.0.tgz", - "integrity": "sha512-YUtIpY122q7osj+zsNMFAfMTubGz0sn5QzE5gPzAIiCmtt2ha3uQUY1+JPyL4gRCTsLPX82Y9brNbo/aqlA91w==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-11.2.0.tgz", + "integrity": "sha512-VxFjie0EanOudYSgMErxXfq6fo8vhr5ICI+BuE3I9FnX7ePllEsVrRQ7O6Q1TLgApeLuPKcHQxAXpP+KnlrJsg==", "dev": true, "dependencies": { "vue-demi": ">=0.14.10" @@ -4643,16 +3686,16 @@ "dev": true }, "node_modules/@yarnpkg/parsers": { - "version": "3.0.0-rc.46", - "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.46.tgz", - "integrity": "sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.2.tgz", + "integrity": "sha512-/HcYgtUSiJiot/XWGLOlGxPYUG65+/31V8oqk17vZLW1xlCoR4PampyePljOxY2n8/3jz9+tIFzICsyGujJZoA==", "dev": true, "dependencies": { "js-yaml": "^3.10.0", "tslib": "^2.4.0" }, "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" } }, "node_modules/@yarnpkg/parsers/node_modules/argparse": { @@ -4696,16 +3739,19 @@ } }, "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, - "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -4718,7 +3764,6 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -4730,14 +3775,15 @@ "dev": true }, "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, "dependencies": { - "debug": "4" + "debug": "^4.3.4" }, "engines": { - "node": ">= 6.0.0" + "node": ">= 14" } }, "node_modules/agentkeepalive": { @@ -4768,7 +3814,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4785,7 +3830,6 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^8.0.0" }, @@ -4799,65 +3843,27 @@ } }, "node_modules/algoliasearch": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.24.0.tgz", - "integrity": "sha512-bf0QV/9jVejssFBmz2HQLxUadxk574t4iwjCKp5E7NBzwKkrDEhKPISIIjAU/p6K5qDx3qoeh4+26zWN1jmw3g==", - "dev": true, - "dependencies": { - "@algolia/cache-browser-local-storage": "4.24.0", - "@algolia/cache-common": "4.24.0", - "@algolia/cache-in-memory": "4.24.0", - "@algolia/client-account": "4.24.0", - "@algolia/client-analytics": "4.24.0", - "@algolia/client-common": "4.24.0", - "@algolia/client-personalization": "4.24.0", - "@algolia/client-search": "4.24.0", - "@algolia/logger-common": "4.24.0", - "@algolia/logger-console": "4.24.0", - "@algolia/recommend": "4.24.0", - "@algolia/requester-browser-xhr": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/requester-node-http": "4.24.0", - "@algolia/transporter": "4.24.0" - } - }, - "node_modules/algoliasearch/node_modules/@algolia/client-common": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", - "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", - "dev": true, - "dependencies": { - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" - } - }, - "node_modules/algoliasearch/node_modules/@algolia/client-search": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", - "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", - "dev": true, - "dependencies": { - "@algolia/client-common": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" - } - }, - "node_modules/algoliasearch/node_modules/@algolia/requester-browser-xhr": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.24.0.tgz", - "integrity": "sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==", - "dev": true, - "dependencies": { - "@algolia/requester-common": "4.24.0" - } - }, - "node_modules/algoliasearch/node_modules/@algolia/requester-node-http": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz", - "integrity": "sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==", - "dev": true, - "dependencies": { - "@algolia/requester-common": "4.24.0" + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.14.0.tgz", + "integrity": "sha512-qr21NtvIkpptwj9z6W5guICK8yijXIGzw7Ka26zAPofnefofVXoXtuAopjtmk1ZKDu4YpACj38n9mgKKc5Zuhw==", + "dev": true, + "dependencies": { + "@algolia/client-abtesting": "5.14.0", + "@algolia/client-analytics": "5.14.0", + "@algolia/client-common": "5.14.0", + "@algolia/client-insights": "5.14.0", + "@algolia/client-personalization": "5.14.0", + "@algolia/client-query-suggestions": "5.14.0", + "@algolia/client-search": "5.14.0", + "@algolia/ingestion": "1.14.0", + "@algolia/monitoring": "1.14.0", + "@algolia/recommend": "5.14.0", + "@algolia/requester-browser-xhr": "5.14.0", + "@algolia/requester-fetch": "5.14.0", + "@algolia/requester-node-http": "5.14.0" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/ansi-colors": { @@ -4924,7 +3930,6 @@ "resolved": "https://registry.npmjs.org/anywidget/-/anywidget-0.9.13.tgz", "integrity": "sha512-q8EKrxkKWrWi+X42EBeV7aLQD9TSppVQdumQIZa9qf3Er89X8ILyVnzgZld5/BGgCBn3bZ5YTJNgAbRN3urEEA==", "dev": true, - "license": "MIT", "dependencies": { "@anywidget/types": "~0.1.9", "@jupyter-widgets/base": "^6", @@ -4966,15 +3971,16 @@ } }, "node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=10" } }, "node_modules/argparse": { @@ -5046,9 +4052,9 @@ "dev": true }, "node_modules/axios": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", - "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "dev": true, "dependencies": { "follow-redirects": "^1.15.6", @@ -5112,12 +4118,15 @@ } }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/binary-search-bounds": { @@ -5159,7 +4168,6 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -5216,83 +4224,26 @@ } }, "node_modules/cacache": { - "version": "16.1.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", - "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", + "version": "18.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", + "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", + "dev": true, "dependencies": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", + "ssri": "^10.0.0", "tar": "^6.1.11", - "unique-filename": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/cacache/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/cacache/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/cacache/node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "unique-filename": "^3.0.0" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/callsites": { @@ -5300,7 +4251,6 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -5422,16 +4372,10 @@ } }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -5444,6 +4388,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -5693,9 +4640,9 @@ } }, "node_modules/command-line-usage/node_modules/typical": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-7.2.0.tgz", - "integrity": "sha512-W1+HdVRUl8fS3MZ9ogD51GOb46xMmhAZzR0WPw5jcgIZQJVvkddYzAl4YTU6g5w33Y1iRQLdIi2/1jhi2RNL0g==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", + "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", "engines": { "node": ">=12.17" } @@ -5984,9 +4931,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", + "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -6225,9 +5172,9 @@ } }, "node_modules/d3-geo": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", - "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", "dependencies": { "d3-array": "2.5.0 - 3" }, @@ -6239,7 +5186,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-4.0.0.tgz", "integrity": "sha512-p0bK60CEzph1iqmnxut7d/1kyTmm3UWtPlwdkM31AU+LW+BXazd5zJdoCn7VFxNCHXRngPHRnsNn5uGjLRGndg==", - "license": "ISC", "dependencies": { "commander": "7", "d3-array": "1 - 3", @@ -6581,9 +5527,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "engines": { "node": ">=8" } @@ -6647,12 +5593,12 @@ } }, "node_modules/dotenv-expand": { - "version": "11.0.6", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.6.tgz", - "integrity": "sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==", + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", "dev": true, "dependencies": { - "dotenv": "^16.4.4" + "dotenv": "^16.4.5" }, "engines": { "node": ">=12" @@ -6662,9 +5608,9 @@ } }, "node_modules/duckdb": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/duckdb/-/duckdb-1.1.1.tgz", - "integrity": "sha512-cFT+zsduuanxQsq7TlgIdmRREkRYb0EUQlj4hgilM0yBnJbt+i0r6Qu30aWd2gr9/bkmOuaamswBCroH/27LVA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/duckdb/-/duckdb-1.1.3.tgz", + "integrity": "sha512-tIpZr2NsSkYmfGC1ETl75RuVsaDyjvR3yAOrECcIyw7bdluzcyzEXOXoiuT+4t54hT+CppZv43gk/HiZdKW9Vw==", "hasInstallScript": true, "dependencies": { "@mapbox/node-pre-gyp": "^1.0.0", @@ -6676,118 +5622,485 @@ "resolved": "packages/duckdb-server", "link": true }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" - }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dev": true, + "node_modules/duckdb/node_modules/@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" }, "engines": { - "node": ">=0.10.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "node_modules/duckdb/node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "optional": true, + "node_modules/duckdb/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dependencies": { - "iconv-lite": "^0.6.2" + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" } }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, + "node_modules/duckdb/node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" }, "engines": { - "node": ">=0.10.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, + "node_modules/duckdb/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dependencies": { - "once": "^1.4.0" + "balanced-match": "^1.0.0" } }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, + "node_modules/duckdb/node_modules/cacache": { + "version": "16.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", + "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", "dependencies": { - "ansi-colors": "^4.1.1" + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" }, "engines": { - "node": ">=8.6" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, + "node_modules/duckdb/node_modules/cacache/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, "engines": { - "node": ">=0.12" + "node": ">=12" }, "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "node_modules/duckdb/node_modules/cacache/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/envinfo": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz", - "integrity": "sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==", - "dev": true, - "license": "MIT", - "bin": { - "envinfo": "dist/cli.js" + "node_modules/duckdb/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" }, "engines": { - "node": ">=4" + "node": ">= 8" } }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==" - }, - "node_modules/error-ex": { + "node_modules/duckdb/node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/duckdb/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/duckdb/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/duckdb/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/duckdb/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/duckdb/node_modules/make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/duckdb/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duckdb/node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/duckdb/node_modules/minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/duckdb/node_modules/node-gyp": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", + "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.13 || ^14.13 || >=16" + } + }, + "node_modules/duckdb/node_modules/nopt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/duckdb/node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/duckdb/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/duckdb/node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/duckdb/node_modules/ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/duckdb/node_modules/unique-filename": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", + "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", + "dependencies": { + "unique-slug": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/duckdb/node_modules/unique-slug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", + "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/envinfo": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz", + "integrity": "sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==" + }, + "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", @@ -6800,8 +6113,7 @@ "version": "1.5.4", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/esbuild": { "version": "0.24.0", @@ -6843,9 +6155,9 @@ } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "engines": { "node": ">=6" } @@ -6863,21 +6175,21 @@ } }, "node_modules/eslint": { - "version": "9.13.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz", - "integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==", + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.14.0.tgz", + "integrity": "sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.11.0", + "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.18.0", "@eslint/core": "^0.7.0", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.13.0", + "@eslint/js": "9.14.0", "@eslint/plugin-kit": "^0.2.0", - "@humanfs/node": "^0.16.5", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.1", + "@humanwhocodes/retry": "^0.4.0", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", @@ -6885,9 +6197,9 @@ "cross-spawn": "^7.0.2", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.1.0", - "eslint-visitor-keys": "^4.1.0", - "espree": "^10.2.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -6923,9 +6235,9 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "50.4.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.4.3.tgz", - "integrity": "sha512-uWtwFxGRv6B8sU63HZM5dAGDhgsatb+LONwmILZJhdRALLOkCX2HFZhdL/Kw2ls8SQMAVEfK+LmnEfxInRN8HA==", + "version": "50.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.5.0.tgz", + "integrity": "sha512-xTkshfZrUbiSHXBwZ/9d5ulZ2OcHXxSvm/NPo494H/hadLRJwOq5PMV0EUpMqsb9V+kQo+9BAgi6Z7aJtdBp2A==", "dev": true, "dependencies": { "@es-joy/jsdoccomment": "~0.49.0", @@ -6948,9 +6260,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", - "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -6964,12 +6276,12 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -6980,7 +6292,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6992,34 +6303,21 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", - "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/espree": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", - "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "dev": true, "dependencies": { - "acorn": "^8.12.0", + "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.1.0" + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7028,18 +6326,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", - "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -7058,7 +6344,6 @@ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -7217,8 +6502,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -7227,11 +6511,10 @@ "dev": true }, "node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", - "dev": true, - "license": "MIT" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "dev": true }, "node_modules/fastq": { "version": "1.17.1", @@ -7313,7 +6596,6 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -7382,18 +6664,18 @@ "dev": true }, "node_modules/focus-trap": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.0.tgz", - "integrity": "sha512-1td0l3pMkWJLFipobUcGaf+5DTY4PLDDrcqoSaKP8ediO/CoWCCYk/fT/Y2A4e6TNB+Sh6clRJCjOPPnKoNHnQ==", + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.1.tgz", + "integrity": "sha512-nB8y4nQl8PshahLpGKZOq1sb0xrMVFSn6at7u/qOsBZTlZRzaapISGENcB6mOkoezbClZyiMwEF/dGY8AZ00rA==", "dev": true, "dependencies": { "tabbable": "^6.2.0" } }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "dev": true, "funding": [ { @@ -7411,9 +6693,9 @@ } }, "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -7437,9 +6719,9 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "dev": true, "dependencies": { "asynckit": "^0.4.0", @@ -7508,25 +6790,15 @@ } }, "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "minipass": "^7.0.3" }, "engines": { - "node": ">=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/fs.realpath": { @@ -7558,21 +6830,23 @@ } }, "node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", "dependencies": { "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "wide-align": "^1.1.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=10" } }, "node_modules/get-caller-file": { @@ -7788,18 +7062,19 @@ } }, "node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=12" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -7826,14 +7101,17 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/globals": { @@ -7841,7 +7119,6 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=18" }, @@ -7976,7 +7253,6 @@ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", "dev": true, - "license": "ISC", "dependencies": { "lru-cache": "^10.0.1" }, @@ -8000,28 +7276,29 @@ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" }, "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, "dependencies": { - "agent-base": "6", + "agent-base": "^7.0.2", "debug": "4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/human-signals": { @@ -8074,9 +7351,9 @@ ] }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { "node": ">= 4" @@ -8129,7 +7406,6 @@ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, - "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -8185,6 +7461,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -8381,7 +7658,6 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -8515,15 +7791,12 @@ } }, "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dependencies": { "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": ">=14" - }, "funding": { "url": "https://github.com/sponsors/isaacs" }, @@ -8667,8 +7940,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -8688,8 +7960,7 @@ "node_modules/json-stringify-pretty-compact": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz", - "integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA==", - "license": "MIT" + "integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA==" }, "node_modules/json-stringify-safe": { "version": "5.0.1", @@ -8733,8 +8004,7 @@ "dev": true, "engines": [ "node >= 0.2.0" - ], - "license": "MIT" + ] }, "node_modules/jsonpointer": { "version": "5.0.1", @@ -8944,21 +8214,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/lerna/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/lerna/node_modules/minimatch": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", @@ -9007,42 +8262,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/lerna/node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/lerna/node_modules/ssri/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/lerna/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -9057,9 +8276,9 @@ } }, "node_modules/lib0": { - "version": "0.2.93", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.93.tgz", - "integrity": "sha512-M5IKsiFJYulS+8Eal8f+zAqf5ckm1vffW0fFDxfgxJ+uiVopvDdd3PxJmz0GsVi3YNO7QCFSq0nAsiDmNhLj9Q==", + "version": "0.2.98", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.98.tgz", + "integrity": "sha512-XteTiNO0qEXqqweWx+b21p/fBnNHUA1NwAtJNJek1oPrewEZs2uiT4gWivHKr9GqCjDPAhchz0UQO8NwU3bBNA==", "dev": true, "dependencies": { "isomorphic.js": "^0.2.4" @@ -9082,7 +8301,6 @@ "resolved": "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-8.0.6.tgz", "integrity": "sha512-uM8DHDEfYG6G5gVivVl+yQd4pH3uRclHC59lzIbSvy7b5FEwR+mU49Zq1jEyRtRFv7+M99mUW9S0wL/4laT4lw==", "dev": true, - "license": "ISC", "dependencies": { "npm-package-arg": "^11.0.2", "npm-registry-fetch": "^17.0.1" @@ -9111,9 +8329,9 @@ } }, "node_modules/libnpmpublish/node_modules/ci-info": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", - "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz", + "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==", "dev": true, "funding": [ { @@ -9125,18 +8343,6 @@ "node": ">=8" } }, - "node_modules/libnpmpublish/node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/lines-and-columns": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", @@ -9230,6 +8436,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/loupe": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", @@ -9237,13 +8456,9 @@ "dev": true }, "node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", - "license": "ISC", - "engines": { - "node": "14 || >=16.14" - } + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, "node_modules/magic-string": { "version": "0.30.12", @@ -9255,70 +8470,41 @@ } }, "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, "dependencies": { - "semver": "^6.0.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/make-fetch-happen": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", - "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", + "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", + "dev": true, "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", + "proc-log": "^4.2.0", "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^9.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/make-fetch-happen/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" + "ssri": "^10.0.0" }, "engines": { - "node": ">=8" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/map-obj": { @@ -9590,9 +8776,9 @@ } }, "node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", "dev": true, "funding": [ { @@ -9610,9 +8796,9 @@ } }, "node_modules/micromark-util-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", - "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", "dev": true, "funding": [ { @@ -9626,9 +8812,9 @@ ] }, "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", - "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", "dev": true, "funding": [ { @@ -9647,9 +8833,9 @@ } }, "node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", "dev": true, "funding": [ { @@ -9663,9 +8849,9 @@ ] }, "node_modules/micromark-util-types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", - "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", "dev": true, "funding": [ { @@ -9768,60 +8954,39 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-collect/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "minipass": "^7.0.3" }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/minipass-fetch": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", - "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", + "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", + "dev": true, "dependencies": { - "minipass": "^3.1.6", + "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^2.1.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" }, "optionalDependencies": { "encoding": "^0.1.13" } }, - "node_modules/minipass-fetch/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/minipass-flush": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", @@ -10015,9 +9180,9 @@ "dev": true }, "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", "engines": { "node": ">= 0.6" } @@ -10029,12 +9194,9 @@ "dev": true }, "node_modules/node-addon-api": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz", - "integrity": "sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==", - "engines": { - "node": "^16 || ^18 || >= 20" - } + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==" }, "node_modules/node-fetch": { "version": "2.6.7", @@ -10056,74 +9218,51 @@ } }, "node_modules/node-gyp": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", - "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.2.0.tgz", + "integrity": "sha512-sp3FonBAaFe4aYTcFdZUn2NYkbP7xroPGYvQmP4Nl5PxamznItBnNCgjrVTKrEfQynInMsJvZrdmqUnysCJ8rw==", + "dev": true, "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^10.0.3", - "nopt": "^6.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^4.1.0", "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" + "tar": "^6.2.1", + "which": "^4.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "^12.13 || ^14.13 || >=16" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/node-gyp/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=16" } }, - "node_modules/node-gyp/node_modules/nopt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", - "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "node_modules/node-gyp/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, "dependencies": { - "abbrev": "^1.0.0" + "isexe": "^3.1.1" }, "bin": { - "nopt": "bin/nopt.js" + "node-which": "bin/which.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/node-gyp/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^16.13.0 || >=18.0.0" } }, "node_modules/node-machine-id": { @@ -10182,17 +9321,18 @@ } }, "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, "dependencies": { - "abbrev": "1" + "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": ">=6" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/normalize-package-data": { @@ -10255,7 +9395,6 @@ "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.2.tgz", "integrity": "sha512-IGN0IAwmhDJwy13Wc8k+4PEbTPhpJnMtfR53ZbOyjkvmEcLS4nCwp6mvMWjS5sUjeiW3mpx6cHmuhKEu9XmcQw==", "dev": true, - "license": "ISC", "dependencies": { "hosted-git-info": "^7.0.0", "proc-log": "^4.0.0", @@ -10263,267 +9402,53 @@ "validate-npm-package-name": "^5.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm-packlist": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", - "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", - "dev": true, - "dependencies": { - "ignore-walk": "^6.0.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-pick-manifest": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.1.0.tgz", - "integrity": "sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==", - "dev": true, - "dependencies": { - "npm-install-checks": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0", - "npm-package-arg": "^11.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm-registry-fetch": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.1.0.tgz", - "integrity": "sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/redact": "^2.0.0", - "jsonparse": "^1.3.1", - "make-fetch-happen": "^13.0.0", - "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minizlib": "^2.1.2", - "npm-package-arg": "^11.0.0", - "proc-log": "^4.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm-registry-fetch/node_modules/@npmcli/fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", - "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", - "dev": true, - "license": "ISC", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-registry-fetch/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/npm-registry-fetch/node_modules/cacache": { - "version": "18.0.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.3.tgz", - "integrity": "sha512-qXCd4rh6I07cnDqh8V48/94Tc/WSfj+o3Gn6NZ0aZovS255bUx8O13uKxRFd2eWG0xgsco7+YItQNPaa5E85hg==", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm-registry-fetch/node_modules/fs-minipass": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", - "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-registry-fetch/node_modules/glob": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", - "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm-registry-fetch/node_modules/jackspeak": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", - "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", - "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/agent": "^2.0.0", - "cacache": "^18.0.0", - "http-cache-semantics": "^4.1.1", - "is-lambda": "^1.0.1", - "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "proc-log": "^4.2.0", - "promise-retry": "^2.0.1", - "ssri": "^10.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm-registry-fetch/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm-registry-fetch/node_modules/minipass-collect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", - "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/npm-registry-fetch/node_modules/minipass-fetch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", - "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm-registry-fetch/node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", + "node_modules/npm-packlist": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", + "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", "dev": true, - "license": "ISC", "dependencies": { - "minipass": "^7.0.3" + "ignore-walk": "^6.0.4" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm-registry-fetch/node_modules/unique-filename": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "node_modules/npm-pick-manifest": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.1.0.tgz", + "integrity": "sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==", "dev": true, - "license": "ISC", "dependencies": { - "unique-slug": "^4.0.0" + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^11.0.0", + "semver": "^7.3.5" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm-registry-fetch/node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "node_modules/npm-registry-fetch": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.1.0.tgz", + "integrity": "sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==", "dev": true, - "license": "ISC", "dependencies": { - "imurmurhash": "^0.1.4" + "@npmcli/redact": "^2.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minizlib": "^2.1.2", + "npm-package-arg": "^11.0.0", + "proc-log": "^4.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm-run-path": { @@ -10539,29 +9464,27 @@ } }, "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", "dependencies": { - "are-we-there-yet": "^3.0.0", + "are-we-there-yet": "^2.0.0", "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", + "gauge": "^3.0.0", "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/nx": { - "version": "20.0.7", - "resolved": "https://registry.npmjs.org/nx/-/nx-20.0.7.tgz", - "integrity": "sha512-Un7eMAqTx+gRB4j6hRWafMvOso4pmFg3Ff+BmfFOgqD8XdE+xV/+Ke9mPTfi4qYD5eQiY1lO15l3dRuBH7+AJw==", + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/nx/-/nx-20.1.1.tgz", + "integrity": "sha512-bLDEDBUuAvFC5b74QUnmJxUHTRa0mkc2wRPmb2rN3d1VlTFjzKTT9ClJTR1emp/DDO620zyAmVCDVKmnSZNFoQ==", "dev": true, "hasInstallScript": true, "dependencies": { "@napi-rs/wasm-runtime": "0.2.4", "@yarnpkg/lockfile": "^1.1.0", - "@yarnpkg/parsers": "3.0.0-rc.46", + "@yarnpkg/parsers": "3.0.2", "@zkochan/js-yaml": "0.0.7", "axios": "^1.7.4", "chalk": "^4.1.0", @@ -10597,16 +9520,16 @@ "nx-cloud": "bin/nx-cloud.js" }, "optionalDependencies": { - "@nx/nx-darwin-arm64": "20.0.7", - "@nx/nx-darwin-x64": "20.0.7", - "@nx/nx-freebsd-x64": "20.0.7", - "@nx/nx-linux-arm-gnueabihf": "20.0.7", - "@nx/nx-linux-arm64-gnu": "20.0.7", - "@nx/nx-linux-arm64-musl": "20.0.7", - "@nx/nx-linux-x64-gnu": "20.0.7", - "@nx/nx-linux-x64-musl": "20.0.7", - "@nx/nx-win32-arm64-msvc": "20.0.7", - "@nx/nx-win32-x64-msvc": "20.0.7" + "@nx/nx-darwin-arm64": "20.1.1", + "@nx/nx-darwin-x64": "20.1.1", + "@nx/nx-freebsd-x64": "20.1.1", + "@nx/nx-linux-arm-gnueabihf": "20.1.1", + "@nx/nx-linux-arm64-gnu": "20.1.1", + "@nx/nx-linux-arm64-musl": "20.1.1", + "@nx/nx-linux-x64-gnu": "20.1.1", + "@nx/nx-linux-x64-musl": "20.1.1", + "@nx/nx-win32-arm64-msvc": "20.1.1", + "@nx/nx-win32-x64-msvc": "20.1.1" }, "peerDependencies": { "@swc-node/register": "^1.8.0", @@ -10728,17 +9651,17 @@ } }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -10912,11 +9835,9 @@ } }, "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "dev": true, - "license": "BlueOak-1.0.0" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" }, "node_modules/pacote": { "version": "18.0.6", @@ -10949,166 +9870,11 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/pacote/node_modules/@npmcli/fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", - "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", - "dev": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/pacote/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/pacote/node_modules/cacache": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", - "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", - "dev": true, - "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/pacote/node_modules/fs-minipass": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", - "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/pacote/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/pacote/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/pacote/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/pacote/node_modules/minipass-collect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", - "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/pacote/node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/pacote/node_modules/unique-filename": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", - "dev": true, - "dependencies": { - "unique-slug": "^4.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/pacote/node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -11131,11 +9897,10 @@ } }, "node_modules/parse-imports": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/parse-imports/-/parse-imports-2.1.1.tgz", - "integrity": "sha512-TDT4HqzUiTMO1wJRwg/t/hYk8Wdp3iF/ToMIlAoVQfL1Xs/sTxq1dKWSMjMbQmIarfWKymOyly40+zmPHXMqCA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/parse-imports/-/parse-imports-2.2.1.tgz", + "integrity": "sha512-OL/zLggRp8mFhKL0rNORUTR4yBYujK/uU+xZL+/0Rgm2QE4nLO9v8PzEweSJEbMGKmDRjJE4R3IMJlL2di4JeQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "es-module-lexer": "^1.5.3", "slashes": "^3.0.12" @@ -11233,7 +9998,6 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -11370,9 +10134,9 @@ } }, "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "dev": true, "funding": [ { @@ -11390,7 +10154,7 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { @@ -11460,7 +10224,6 @@ "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", "dev": true, - "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -11599,10 +10362,23 @@ "node": ">=8" } }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dev": true, + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, "node_modules/read": { @@ -11866,9 +10642,9 @@ } }, "node_modules/regex": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/regex/-/regex-4.3.3.tgz", - "integrity": "sha512-r/AadFO7owAq1QJVeZ/nq9jNS1vyZt+6t1p/E59B56Rn2GCya+gr1KSyOzNL/er+r+B7phv5jG2xU2Nz1YkmJg==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-4.4.0.tgz", + "integrity": "sha512-uCUSuobNVeqUupowbdZub6ggI5/JZkYyJdDogddJr60L764oxC2pMZov1fQ3wM9bdyzUILDG+Sqx6NAKAz9rKQ==", "dev": true }, "node_modules/require-directory": { @@ -11937,7 +10713,6 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -11984,7 +10759,6 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", "dev": true, - "license": "ISC", "dependencies": { "glob": "^11.0.0", "package-json-from-dist": "^1.0.0" @@ -12004,7 +10778,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -12014,7 +10787,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", "dev": true, - "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^4.0.1", @@ -12034,11 +10806,10 @@ } }, "node_modules/rimraf/node_modules/jackspeak": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz", - "integrity": "sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -12047,17 +10818,13 @@ }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/rimraf/node_modules/lru-cache": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz", - "integrity": "sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", "dev": true, - "license": "ISC", "engines": { "node": "20 || >=22" } @@ -12067,7 +10834,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -12083,7 +10849,6 @@ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" @@ -12101,9 +10866,9 @@ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" }, "node_modules/rollup": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", - "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.26.0.tgz", + "integrity": "sha512-ilcl12hnWonG8f+NxU6BlgysVA0gvY2l8N0R84S1HcINbW20bvwuCngJkkInV6LXhwRpucsW5k1ovDwEdBVrNg==", "dev": true, "dependencies": { "@types/estree": "1.0.6" @@ -12116,22 +10881,24 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.0", - "@rollup/rollup-android-arm64": "4.24.0", - "@rollup/rollup-darwin-arm64": "4.24.0", - "@rollup/rollup-darwin-x64": "4.24.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", - "@rollup/rollup-linux-arm-musleabihf": "4.24.0", - "@rollup/rollup-linux-arm64-gnu": "4.24.0", - "@rollup/rollup-linux-arm64-musl": "4.24.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", - "@rollup/rollup-linux-riscv64-gnu": "4.24.0", - "@rollup/rollup-linux-s390x-gnu": "4.24.0", - "@rollup/rollup-linux-x64-gnu": "4.24.0", - "@rollup/rollup-linux-x64-musl": "4.24.0", - "@rollup/rollup-win32-arm64-msvc": "4.24.0", - "@rollup/rollup-win32-ia32-msvc": "4.24.0", - "@rollup/rollup-win32-x64-msvc": "4.24.0", + "@rollup/rollup-android-arm-eabi": "4.26.0", + "@rollup/rollup-android-arm64": "4.26.0", + "@rollup/rollup-darwin-arm64": "4.26.0", + "@rollup/rollup-darwin-x64": "4.26.0", + "@rollup/rollup-freebsd-arm64": "4.26.0", + "@rollup/rollup-freebsd-x64": "4.26.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.26.0", + "@rollup/rollup-linux-arm-musleabihf": "4.26.0", + "@rollup/rollup-linux-arm64-gnu": "4.26.0", + "@rollup/rollup-linux-arm64-musl": "4.26.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.26.0", + "@rollup/rollup-linux-riscv64-gnu": "4.26.0", + "@rollup/rollup-linux-s390x-gnu": "4.26.0", + "@rollup/rollup-linux-x64-gnu": "4.26.0", + "@rollup/rollup-linux-x64-musl": "4.26.0", + "@rollup/rollup-win32-arm64-msvc": "4.26.0", + "@rollup/rollup-win32-ia32-msvc": "4.26.0", + "@rollup/rollup-win32-x64-msvc": "4.26.0", "fsevents": "~2.3.2" } }, @@ -12201,9 +10968,9 @@ ] }, "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", "engines": { "node": ">=10" } @@ -12232,21 +10999,19 @@ } }, "node_modules/seroval": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.0.7.tgz", - "integrity": "sha512-n6ZMQX5q0Vn19Zq7CIKNIo7E75gPkGCFUEqDpa8jgwpYr/vScjqnQ6H09t1uIiZ0ZSK0ypEGvrYK2bhBGWsGdw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.1.1.tgz", + "integrity": "sha512-rqEO6FZk8mv7Hyv4UCj3FD3b6Waqft605TLfsCe/BiaylRpyyMC0b+uA5TJKawX3KzMrdi3wsLbCaLplrQmBvQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/seroval-plugins": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.0.7.tgz", - "integrity": "sha512-GO7TkWvodGp6buMEX9p7tNyIkbwlyuAWbI6G9Ec5bhcm7mQdu3JOK1IXbEUwb3FVzSc363GraG/wLW23NSavIw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.1.1.tgz", + "integrity": "sha512-qNSy1+nUj7hsCOon7AO4wdAIo9P0jrzAMp18XhiOzA6/uO5TKtP7ScozVJ8T293oRIvi5wyCHSM4TrJo/c/GJA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -12308,8 +11073,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/signal-exit": { "version": "3.0.7", @@ -12358,8 +11122,7 @@ "version": "3.0.12", "resolved": "https://registry.npmjs.org/slashes/-/slashes-3.0.12.tgz", "integrity": "sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/smart-buffer": { "version": "4.2.0", @@ -12384,28 +11147,28 @@ } }, "node_modules/socks-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", - "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", + "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "dev": true, "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" }, "engines": { - "node": ">= 10" + "node": ">= 14" } }, "node_modules/solid-js": { - "version": "1.8.17", - "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.8.17.tgz", - "integrity": "sha512-E0FkUgv9sG/gEBWkHr/2XkBluHb1fkrHywUgA6o6XolPDCJ4g1HaLmQufcBBhiF36ee40q+HpG/vCZu7fLpI3Q==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.3.tgz", + "integrity": "sha512-5ba3taPoZGt9GY3YlsCB24kCg0Lv/rie/HTD4kG6h4daZZz7+yK02xn8Vx8dLYBc9i6Ps5JwAbEiqjmKaLB3Ag==", "dev": true, - "license": "MIT", "dependencies": { "csstype": "^3.1.0", - "seroval": "^1.0.4", - "seroval-plugins": "^1.0.3" + "seroval": "^1.1.0", + "seroval-plugins": "^1.1.0" } }, "node_modules/sort-keys": { @@ -12485,9 +11248,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", - "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", "dev": true }, "node_modules/speakingurl": { @@ -12526,40 +11289,28 @@ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" }, "node_modules/ssri": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", - "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/ssri/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", + "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "minipass": "^7.0.3" }, "engines": { - "node": ">=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/std-env": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", - "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", - "dev": true, - "license": "MIT" + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", + "dev": true }, "node_modules/string_decoder": { "version": "1.3.0", @@ -12728,9 +11479,9 @@ } }, "node_modules/synckit": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", - "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", "dev": true, "dependencies": { "@pkgr/core": "^0.1.0", @@ -12801,6 +11552,28 @@ "node": ">=6" } }, + "node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tar/node_modules/minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", @@ -12889,8 +11662,7 @@ "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/tinyexec": { "version": "0.3.1", @@ -12899,9 +11671,9 @@ "dev": true }, "node_modules/tinypool": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz", - "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", "dev": true, "engines": { "node": "^18.0.0 || >=20.0.0" @@ -12937,404 +11709,144 @@ "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/topojson-client": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz", - "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==", - "license": "ISC", - "dependencies": { - "commander": "2" - }, - "bin": { - "topo2geo": "bin/topo2geo", - "topomerge": "bin/topomerge", - "topoquantize": "bin/topoquantize" - } - }, - "node_modules/topojson-client/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT" - }, - "node_modules/touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dev": true, - "dependencies": { - "nopt": "~1.0.10" - }, - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, - "node_modules/touch/node_modules/nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", - "dev": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/treeverse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-3.0.0.tgz", - "integrity": "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-json-schema-generator": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-2.3.0.tgz", - "integrity": "sha512-t4lBQAwZc0sOJq9LJt3NgbznIcslVnm0JeEMFq8qIRklpMRY8jlYD0YmnRWbqBKANxkby91P1XanSSlSOFpUmg==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.15", - "commander": "^12.0.0", - "glob": "^10.3.12", - "json5": "^2.2.3", - "normalize-path": "^3.0.0", - "safe-stable-stringify": "^2.4.3", - "tslib": "^2.6.2", - "typescript": "^5.4.5" - }, - "bin": { - "ts-json-schema-generator": "bin/ts-json-schema-generator.js" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/ts-json-schema-generator/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/ts-json-schema-generator/node_modules/commander": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz", - "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==", - "engines": { - "node": ">=18" - } - }, - "node_modules/ts-json-schema-generator/node_modules/glob": { - "version": "10.3.12", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", - "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", - "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.10.2" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ts-json-schema-generator/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "license": "0BSD" - }, - "node_modules/tuf-js": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz", - "integrity": "sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==", - "dev": true, - "dependencies": { - "@tufjs/models": "2.0.1", - "debug": "^4.3.4", - "make-fetch-happen": "^13.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/tuf-js/node_modules/@npmcli/fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", - "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", - "dev": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/tuf-js/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/tuf-js/node_modules/cacache": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", - "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", - "dev": true, - "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/tuf-js/node_modules/fs-minipass": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", - "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "dependencies": { - "minipass": "^7.0.3" + "is-number": "^7.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8.0" } }, - "node_modules/tuf-js/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, + "node_modules/topojson-client": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz", + "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "commander": "2" }, "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "topo2geo": "bin/topo2geo", + "topomerge": "bin/topomerge", + "topoquantize": "bin/topoquantize" } }, - "node_modules/tuf-js/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "node_modules/topojson-client/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "bin": { + "nodetouch": "bin/nodetouch.js" } }, - "node_modules/tuf-js/node_modules/make-fetch-happen": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", - "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/treeverse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-3.0.0.tgz", + "integrity": "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==", "dev": true, - "dependencies": { - "@npmcli/agent": "^2.0.0", - "cacache": "^18.0.0", - "http-cache-semantics": "^4.1.1", - "is-lambda": "^1.0.1", - "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "proc-log": "^4.2.0", - "promise-retry": "^2.0.1", - "ssri": "^10.0.0" - }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/tuf-js/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/tuf-js/node_modules/minipass-collect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", - "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=8" } }, - "node_modules/tuf-js/node_modules/minipass-fetch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", - "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", - "dev": true, + "node_modules/ts-json-schema-generator": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-2.3.0.tgz", + "integrity": "sha512-t4lBQAwZc0sOJq9LJt3NgbznIcslVnm0JeEMFq8qIRklpMRY8jlYD0YmnRWbqBKANxkby91P1XanSSlSOFpUmg==", "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" + "@types/json-schema": "^7.0.15", + "commander": "^12.0.0", + "glob": "^10.3.12", + "json5": "^2.2.3", + "normalize-path": "^3.0.0", + "safe-stable-stringify": "^2.4.3", + "tslib": "^2.6.2", + "typescript": "^5.4.5" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "bin": { + "ts-json-schema-generator": "bin/ts-json-schema-generator.js" }, - "optionalDependencies": { - "encoding": "^0.1.13" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/tuf-js/node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", + "node_modules/ts-json-schema-generator/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "engines": { + "node": ">=18" + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "dev": true, "dependencies": { - "minipass": "^7.0.3" + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=6" } }, - "node_modules/tuf-js/node_modules/unique-filename": { + "node_modules/tsconfig-paths/node_modules/strip-bom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "dependencies": { - "unique-slug": "^4.0.0" - }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/tuf-js/node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/tuf-js": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz", + "integrity": "sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==", "dev": true, "dependencies": { - "imurmurhash": "^0.1.4" + "@tufjs/models": "2.0.1", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/type-check": { @@ -13407,9 +11919,9 @@ "dev": true }, "node_modules/underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", "dev": true }, "node_modules/undici-types": { @@ -13418,25 +11930,27 @@ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "node_modules/unique-filename": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", - "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "dev": true, "dependencies": { - "unique-slug": "^3.0.0" + "unique-slug": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/unique-slug": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", - "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "dev": true, "dependencies": { "imurmurhash": "^0.1.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/unist-util-is": { @@ -13557,15 +12071,16 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/uuid": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.2.tgz", - "integrity": "sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "dev": true, "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], "bin": { - "uuid": "dist/esm/bin/uuid" + "uuid": "dist/bin/uuid" } }, "node_modules/validate-npm-package-license": { @@ -13593,7 +12108,6 @@ "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", "dev": true, - "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -13639,7 +12153,6 @@ "version": "5.30.0", "resolved": "https://registry.npmjs.org/vega/-/vega-5.30.0.tgz", "integrity": "sha512-ZGoC8LdfEUV0LlXIuz7hup9jxuQYhSaWek2M7r9dEHAPbPrzSQvKXZ0BbsJbrarM100TGRpTVN/l1AFxCwDkWw==", - "license": "BSD-3-Clause", "dependencies": { "vega-crossfilter": "~4.1.2", "vega-dataflow": "~5.7.6", @@ -13673,14 +12186,12 @@ "node_modules/vega-canvas": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/vega-canvas/-/vega-canvas-1.2.7.tgz", - "integrity": "sha512-OkJ9CACVcN9R5Pi9uF6MZBF06pO6qFpDYHWSKBJsdHP5o724KrsgR6UvbnXFH82FdsiTOff/HqjuaG8C7FL+9Q==", - "license": "BSD-3-Clause" + "integrity": "sha512-OkJ9CACVcN9R5Pi9uF6MZBF06pO6qFpDYHWSKBJsdHP5o724KrsgR6UvbnXFH82FdsiTOff/HqjuaG8C7FL+9Q==" }, "node_modules/vega-crossfilter": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/vega-crossfilter/-/vega-crossfilter-4.1.2.tgz", "integrity": "sha512-J7KVEXkpfRJBfRvwLxn5vNCzQCNkrnzmDvkvwhuiwT4gPm5sk7MK5TuUP8GCl/iKYw+kWeVXEtrVHwWtug+bcQ==", - "license": "BSD-3-Clause", "dependencies": { "d3-array": "^3.2.2", "vega-dataflow": "^5.7.6", @@ -13691,7 +12202,6 @@ "version": "5.7.6", "resolved": "https://registry.npmjs.org/vega-dataflow/-/vega-dataflow-5.7.6.tgz", "integrity": "sha512-9Md8+5iUC1MVKPKDyZ7pCEHk6I9am+DgaMzZqo/27O/KI4f23/WQXPyuI8jbNmc/mkm340P0TKREmzL5M7+2Dg==", - "license": "BSD-3-Clause", "dependencies": { "vega-format": "^1.1.2", "vega-loader": "^4.5.2", @@ -13699,19 +12209,18 @@ } }, "node_modules/vega-embed": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/vega-embed/-/vega-embed-6.26.0.tgz", - "integrity": "sha512-AZCTdKHDAuhp6TFZRQOOs332tStCwZr/5e4uZMNEuJL69A57cT66NNZJdNiCP6u66REzIToYtMJhMTL9wl5B3A==", - "license": "BSD-3-Clause", + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/vega-embed/-/vega-embed-6.28.0.tgz", + "integrity": "sha512-QCjrNCDZPrSOZPG3UmfFZsd95mUQEZSYAWdoi2TOEnzBv/NzB+BX+Fc6jdpcAHsORn3TqxL0um/jktyjnV88zg==", "dependencies": { "fast-json-patch": "^3.1.1", "json-stringify-pretty-compact": "^3.0.0", - "semver": "^7.6.2", - "tslib": "^2.6.3", + "semver": "^7.6.3", + "tslib": "^2.8.1", "vega-interpreter": "^1.0.5", "vega-schema-url-parser": "^2.2.0", "vega-themes": "^2.15.0", - "vega-tooltip": "^0.34.0" + "vega-tooltip": "^0.35.1" }, "peerDependencies": { "vega": "^5.21.0", @@ -13722,7 +12231,6 @@ "version": "4.10.1", "resolved": "https://registry.npmjs.org/vega-encode/-/vega-encode-4.10.1.tgz", "integrity": "sha512-d25nVKZDrg109rC65M8uxE+7iUrTxktaqgK4fU3XZBgpWlh1K4UbU5nDag7kiHVVN4tKqwgd+synEotra9TiVQ==", - "license": "BSD-3-Clause", "dependencies": { "d3-array": "^3.2.2", "d3-interpolate": "^3.0.1", @@ -13740,7 +12248,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-5.1.1.tgz", "integrity": "sha512-zv9L1Hm0KHE9M7mldHyz8sXbGu3KmC0Cdk7qfHkcTNS75Jpsem6jkbu6ZAwx5cNUeW91AxUQOu77r4mygq2wUQ==", - "license": "BSD-3-Clause", "dependencies": { "@types/estree": "^1.0.0", "vega-util": "^1.17.2" @@ -13750,7 +12257,6 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/vega-force/-/vega-force-4.2.1.tgz", "integrity": "sha512-2BcuuqFr77vcCyKfcpedNFeYMxi+XEFCrlgLWNx7YV0PI8pdP5y/yPkzyuE9Tb894+KkRAvfQHZRAshcnFNcMw==", - "license": "BSD-3-Clause", "dependencies": { "d3-force": "^3.0.0", "vega-dataflow": "^5.7.6", @@ -13761,7 +12267,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vega-format/-/vega-format-1.1.2.tgz", "integrity": "sha512-0kUfAj0dg0U6GcEY0Kp6LiSTCZ8l8jl1qVdQyToMyKmtZg/q56qsiJQZy3WWRr1MtWkTIZL71xSJXgjwjeUaAw==", - "license": "BSD-3-Clause", "dependencies": { "d3-array": "^3.2.2", "d3-format": "^3.1.0", @@ -13774,7 +12279,6 @@ "version": "5.15.0", "resolved": "https://registry.npmjs.org/vega-functions/-/vega-functions-5.15.0.tgz", "integrity": "sha512-pCqmm5efd+3M65jrJGxEy3UGuRksmK6DnWijoSNocnxdCBxez+yqUUVX9o2pN8VxMe3648vZnR9/Vk5CXqRvIQ==", - "license": "BSD-3-Clause", "dependencies": { "d3-array": "^3.2.2", "d3-color": "^3.1.0", @@ -13793,7 +12297,6 @@ "version": "4.4.2", "resolved": "https://registry.npmjs.org/vega-geo/-/vega-geo-4.4.2.tgz", "integrity": "sha512-unuV/UxUHf6UJu6GYxMZonC3SZlMfFXYLOkgEsRSvmsMPt3+CVv8FmG88dXNRUJUrdROrJepgecqx0jOwMSnGA==", - "license": "BSD-3-Clause", "dependencies": { "d3-array": "^3.2.2", "d3-color": "^3.1.0", @@ -13809,7 +12312,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/vega-hierarchy/-/vega-hierarchy-4.1.2.tgz", "integrity": "sha512-m+xDtT5092YPSnV0rdTLW+AWmoCb+A54JQ66MUJwiDBpKxvfKnTiQeuiWDU2YudjUoXZN9EBOcI6QHF8H2Lu2A==", - "license": "BSD-3-Clause", "dependencies": { "d3-hierarchy": "^3.1.2", "vega-dataflow": "^5.7.6", @@ -13825,7 +12327,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/vega-label/-/vega-label-1.3.0.tgz", "integrity": "sha512-EfSFSCWAwVPsklM5g0gUEuohALgryuGC/SKMmsOH7dYT/bywmLBZhLVbrE+IHJAUauoGrMhYw1mqnXL/0giJBg==", - "license": "BSD-3-Clause", "dependencies": { "vega-canvas": "^1.2.7", "vega-dataflow": "^5.7.6", @@ -13858,11 +12359,15 @@ "vega": "^5.24.0" } }, + "node_modules/vega-lite/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, "node_modules/vega-loader": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/vega-loader/-/vega-loader-4.5.2.tgz", "integrity": "sha512-ktIdGz3DRIS3XfTP9lJ6oMT5cKwC86nQkjUbXZbOtwXQFVNE2xVWBuH13GP6FKUZxg5hJCMtb5v/e/fwTvhKsQ==", - "license": "BSD-3-Clause", "dependencies": { "d3-dsv": "^3.0.1", "node-fetch": "^2.6.7", @@ -13875,7 +12380,6 @@ "version": "6.4.0", "resolved": "https://registry.npmjs.org/vega-parser/-/vega-parser-6.4.0.tgz", "integrity": "sha512-/hFIJs0yITxfvLIfhhcpUrcbKvu4UZYoMGmly5PSsbgo60oAsVQW8ZbX2Ji3iNFqZJh1ifoX/P0j+9wep1OISw==", - "license": "BSD-3-Clause", "dependencies": { "vega-dataflow": "^5.7.6", "vega-event-selector": "^3.0.1", @@ -13888,7 +12392,6 @@ "version": "1.6.1", "resolved": "https://registry.npmjs.org/vega-projection/-/vega-projection-1.6.1.tgz", "integrity": "sha512-sqfnAAHumU7MWU1tQN3b6HNgKGF3legek0uLHhjLKcDJQxEc7kwcD18txFz2ffQks6d5j+AUhBiq4GARWf0DEQ==", - "license": "BSD-3-Clause", "dependencies": { "d3-geo": "^3.1.0", "d3-geo-projection": "^4.0.0", @@ -13899,7 +12402,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/vega-regression/-/vega-regression-1.3.0.tgz", "integrity": "sha512-gxOQfmV7Ft/MYKpXDEo09WZyBuKOBqxqDRWay9KtfGq/E0Y4vbTPsWLv2cB1ToPJdKE6XSN6Re9tCIw5M/yMUg==", - "license": "BSD-3-Clause", "dependencies": { "d3-array": "^3.2.2", "vega-dataflow": "^5.7.6", @@ -13911,7 +12413,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/vega-runtime/-/vega-runtime-6.2.0.tgz", "integrity": "sha512-30UXbujWjKNd5aeP+oeHuwFmzuyVYlBj4aDy9+AjfWLECu8wJt4K01vwegcaGPdCWcPLVIv4Oa9Lob4mcXn5KQ==", - "license": "BSD-3-Clause", "dependencies": { "vega-dataflow": "^5.7.6", "vega-util": "^1.17.2" @@ -13921,7 +12422,6 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/vega-scale/-/vega-scale-7.4.1.tgz", "integrity": "sha512-dArA28DbV/M92O2QvswnzCmQ4bq9WwLKUoyhqFYWCltmDwkmvX7yhqiFLFMWPItIm7mi4Qyoygby6r4DKd1X2A==", - "license": "BSD-3-Clause", "dependencies": { "d3-array": "^3.2.2", "d3-interpolate": "^3.0.1", @@ -13935,7 +12435,6 @@ "version": "4.13.0", "resolved": "https://registry.npmjs.org/vega-scenegraph/-/vega-scenegraph-4.13.0.tgz", "integrity": "sha512-nfl45XtuqB5CxyIZJ+bbJ+dofzosPCRlmF+eUQo+0J23NkNXsTzur+1krJDSdhcw0SOYs4sbYRoMz1cpuOM4+Q==", - "license": "BSD-3-Clause", "dependencies": { "d3-path": "^3.1.0", "d3-shape": "^3.2.0", @@ -13954,7 +12453,6 @@ "version": "5.4.2", "resolved": "https://registry.npmjs.org/vega-selections/-/vega-selections-5.4.2.tgz", "integrity": "sha512-99FUhYmg0jOJr2/K4TcEURmJRkuibrCDc8KBUX7qcQEITzrZ5R6a4QE+sarCvbb3hi8aA9GV2oyST6MQeA9mgQ==", - "license": "BSD-3-Clause", "dependencies": { "d3-array": "3.2.4", "vega-expression": "^5.0.1", @@ -13965,7 +12463,6 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/vega-statistics/-/vega-statistics-1.9.0.tgz", "integrity": "sha512-GAqS7mkatpXcMCQKWtFu1eMUKLUymjInU0O8kXshWaQrVWjPIO2lllZ1VNhdgE0qGj4oOIRRS11kzuijLshGXQ==", - "license": "BSD-3-Clause", "dependencies": { "d3-array": "^3.2.2" } @@ -13974,7 +12471,6 @@ "version": "2.15.0", "resolved": "https://registry.npmjs.org/vega-themes/-/vega-themes-2.15.0.tgz", "integrity": "sha512-DicRAKG9z+23A+rH/3w3QjJvKnlGhSbbUXGjBvYGseZ1lvj9KQ0BXZ2NS/+MKns59LNpFNHGi9us/wMlci4TOA==", - "license": "BSD-3-Clause", "peerDependencies": { "vega": "*", "vega-lite": "*" @@ -13984,7 +12480,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/vega-time/-/vega-time-2.1.2.tgz", "integrity": "sha512-6rXc6JdDt8MnCRy6UzUCsa6EeFycPDmvioMddLfKw38OYCV8pRQC5nw44gyddOwXgUTJLiCtn/sp53P0iA542A==", - "license": "BSD-3-Clause", "dependencies": { "d3-array": "^3.2.2", "d3-time": "^3.1.0", @@ -13992,18 +12487,20 @@ } }, "node_modules/vega-tooltip": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/vega-tooltip/-/vega-tooltip-0.34.0.tgz", - "integrity": "sha512-TtxwkcLZ5aWQTvKGlfWDou8tISGuxmqAW1AgGZjrDpf75qsXvgtbPdRAAls2LZMqDxpr5T1kMEZs9XbSpiI8yw==", + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/vega-tooltip/-/vega-tooltip-0.35.1.tgz", + "integrity": "sha512-KpPa3H0tE3bTr0DFho1qJUygRLmVy8+iAnEnJvPaQIdI6QBcvFqrkHoA23QhTFtRLNdHaGzmuxoJNWJ1TcIyzg==", "dependencies": { "vega-util": "^1.17.2" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "^4.24.4" } }, "node_modules/vega-transforms": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/vega-transforms/-/vega-transforms-4.12.0.tgz", "integrity": "sha512-bh/2Qbj85O70mjfLRgPKAsABArgSUP0k+GjmaY54zukIRxoGxKju+85nigeX/aR/INpEqNWif+5lL+NvmyWA5w==", - "license": "BSD-3-Clause", "dependencies": { "d3-array": "^3.2.2", "vega-dataflow": "^5.7.6", @@ -14016,7 +12513,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/vega-typings/-/vega-typings-1.3.1.tgz", "integrity": "sha512-j9Sdgmvowz09jkMgTFGVfiv7ycuRP/TQkdHRPXIYwt3RDgPQn7inyFcJ8C8ABFt4MiMWdjOwbneF6KWW8TRXIw==", - "license": "BSD-3-Clause", "dependencies": { "@types/geojson": "7946.0.4", "vega-event-selector": "^3.0.1", @@ -14033,7 +12529,6 @@ "version": "5.13.0", "resolved": "https://registry.npmjs.org/vega-view/-/vega-view-5.13.0.tgz", "integrity": "sha512-ZPAAQ3iYz6YrQjJoDT+0bcxJkXt9PKF5v4OO7Omw8PFhkIv++jFXeKlQTW1bBtyQ92dkdGGHv5lYY67Djqjf3A==", - "license": "BSD-3-Clause", "dependencies": { "d3-array": "^3.2.2", "d3-timer": "^3.0.1", @@ -14049,7 +12544,6 @@ "version": "4.6.0", "resolved": "https://registry.npmjs.org/vega-view-transforms/-/vega-view-transforms-4.6.0.tgz", "integrity": "sha512-z3z66aJTA3ZRo4oBY4iBXnn+A4KqBGZT/UrlKDbm+7Ec+Ip+hK2tF8Kmhp/WNcMsDZoUWFqLJgR2VgOgvJk9RA==", - "license": "BSD-3-Clause", "dependencies": { "vega-dataflow": "^5.7.6", "vega-scenegraph": "^4.13.0", @@ -14060,7 +12554,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/vega-voronoi/-/vega-voronoi-4.2.3.tgz", "integrity": "sha512-aYYYM+3UGqwsOx+TkVtF1IZfguy0H7AN79dR8H0nONRIc+vhk/lbnlkgwY2nSzEu0EZ4b5wZxeGoDBEVmdDEcg==", - "license": "BSD-3-Clause", "dependencies": { "d3-delaunay": "^6.0.2", "vega-dataflow": "^5.7.6", @@ -14071,7 +12564,6 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/vega-wordcloud/-/vega-wordcloud-4.1.5.tgz", "integrity": "sha512-p+qXU3cb9VeWzJ/HEdax0TX2mqDJcSbrCIfo2d/EalOXGkvfSLKobsmMQ8DxPbtVp0uhnpvfCGDyMJw+AzcI2A==", - "license": "BSD-3-Clause", "dependencies": { "vega-canvas": "^1.2.7", "vega-dataflow": "^5.7.6", @@ -14109,9 +12601,9 @@ } }, "node_modules/vite": { - "version": "5.4.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", - "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", "dev": true, "dependencies": { "esbuild": "^0.21.3", @@ -14168,13 +12660,14 @@ } }, "node_modules/vite-node": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.4.tgz", - "integrity": "sha512-kqa9v+oi4HwkG6g8ufRnb5AeplcRw8jUF6/7/Qz1qRQOXHImG8YnLbB+LLszENwFnoBl9xIf9nVdCFzNd7GQEg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.5.tgz", + "integrity": "sha512-rd0QIgx74q4S1Rd56XIiL2cYEdyWn13cunYBIuqh9mpmQr7gGS0IxXoP8R6OaZtNQQLyXSWbd4rXKYUbhFpK5w==", "dev": true, "dependencies": { "cac": "^6.7.14", "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", "pathe": "^1.1.2", "vite": "^5.0.0" }, @@ -14196,7 +12689,6 @@ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "aix" @@ -14213,7 +12705,6 @@ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -14230,7 +12721,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -14247,7 +12737,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -14264,7 +12753,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -14281,7 +12769,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -14298,7 +12785,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -14315,7 +12801,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -14332,7 +12817,6 @@ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -14349,7 +12833,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -14366,7 +12849,6 @@ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -14383,7 +12865,6 @@ "loong64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -14400,7 +12881,6 @@ "mips64el" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -14417,7 +12897,6 @@ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -14434,7 +12913,6 @@ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -14451,7 +12929,6 @@ "s390x" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -14468,7 +12945,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -14485,7 +12961,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "netbsd" @@ -14502,7 +12977,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openbsd" @@ -14519,7 +12993,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "sunos" @@ -14536,7 +13009,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -14553,7 +13025,6 @@ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -14570,7 +13041,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -14585,7 +13055,6 @@ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, - "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -14659,30 +13128,30 @@ } }, "node_modules/vitest": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.4.tgz", - "integrity": "sha512-eDjxbVAJw1UJJCHr5xr/xM86Zx+YxIEXGAR+bmnEID7z9qWfoxpHw0zdobz+TQAFOLT+nEXz3+gx6nUJ7RgmlQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.5.tgz", + "integrity": "sha512-P4ljsdpuzRTPI/kbND2sDZ4VmieerR2c9szEZpjc+98Z9ebvnXmM5+0tHEKqYZumXqlvnmfWsjeFOjXVriDG7A==", "dev": true, "dependencies": { - "@vitest/expect": "2.1.4", - "@vitest/mocker": "2.1.4", - "@vitest/pretty-format": "^2.1.4", - "@vitest/runner": "2.1.4", - "@vitest/snapshot": "2.1.4", - "@vitest/spy": "2.1.4", - "@vitest/utils": "2.1.4", + "@vitest/expect": "2.1.5", + "@vitest/mocker": "2.1.5", + "@vitest/pretty-format": "^2.1.5", + "@vitest/runner": "2.1.5", + "@vitest/snapshot": "2.1.5", + "@vitest/spy": "2.1.5", + "@vitest/utils": "2.1.5", "chai": "^5.1.2", "debug": "^4.3.7", "expect-type": "^1.1.0", "magic-string": "^0.30.12", "pathe": "^1.1.2", - "std-env": "^3.7.0", + "std-env": "^3.8.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.1", "tinypool": "^1.0.1", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.1.4", + "vite-node": "2.1.5", "why-is-node-running": "^2.3.0" }, "bin": { @@ -14697,8 +13166,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.4", - "@vitest/ui": "2.1.4", + "@vitest/browser": "2.1.5", + "@vitest/ui": "2.1.5", "happy-dom": "*", "jsdom": "*" }, @@ -14792,7 +13261,6 @@ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, - "license": "MIT", "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" @@ -14812,6 +13280,15 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -14973,7 +13450,6 @@ "version": "8.18.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -15070,12 +13546,12 @@ } }, "node_modules/yjs": { - "version": "13.6.14", - "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.14.tgz", - "integrity": "sha512-D+7KcUr0j+vBCUSKXXEWfA+bG4UQBviAwP3gYBhkstkgwy5+8diOPMx0iqLIOxNo/HxaREUimZRxqHGAHCL2BQ==", + "version": "13.6.20", + "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.20.tgz", + "integrity": "sha512-Z2YZI+SYqK7XdWlloI3lhMiKnCdFCVC4PchpdO+mCYwtiTwncjUbnRK9R1JmkNfdmHyDXuWN3ibJAt0wsqTbLQ==", "dev": true, "dependencies": { - "lib0": "^0.2.86" + "lib0": "^0.2.98" }, "engines": { "node": ">=16.0.0", @@ -15177,11 +13653,6 @@ "version": "0.11.0", "license": "BSD-3-Clause" }, - "packages/sql-old": { - "name": "@uwdata/mosaic-sql-old", - "version": "0.11.0", - "license": "BSD-3-Clause" - }, "packages/vega-example": { "name": "mosaic-vega-example", "version": "0.11.0", @@ -15221,6 +13692,18 @@ "devDependencies": { "anywidget": "^0.9.13" } + }, + "packages/widget/node_modules/uuid": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", + "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/esm/bin/uuid" + } } } } From 1cb598046696aefa5537892e851789ee207989ad Mon Sep 17 00:00:00 2001 From: jheer Date: Thu, 14 Nov 2024 19:04:45 -0800 Subject: [PATCH 10/12] chore: clean up and better comment sql func regexp. --- packages/sql/src/visit/visitors.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/sql/src/visit/visitors.js b/packages/sql/src/visit/visitors.js index e5d09f9e..fca7131c 100644 --- a/packages/sql/src/visit/visitors.js +++ b/packages/sql/src/visit/visitors.js @@ -4,8 +4,13 @@ import { ColumnRefNode } from '../ast/column-ref.js'; import { SQLNode } from '../ast/node.js'; import { walk } from './walk.js'; +// regexp to match valid aggregate function names const aggrRegExp = new RegExp(`^(${aggregateNames.join('|')})`); -const funcRegExp = /(\\'|\\"|"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\$\w+|\w+\()/g; + +// regexp to tokenize sql text in order to find function calls +// includes checks to avoid analyzing text within quoted strings +// function call tokens will have a pattern like "name(". +const funcRegExp = /(\\'|\\"|"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\w+\()/g; function hasVerbatimAggregate(s) { return s From 7ed7c1c823b1481ac79986f389e2943ced2cbe5e Mon Sep 17 00:00:00 2001 From: jheer Date: Thu, 14 Nov 2024 19:10:08 -0800 Subject: [PATCH 11/12] fix: Fix aggr function name test regexp. --- packages/sql/src/visit/visitors.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sql/src/visit/visitors.js b/packages/sql/src/visit/visitors.js index fca7131c..1dbecdb1 100644 --- a/packages/sql/src/visit/visitors.js +++ b/packages/sql/src/visit/visitors.js @@ -5,7 +5,7 @@ import { SQLNode } from '../ast/node.js'; import { walk } from './walk.js'; // regexp to match valid aggregate function names -const aggrRegExp = new RegExp(`^(${aggregateNames.join('|')})`); +const aggrRegExp = new RegExp(`^(${aggregateNames.join('|')})$`); // regexp to tokenize sql text in order to find function calls // includes checks to avoid analyzing text within quoted strings @@ -15,7 +15,7 @@ const funcRegExp = /(\\'|\\"|"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\w+\()/g; function hasVerbatimAggregate(s) { return s .split(funcRegExp) - .some(tok => tok.endsWith('(') && aggrRegExp.test(tok)); + .some(tok => tok.endsWith('(') && aggrRegExp.test(tok.slice(0, -1))); } /** From 41dfe32f46ed85429273ce32214b2dc5985699f0 Mon Sep 17 00:00:00 2001 From: jheer Date: Fri, 15 Nov 2024 11:15:33 -0800 Subject: [PATCH 12/12] docs: Update SQL docs. --- docs/api/sql/aggregate-functions.md | 48 +++++++----------- docs/api/sql/expressions.md | 72 ++++---------------------- docs/api/sql/operators.md | 6 +++ docs/api/sql/queries.md | 56 ++++++++++---------- docs/api/sql/window-functions.md | 34 ++++--------- docs/sql/index.md | 79 +++++++++++++---------------- 6 files changed, 109 insertions(+), 186 deletions(-) diff --git a/docs/api/sql/aggregate-functions.md b/docs/api/sql/aggregate-functions.md index 2c1022e0..48e0a01e 100644 --- a/docs/api/sql/aggregate-functions.md +++ b/docs/api/sql/aggregate-functions.md @@ -2,60 +2,46 @@ SQL aggregate function expressions. -## AggregateFunction {#aggregate-function} +## AggregateNode {#aggregate-node} -The `AggregateFunction` class represents an aggregate function. -It includes a non-null `aggregate` property indicating an aggregate expression. -Users should not need to instantiate `AggregateFunction` instances directly, but instead should use aggregate function methods such as [`count()`](#count), [`sum()`](#sum), _etc_. - -### basis - -`AggregateFunction.basis` - -The `basis` property indicates an underlying table column that can serve as a selection target for this aggregate operation. - - -### label - -`AggregateFunction.label` - -The `label` property provides a descriptive text label. +The `AggregateNode` class represents a SQL AST node for an aggregate function call. +Users should not need to instantiate `AggregateNode` instances directly, but instead should use aggregate function methods such as [`count()`](#count), [`sum()`](#sum), _etc_. ### distinct -`AggregateFunction.distinct()` +`AggregateNode.distinct()` -Returns a new AggregateFunction instance that applies the aggregation over distinct values only. +Returns a new AggregateNode instance that applies the aggregation over distinct values only. ### where -`AggregateFunction.where(filter)` +`AggregateNode.where(filter)` -Returns a new AggregateFunction instance filtered according to a Boolean-valied _filter_ expression. +Returns a new AggregateNode instance filtered according to a Boolean-valied _filter_ expression. ### window -`AggregateFunction.window()` +`AggregateNode.window()` -Returns a windowed version of this aggregate function as a new [WindowFunction](./window-functions#window-function) instance. +Returns a windowed version of this aggregate function as a new [WindowNode](./window-functions#window-node) instance. ### partitionby -`AggregateFunction.partitionby(...expressions)` +`AggregateNode.partitionby(...expressions)` -Provide one or more _expressions_ by which to partition a windowed version of this aggregate function and returns a new [WindowFunction](./window-functions#window-function) instance. +Provide one or more _expressions_ by which to partition a windowed version of this aggregate function and returns a new [WindowNode](./window-functions#window-node) instance. ### orderby -`AggregateFunction.orderby(...expressions)` +`AggregateNode.orderby(...expressions)` -Provide one or more _expressions_ by which to sort a windowed version of this aggregate function and returns a new [WindowFunction](./window-functions#window-function) instance. +Provide one or more _expressions_ by which to sort a windowed version of this aggregate function and returns a new [WindowNodw](./window-functions#window-node) instance. ### rows -`WindowFunction.rows(expression)` +`AggregateNode.rows(expression)` -Provide a window "rows" frame specification as an array or array-valued _expression_ and returns a windowed version of this aggregate function as a new [WindowFunction](./window-functions#window-function) instance. +Provide a window "rows" frame specification as an array or array-valued _expression_ and returns a windowed version of this aggregate function as a new [WindowNode](./window-functions#window-node) instance. A "rows" window frame is insensitive to peer rows (those that are tied according to the [orderby](#orderby) criteria). The frame expression should evaluate to a two-element array indicating the number of preceding or following rows. A zero value (`0`) indicates the current row. @@ -63,9 +49,9 @@ A non-finite value (including `null` and `undefined`) indicates either unbounded ### range -`WindowFunction.range(expression)` +`AggregateNode.range(expression)` -Provide a window "range" frame specification as an array or array-valued _expression_ and returns a windowed version of this aggregate function as a new [WindowFunction](./window-functions#window-function) instance. +Provide a window "range" frame specification as an array or array-valued _expression_ and returns a windowed version of this aggregate function as a new [WindowNode](./window-functions#window-node) instance. A "range" window grows to include peer rows (those that are tied according to the [orderby](#orderby) criteria). The frame expression should evaluate to a two-element array indicating the number of preceding or following rows. A zero value (`0`) indicates the current row. diff --git a/docs/api/sql/expressions.md b/docs/api/sql/expressions.md index d5bd79f8..c370a703 100644 --- a/docs/api/sql/expressions.md +++ b/docs/api/sql/expressions.md @@ -1,88 +1,38 @@ # SQL Expressions -SQL expression builders. +SQL expression builders. All SQL expressions are represented in the form of an abstract syntax tree (AST). Helper methods and functions build out this tree. ## column `column(name)` -Create an expression that references a column by _name_. +Create an expression AST node that references a column by _name_. Upon string coercion, the column name will be properly quoted. ## literal `literal(value)` -Create an expression that references a literal _value_. +Create an expression AST node that references a literal _value_. Upon string coercion, an appropriate SQL value will be produced. -For example, string literals will be properly quoted and JavaScript `Date` objects that match an extact UTC date will be converted to the SQL statement `MAKE_DATE(year, month, day)`. +For example, string literals will be properly quoted and JavaScript `Date` objects that match an extact UTC date will be converted to the SQL Date definitions. +The supported primitive types are: boolean, null, number, string, regexp, and Date (maps to SQL Date or Timestamp depending on the value). ## sql ``sql`...` `` -A template tag for arbitrary SQL expressions. +A template tag for arbitrary SQL expressions that do not require deep analysis. +Creates an expression AST node with only a partially structured form consisting of unstructured text and interpolated values. Interpolated values may be strings, other SQL expressions (such as [`column` references](#column) or [operators](./operators)), or [`Param`](../core/param) values. - -The snippet below creates a dynamic expression that adds a Param value to a column. The resulting expression will track the column dependency and expose an [`addEventListener`](#addeventlistener) method for tracking param changes. +The snippet below creates a dynamic expression that adds a Param value to a column. +Contained column references can be extracted using the `collectColumns` method. +Contained Param values can be extracted using the `collectParams` method. ``` js const param = Param.value(5); sql`${column("foo")} + ${param}` ``` -SQL expressions may be nested, in which case all nested column dependencies and parameter updates will propagate to the top-level expression. - -## agg - -``agg`...` `` - -A template tag for aggregate SQL expressions. -This method is similar to [`sql`](#sql), but additionally annotates the resulting expression with an `aggregate` property to indicate that it is an aggregate expression. -This is valuable for helping downstream tools provide a cursory query analysis. - -## SQLExpression - -`new SQLExpression(spans, column, props)` - -The `SQLExpression` class provides a structured object format for SQL expressions. -Typically you will not want to create an expression using the class constructor, but instead use more convenient, high-level methods such as those above. - -The constructor takes three arguments: - -- _parts_: an ordered array of expression components, which may include strings, sub-expressions, or Param values. When "concatenated" together, these parts should form the full expression. -- _columns_: an array column name strings, indicating columns the expression depends on. Note that if a column is provided only via raw strings, that dependency will not be tracked. -- _props_: an optional object of key-value pairs with which to annotate the resulting expression object. For example, a non-null `aggregate` property will indicate an aggregate expression. Different expression generators may include different annotations to track state and simplify downstream analysis. - -### columns - -`SQLExpression.columns` - -The `columns` property returns an array of tracked column dependencies. -The column list is de-duplicated, and includes dynamic dependencies that may be due to Param-valued expression components. - -### column - -`SQLExpression.column` - -A convenience property for accessing the first column in the [`columns`](#columns) array, or undefined if there is no such column. - -### annotate - -`SQLExpression.annotate(props)` - -Annotate this expression instance with additional properties and return this expression instance. - -### toString - -`SQLExpression.toString()` - -Returns a SQL expression string based on the current state of this expression instance. - -### addEventListener - -`SQLExpression.addEventListener(type, callback)` - -If an expression includes any Param values, it will expose this method. -Expression updates are broadcast using the `"value"` event type. +SQL expressions may be nested, in which case all nested column dependencies and parameter updates are still extractable via the collection visitors. diff --git a/docs/api/sql/operators.md b/docs/api/sql/operators.md index 24caa610..72845c38 100644 --- a/docs/api/sql/operators.md +++ b/docs/api/sql/operators.md @@ -99,3 +99,9 @@ Equivalent to `lo <= expression AND expression <= hi`. Returns an expression testing if the input _expression_ does not lie between the values _lo_ and _hi_, provided as a two-element array. Equivalent to `NOT(lo <= expression AND expression <= hi)`. + +## isIn + +`isIn(expression, values)` + +Returns an expression testing if the input _expression_ matches any of the entries in the _values_ array. Maps to `expression IN (...values)`. diff --git a/docs/api/sql/queries.md b/docs/api/sql/queries.md index fbc5fc6d..7194b766 100644 --- a/docs/api/sql/queries.md +++ b/docs/api/sql/queries.md @@ -40,10 +40,10 @@ To learn more about the anatomy of a query, take a look at the [DuckDB Select st ## Query -The `Query` and related `SetOperation` classes provide structured representations of SQL queries. +The top-level `Query` class, along with its concrete `SelectQuery` and `SetOperation` subclasses, provide structured representations of SQL queries. Upon string coercion, these objects produce a complete SQL query string. -The following static methods create a new `Query` and invoke the corresponding method: +The following static methods create a new `SelectQuery` and invoke the corresponding method: - `Query.select()`: See the [`select`](#select) method below. - `Query.from()`: See the [`from`](#from) method below. @@ -62,20 +62,32 @@ To instead create a query for metadata (column names and types), pass a query to ## clone -`query.clone()` +`Query.clone()` Return a new query that is a shallow copy of the current instance. +## subqueries + +`Query.subqueries` + +The `subqueries` getter property returns an array of subquery instances, or an empty array if there are no subqueries. For selection queries, the subqueries may include common table expressions within `WITH` or nested queries within `FROM`. For set operations, the subqueries are the set operation arguments. + +## toString + +`Query.toString()` + +Coerce this query object to a SQL query string. + ## select -`query.select(...expressions)` +`SelectQuery.select(...expressions)` Select columns and return this query instance. The _expressions_ argument may include column name strings, [`column` references](./expressions#column), and maps from column names to expressions (either as JavaScript `object` values or nested key-value arrays as produced by `Object.entries`). ## from -`query.from(...tables)` +`SelectQuery.from(...tables)` Indicate the tables to draw records from and return this query instance. The _tables_ may be table name strings, queries or subquery expressions, and maps from table names to expressions (either as JavaScript `object` values or nested key-value arrays as produced by `Object.entries`). @@ -89,13 +101,13 @@ The input _expressions_ should consist of one or more maps (as JavaScript `objec ## distinct -`query.distinct()` +`SelectQuery.distinct(value = true)` -Update the query to require `DISTINCT` values only and return this query instance. +Update the query to require `DISTINCT` values and return this query instance. ## sample -`query.sample(size, method)` +`SelectQuery.sample(size, method)` Update the query to sample a subset of _rows_ and return this query instance. If _size_ is a number between 0 and 1, it is interpreted as a percentage of the full dataset to sample. @@ -105,21 +117,21 @@ See the [DuckDB Sample documentation](https://duckdb.org/docs/sql/samples) for m ## where -`query.where(...expressions)` +`SelectQuery.where(...expressions)` Update the query to additionally filter by the provided predicate _expressions_ and return this query instance. This method is additive: any previously defined filter criteria will still remain. ## groupby -`query.groupby(...expressions)` +`SelectQuery.groupby(...expressions)` Update the query to additionally group by the provided _expressions_ and return this query instance. This method is additive: any previously defined group by criteria will still remain. ## having -`query.having(...expressions)` +`SelectQuery.having(...expressions)` Update the query to additionally filter aggregate results by the provided predicate _expressions_ and return this query instance. Unlike `where` criteria, which are applied before an aggregation, the `having` criteria are applied to aggregated results. @@ -127,7 +139,7 @@ This method is additive: any previously defined filter criteria will still remai ## window -`query.window(...expressions)` +`SelectQuery.window(...expressions)` Update the query with named window frame definitions and return this query instance. The _expressions_ arguments should be JavaScript `object` values that map from window names to window frame definitions. @@ -135,7 +147,7 @@ This method is additive: any previously defined windows will still remain. ## qualify -`query.qualify(...expressions)` +`SelectQuery.qualify(...expressions)` Update the query to additionally filter windowed results by the provided predicate _expressions_ and return this query instance. Use this method instead of `where` to filter the results of window operations. @@ -143,31 +155,19 @@ This method is additive: any previously defined filter criteria will still remai ## orderby -`query.orderby(...expressions)` +`SelectQuery.orderby(...expressions)` Update the query to additionally order results by the provided _expressions_ and return this query instance. This method is additive: any previously defined sort criteria will still remain. ## limit -`query.limit(rows)` +`SelectQuery.limit(rows)` Update the query to limit results to the specified number of _rows_ and return this query instance. ## offset -`query.offset(rows)` +`SelectQuery.offset(rows)` Update the query to offset the results by the specified number of _rows_ and return this query instance. - -## subqueries - -`query.subqueries` - -The `subqueries` getter property returns an array of subquery instances, or an empty array if there are no subqueries. - -## toString - -`query.toString()` - -Coerce this query object to a SQL query string. diff --git a/docs/api/sql/window-functions.md b/docs/api/sql/window-functions.md index b6c61edf..bb352b3f 100644 --- a/docs/api/sql/window-functions.md +++ b/docs/api/sql/window-functions.md @@ -2,48 +2,36 @@ SQL window function expressions. -## WindowFunction {#window-function} +## WindowNode {#window-node} -The `WindowFunction` class represents a window function. +The `WindowNode` class represents a window function. It includes a non-null `window` property indicating a window expression. -Users should not need to instantiate `WindowFunction` instances directly, but instead should use window function methods such as [`row_number()`](#row_number), [`lag()`](#lag), _etc_. - -### basis - -`WindowFunction.basis` - -The `basis` property indicates an underlying table column that can serve as a selection target for this window operation. - -### label - -`WindowFunction.label` - -The `label` property provides a descriptive text label. +Users should not need to instantiate `WindowNode` instances directly, but instead should use window function methods such as [`row_number()`](#row_number), [`lag()`](#lag), _etc_. ### over -`WindowFunction.over(name)` +`WindowNode.over(name)` -Provide the _name_ of a window definition for this function and returns a new WindowFunction instance. +Provide the _name_ of a window definition for this function and returns a new WindowNode instance. The window should be defined separately in an issued query, for example using the [Query.window](./queries#window) method. ### partitionby -`WindowFunction.partitionby(...expressions)` +`WindowNode.partitionby(...expressions)` Provide one or more _expressions_ by which to partition this window function and returns a new WindowFunction instance. ### orderby -`WindowFunction.orderby(...expressions)` +`WindowNode.orderby(...expressions)` Provide one or more _expressions_ by which to sort this window function and returns a new WindowFunction instance. ### rows -`WindowFunction.rows(expression)` +`WindowNode.rows(expression)` -Provide a window "rows" frame specification as an array or array-valued _expression_ and returns a new WindowFunction instance. +Provide a window "rows" frame specification as an array or array-valued _expression_ and returns a new WindowNode instance. A "rows" window frame is insensitive to peer rows (those that are tied according to the [orderby](#orderby) criteria). The frame expression should evaluate to a two-element array indicating the number of preceding or following rows. A zero value (`0`) indicates the current row. @@ -51,9 +39,9 @@ A non-finite value (including `null` and `undefined`) indicates either unbounded ### range -`WindowFunction.range(expression)` +`WindowNode.range(expression)` -Provide a window "range" frame specification as an array or array-valued _expression_ and returns a new WindowFunction instance. +Provide a window "range" frame specification as an array or array-valued _expression_ and returns a new WindowNode instance. A "range" window grows to include peer rows (those that are tied according to the [orderby](#orderby) criteria). The frame expression should evaluate to a two-element array indicating the number of preceding or following rows. A zero value (`0`) indicates the current row. diff --git a/docs/sql/index.md b/docs/sql/index.md index 07d0dbce..1da81caf 100644 --- a/docs/sql/index.md +++ b/docs/sql/index.md @@ -36,56 +36,21 @@ Query [Query API Reference](/api/sql/queries) -## SQL Expressions - -The `sql` template literal builds individual SQL expressions. - -``` js -import { Query, sql } from "@uwdata/mosaic-sql"; -Query - .select({ logFoo: sql`log(foo + 1)` }) - .from("myTable"); -``` - -Interpolated values may include column references, nested expressions, or params. -The resulting expression will keep track of referenced columns. -Expressions that contain a [`Param`](/core/#params) will listen for updates and act like params of their own, including support for `value` event listeners: - -``` js -import { Param } from "@uwdata/core"; -import { column, sql } from "@uwdata/mosaic-sql"; - -const col = column("foo"); -const param = Param.value(Math.PI); -const expr = sql`${col} * ${param}`; - -expr.addEventListener("value", v => console.log("Update!", v)); -param.update(Math.E); // --> expr will invoke value listeners -``` - -The `agg` template literal is similar to `sql`, but additionally flags the result as an aggregate expression. -This annotation can guide query construction and analysis. - -``` js -import { Query, agg, column } from "@uwdata/mosaic-sql"; -const foo = column("foo"); -const bar = column("bar"); -Query - .from("myTable") - .select({ hi: agg`GREATEST(MAX(${foo}), MAX(${bar}))` }); -``` - -[Expression API Reference](/api/sql/expressions) - ## Operators -The Mosaic SQL package includes operators for comparing values: +The Mosaic SQL package includes operators for manipulating and comparing values: - Logical operators: `and`, `or`, `not` - Comparison operators: `eq`, `neq`, `lt`, `gt`, `lte`, `gte` - Range operators: `isBetween`, `isNotBetween` +- Set operators: `isIn` - Null checks: `isNull`, `isNotNull` - Null-sensitive comparison: `isDistinct`, `isNotDistinct` +- Arithmetic operators: `add`, `sub`, `mul`, `div`, `idiv`, `mod`, `pow` +- Bitwise operators: `bitNot`, `bitAnd`, `bitOr`, `bitLeft`, `bitRight` +- Conditionals: `cond` + +When given a string input, an operator function interprets it as a column name. For example, to create a simple range query: @@ -101,7 +66,7 @@ Query ## Aggregate Functions -DuckDB-supported [aggregate functions](https://duckdb.org/docs/sql/aggregates.html), including `min`, `max`, `count`, `sum`, `avg`, `stddev`, `median`, `quantile`, `argmax`, and `argmin`. +DuckDB-supported [aggregate functions](https://duckdb.org/docs/sql/aggregates.html), including `min`, `max`, `count`, `sum`, `avg`, `stddev`, `median`, `quantile`, `argmax`, `argmin`, `corr`, and others. ``` js import { Query, max, min, quantile } from "@uwdata/mosaic-sql"; @@ -136,6 +101,34 @@ Query.select({ [Window Functions API Reference](/api/sql/window-functions) +## SQL Expressions + +When deeper analysis is not needed, the `sql` template literal can be used to build individual SQL expressions using custom text. + +``` js +import { Query, sql } from "@uwdata/mosaic-sql"; +Query + .select({ logFoo: sql`log(foo + 1)` }) + .from("myTable"); +``` + +Interpolated values may include column references, nested expressions, or params. +Referenced columns or params can later be extracted using SQL expression visitors. + +``` js +import { Param } from "@uwdata/core"; +import { collectColumns, collectParams, column, sql } from "@uwdata/mosaic-sql"; + +const col = column("foo"); +const param = Param.value(Math.PI); +const expr = sql`${col} * ${param}`; + +const cols = collectColumns(expr); // -> [ col ] +const params = collectParams(expr): // -> [ param ] +``` + +[Expression API Reference](/api/sql/expressions) + ## Data Loading Data loading helpers generate query strings for loading data from external CSV, JSON, or Parquet files: `loadCSV`, `loadJSON`, `loadParquet`.