diff --git a/LICENSE b/LICENSE
index 09fc9181..e39f7d27 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
BSD 3-Clause License
-Copyright (c) 2020-2022, UW Interactive Data Lab
+Copyright (c) 2020-2024, UW Interactive Data Lab
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/README.md b/README.md
index 8d506a5f..689a5559 100644
--- a/README.md
+++ b/README.md
@@ -93,13 +93,9 @@ Arquero uses modern JavaScript features, and so will not work with some outdated
### In Node.js or Application Bundles
-First install `arquero` as a dependency, for example via `npm install arquero --save`. Arquero assumes Node version 12 or higher.
-
-Import using CommonJS module syntax:
-
-```js
-const aq = require('arquero');
-```
+First install `arquero` as a dependency, for example via `npm install arquero --save`.
+Arquero assumes Node version 18 or higher.
+As of Arquero version 6, the library uses type `module` and should be loaded using ES module syntax.
Import using ES module syntax, import all exports into a single object:
@@ -113,6 +109,12 @@ Import using ES module syntax, with targeted imports:
import { op, table } from 'arquero';
```
+Dynamic import (e.g., within a Node.js REPL):
+
+```js
+aq = await import('arquero');
+```
+
## Build Instructions
To build and develop Arquero locally:
diff --git a/docs/api/expressions.md b/docs/api/expressions.md
index ea0d0d5a..cd819764 100644
--- a/docs/api/expressions.md
+++ b/docs/api/expressions.md
@@ -142,14 +142,10 @@ So why do we do this? Here are a few reasons:
* **Performance**. After parsing an expression, Arquero performs code generation, often creating more performant code in the process. This level of indirection also allows us to generate optimized expressions for certain inputs, such as Apache Arrow data.
-* **Flexibility**. Providing our own parsing also allows us to introduce new kinds of backing data without changing the API. For example, we could add support for different underlying data formats and storage layouts.
-
-* **Portability**. While a common use case of Arquero is to query data directly in the same JavaScript runtime, Arquero verbs can also be [*serialized as queries*](./#queries): one can specify verbs in one environment, but then send them to another environment for processing. For example, the [arquero-worker](https://github.com/uwdata/arquero-worker) package sends queries to a worker thread, while the [arquero-sql](https://github.com/chanwutk/arquero-sql) package sends them to a backing database server. As custom methods may not be defined in those environments, Arquero is designed to make this translation between environments possible and easier to reason about.
-
-* **Safety**. Arquero table expressions do not let you call methods defined on input data values. For example, to trim a string you must call `op.trim(str)`, not `str.trim()`. Again, this aids portability: otherwise unsupported methods defined on input data elements might "sneak" in to the processing. Invoking arbitrary methods may also lead to security vulnerabilities when allowing untrusted third parties to submit queries into a system.
+* **Flexibility**. Providing our own parsing also allows us to introduce new kinds of backing data without changing the API. For example, we could add support for different underlying data formats and storage layouts. More importantly, it also allows us analyze expressions and incorporate aggregate and window functions in otherwise "normal" JavaScript expressions.
* **Discoverability**. Defining all functions on a single object provides a single catalog of all available operations. In most IDEs, you can simply type `op.` (and perhaps hit the tab key) to the see a list of all available functions and benefit from auto-complete!
-Of course, one might wish to make different trade-offs. Arquero is designed to support common use cases while also being applicable to more complex production setups. This goal comes with the cost of more rigid management of functions. However, Arquero can be extended with custom variables, functions, and even new table methods or verbs! As starting points, see the [params](table#params), [addFunction](extensibility#addFunction), and [addTableMethod](extensibility#addTableMethod) functions to introduce external variables, register new `op` functions, or extend tables with new methods.
+Of course, one might wish to make different trade-offs. Arquero is designed to support common use cases while also being applicable to more complex production setups. This goal comes with the cost of more rigid management of functions. However, Arquero can be extended with custom variables, functions, and even new table methods or verbs! As starting points, see the [params](table#params) and [addFunction](extensibility#addFunction) methods to introduce external variables or register new `op` functions.
-All that being said, not all use cases require portability, safety, etc. For such cases Arquero provides an escape hatch: use the [`escape()` expression helper](./#escape) to apply a standard JavaScript function *as-is*, skipping any internal parsing and code generation.
\ No newline at end of file
+All that being said, Arquero provides an escape hatch: use the [`escape()` expression helper](./#escape) to apply a standard JavaScript function *as-is*, skipping any internal parsing and code generation. As a result, escaped functions do *not* support aggregation and window operations, as these depend on Arquero's internal parsing and code generation.
diff --git a/docs/api/extensibility.md b/docs/api/extensibility.md
index 30ab6a3a..0308c139 100644
--- a/docs/api/extensibility.md
+++ b/docs/api/extensibility.md
@@ -14,6 +14,7 @@ title: Extensibility \| Arquero API Reference
* [addVerb](#addVerb)
* [Package Bundles](#packages)
* [addPackage](#addPackage)
+* [Table Methods](#table-methods)
@@ -123,158 +124,21 @@ aq.table({ x: [4, 3, 2, 1] })
## Table Methods
-Add new table-level methods or verbs. The [addTableMethod](#addTableMethod) function registers a new function as an instance method of tables only. The [addVerb](#addVerb) method registers a new transformation verb with both tables and serializable [queries](./#query).
-
-
#
-aq.addTableMethod(name, method[, options]) · [Source](https://github.com/uwdata/arquero/blob/master/src/register.js)
-
-Register a custom table method, adding a new method with the given *name* to all table instances. The provided *method* must take a table as its first argument, followed by any additional arguments.
-
-This method throws an error if the *name* argument is not a legal string value.
-To protect Arquero internals, the *name* can not start with an underscore (`_`) character. If a custom method with the same name is already registered, the override option must be specified to overwrite it. In no case may a built-in method be overridden.
-
-* *name*: The name to use for the table method.
-* *method*: A function implementing the table method. This function should accept a table as its first argument, followed by any additional arguments.
-* *options*: Function registration options.
- * *override*: Boolean flag (default `false`) indicating if the added method is allowed to override an existing method with the same name. Built-in table methods can **not** be overridden; this flag applies only to methods previously added using the extensibility API.
-
-*Examples*
-
-```js
-// add a table method named size, returning an array of row and column counts
-aq.addTableMethod('size', table => [table.numRows(), table.numCols()]);
-aq.table({ a: [1,2,3], b: [4,5,6] }).size() // [3, 2]
-```
-
-#
-aq.addVerb(name, method, params[, options]) · [Source](https://github.com/uwdata/arquero/blob/master/src/register.js)
-
-Register a custom transformation verb with the given *name*, adding both a table method and serializable [query](./#query) support. The provided *method* must take a table as its first argument, followed by any additional arguments. The required *params* argument describes the parameters the verb accepts. If you wish to add a verb to tables but do not require query serialization support, use [addTableMethod](#addTableMethod).
-
-This method throws an error if the *name* argument is not a legal string value.
-To protect Arquero internals, the *name* can not start with an underscore (`_`) character. If a custom method with the same name is already registered, the override option must be specified to overwrite it. In no case may a built-in method be overridden.
-
-* *name*: The name to use for the table method.
-* *method*: A function implementing the table method. This function should accept a table as its first argument, followed by any additional arguments.
-* *params*: An array of schema descriptions for the verb parameters. These descriptors are needed to support query serialization. Each descriptor is an object with *name* (string-valued parameter name) and *type* properties (string-valued parameter type, see below). If a parameter has type `"Options"`, the descriptor can include an additional object-valued *props* property to describe any non-literal values, for which the keys are property names and the values are parameter types.
-* *options*: Function registration options.
- * *override*: Boolean flag (default `false`) indicating if the added method is allowed to override an existing method with the same name. Built-in verbs can **not** be overridden; this flag applies only to methods previously added using the extensibility API.
-
-*Parameter Types*. The supported parameter types are:
-
-* `"Expr"`: A single table expression, such as the input to [`filter()`](verbs/#filter).
-* `"ExprList"`: A list of column references or expressions, such as the input to [`groupby()`](verbs/#groupby).
-* `"ExprNumber"`: A number literal or numeric table expression, such as the *weight* option of [`sample()`](verbs/#sample).
-* `"ExprObject"`: An object containing a set of expressions, such as the input to [`rollup()`](verbs/#rollup).
-* `"JoinKeys"`: Input join keys, as in [`join()`](verbs/#join).
-* `"JoinValues"`: Output join values, as in [`join()`](verbs/#join).
-* `"Options"`: An options object of key-value pairs. If any of the option values are column references or table expressions, the descriptor should include a *props* property with property names as keys and parameter types as values.
-* `"OrderKeys"`: A list of ordering criteria, as in [`orderby`](verbs/#orderby).
-* `"SelectionList"`: A set of columns to select and potentially rename, as in [`select`](verbs/#select).
-* `"TableRef"`: A reference to an additional input table, as in [`join()`](verbs/#join).
-* `"TableRefList"`: A list of one or more additional input tables, as in [`concat()`](verbs/#concat).
-
-*Examples*
-
-```js
-// add a bootstrapped confidence interval verb that
-// accepts an aggregate expression plus options
-aq.addVerb(
- 'bootstrap_ci',
- (table, expr, options = {}) => table
- .params({ frac: options.frac || 1000 })
- .sample((d, $) => op.round($.frac * op.count()), { replace: true })
- .derive({ id: (d, $) => op.row_number() % $.frac })
- .groupby('id')
- .rollup({ bs: expr })
- .rollup({
- lo: op.quantile('bs', options.lo || 0.025),
- hi: op.quantile('bs', options.hi || 0.975)
- }),
- [
- { name: 'expr', type: 'Expr' },
- { name: 'options', type: 'Options' }
- ]
-);
-
-// apply the new verb
-aq.table({ x: [1, 2, 3, 4, 6, 8, 9, 10] })
- .bootstrap_ci(op.mean('x'))
-```
-
-
-
-## Package Bundles
-
-Extend Arquero with a bundle of functions, table methods, and/or verbs.
-
-#
-aq.addPackage(bundle[, options]) · [Source](https://github.com/uwdata/arquero/blob/master/src/register.js)
-
-Register a *bundle* of extensions, which may include standard functions, aggregate functions, window functions, table methods, and verbs. If the input *bundle* has a key named `"arquero_package"`, the value of that property is used; otherwise the *bundle* object is used directly. This method is particularly useful for publishing separate packages of Arquero extensions and then installing them with a single method call.
-
-A package bundle has the following structure:
-
-```js
-const bundle = {
- functions: { ... },
- aggregateFunctions: { ... },
- windowFunctions: { ... },
- tableMethods: { ... },
- verbs: { ... }
-};
-```
-
-All keys are optional. For example, `functions` or `verbs` may be omitted. Each sub-bundle is an object of key-value pairs, where the key is the name of the function and the value is the function to add.
-
-The lone exception is the `verbs` bundle, which instead uses an object format with *method* and *params* keys, corresponding to the *method* and *params* arguments of [addVerb](#addVerb):
-
-```js
-const bundle = {
- verbs: {
- name: {
- method: (table, expr) => { ... },
- params: [ { name: 'expr': type: 'Expr' } ]
- }
- }
-};
-```
-
-The package method performs validation prior to adding any package content. The method will throw an error if any of the package items fail validation. See the [addFunction](#addFunction), [addAggregateFunction](#addAggregateFunction), [addWindowFunction](#windowFunction), [addTableMethod](#addTableMethod), and [addVerb](#addVerb) methods for specific validation criteria. The *options* argument can be used to specify if method overriding is permitted, as supported by each of the aforementioned methods.
-
-* *bundle*: The package bundle of extensions.
-* *options*: Function registration options.
- * *override*: Boolean flag (default `false`) indicating if the added method is allowed to override an existing method with the same name. Built-in table methods or verbs can **not** be overridden; for table methods and verbs this flag applies only to methods previously added using the extensibility API.
+To add new table-level methods, including transformation verbs, simply assign new methods to the `ColumnTable` class prototype.
*Examples*
```js
-// add a package
-aq.addPackage({
- functions: {
- square: x => x * x,
- },
- tableMethods: {
- size: table => [table.numRows(), table.numCols()]
- }
-});
-```
-
-```js
-// add a package, ignores any content outside of "arquero_package"
-aq.addPackage({
- arquero_package: {
- functions: {
- square: x => x * x,
- },
- tableMethods: {
- size: table => [table.numRows(), table.numCols()]
+import { ColumnTable, op } from 'arquero';
+
+// add a sum verb, which returns a new table containing summed
+// values (potentially grouped) for a given column name
+Object.assign(
+ ColumnTable.prototype,
+ {
+ sum(column, { as = 'sum' } = {}) {
+ return this.rollup({ [as]: op.sum(column) });
}
}
-});
+);
```
-
-```js
-// add a package from a separate library
-aq.addPackage(require('arquero-arrow'));
-```
\ No newline at end of file
diff --git a/docs/api/index.md b/docs/api/index.md
index 2fb03053..6d128398 100644
--- a/docs/api/index.md
+++ b/docs/api/index.md
@@ -10,7 +10,7 @@ title: Arquero API Reference
* [Table Input](#input)
* [load](#load), [loadArrow](#loadArrow), [loadCSV](#loadCSV), [loadFixed](#loadFixed), [loadJSON](#loadJSON)
* [Table Output](#output)
- * [toArrow](#toArrow)
+ * [toArrow](#toArrow), [toArrowIPC](#toArrowIPC)
* [Expression Helpers](#expression-helpers)
* [op](#op), [agg](#agg), [escape](#escape)
* [bin](#bin), [desc](#desc), [frac](#frac), [rolling](#rolling), [seed](#seed)
@@ -18,8 +18,6 @@ title: Arquero API Reference
* [all](#all), [not](#not), [range](#range)
* [matches](#matches), [startswith](#startswith), [endswith](#endswith)
* [names](#names)
-* [Queries](#queries)
- * [query](#query), [queryFrom](#queryFrom)
@@ -102,6 +100,11 @@ This method performs parsing only. To both load and parse an Arrow file, use [lo
* *arrowTable*: An [Apache Arrow](https://arrow.apache.org/docs/js/) data table or a byte array (e.g., [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) or [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array)) in the Arrow IPC format.
* *options*: An Arrow import options object:
* *columns*: An ordered set of columns to import. The input may consist of: column name strings, column integer indices, objects with current column names as keys and new column names as values (for renaming), or a selection helper function such as [all](#all), [not](#not), or [range](#range)).
+ * *convertDate*: Boolean flag (default `true`) to convert Arrow date values to JavaScript Date objects. If false, defaults to what the Arrow implementation provides, typically timestamps as number values.
+ * *convertDecimal*: Boolean flag (default `true`) to convert Arrow fixed point decimal values to JavaScript numbers. If false, defaults to what the Arrow implementation provides, typically byte arrays. The conversion will be lossy if the decimal can not be exactly represented as a double-precision floating point number.
+ *convertTimestamp*: Boolean flag (default `true`) to convert Arrow timestamp values to JavaScript Date objects. If false, defaults to what the Arrow implementation provides, typically timestamps as number values.
+ *convertBigInt*: Boolean flag (default `false`) to convert Arrow integers with bit widths of 64 bits or higher to JavaScript numbers. If false, defaults to what the Arrow implementation provides, typically `BigInt` values. The conversion will be lossy if the integer is so large it can not be exactly represented as a double-precision floating point number.
+ *memoize*: Boolean hint (default `true`) to enable memoization of expensive conversions. If true, memoization is applied for string and nested (list, struct) types, caching extracted values to enable faster access. Memoization is also applied to converted Date values, in part to ensure exact object equality. This hint is ignored for dictionary columns, whose values are always memoized.
*Examples*
@@ -405,10 +408,10 @@ const dt = await aq.loadJSON('data/table.json', { autoType: false })
## Table Output
-Methods for writing table data to an output format. Most output methods are defined as [table methods](table#output), not in the top level namespace.
+Methods for writing data to an output format. Most output methods are available as [table methods](table#output), in addition to the top level namespace.
#
-aq.toArrow(data[, options]) · [Source](https://github.com/uwdata/arquero/blob/master/src/arrow/encode/index.js)
+aq.toArrow(data[, options]) · [Source](https://github.com/uwdata/arquero/blob/master/src/arrow/to-arrow.js)
Create an [Apache Arrow](https://arrow.apache.org/docs/js/) table for the input *data*. The input data can be either an [Arquero table](#table) or an array of standard JavaScript objects. This method will throw an error if type inference fails or if the generated columns have differing lengths. For Arquero tables, this method can instead be invoked as [table.toArrow()](table#toArrow).
@@ -477,6 +480,34 @@ const at = toArrow([
]);
```
+#
+table.toArrowBuffer(data[, options]) · [Source](https://github.com/uwdata/arquero/blob/master/src/arrow/to-arrow-ipc.js)
+
+Format input data in the binary [Apache Arrow](https://arrow.apache.org/docs/js/) IPC format. The input data can be either an [Arquero table](#table) or an array of standard JavaScript objects. This method will throw an error if type inference fails or if the generated columns have differing lengths. For Arquero tables, this method can instead be invoked as [table.toArrowIPC()](table#toArrowIPC).
+
+The resulting binary data may be saved to disk or passed between processes or tools. For example, when using [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers), the output of this method can be passed directly between threads (no data copy) as a [Transferable](https://developer.mozilla.org/en-US/docs/Web/API/Transferable) object. Additionally, Arrow binary data can be loaded in other language environments such as [Python](https://arrow.apache.org/docs/python/) or [R](https://arrow.apache.org/docs/r/).
+
+This method will throw an error if type inference fails or if the generated columns have differing lengths.
+
+* *options*: Options for Arrow encoding, same as [toArrow](#toArrow) but with an additional *format* option.
+ * *format*: The Arrow IPC byte format to use. One of `'stream'` (default) or `'file'`.
+
+*Examples*
+
+Encode Arrow data from an input Arquero table:
+
+```js
+import { table, toArrowIPC } from 'arquero';
+
+const dt = table({
+ x: [1, 2, 3, 4, 5],
+ y: [3.4, 1.6, 5.4, 7.1, 2.9]
+});
+
+// encode table as a transferable Arrow byte buffer
+// here, infers Uint8 for 'x' and Float64 for 'y'
+const bytes = toArrowIPC(dt);
+```
@@ -776,58 +807,3 @@ table.rename(aq.names(['a', 'b', 'c']))
// select and rename the first three columns, all other columns are dropped
table.select(aq.names(['a', 'b', 'c']))
```
-
-
-
-
-
-## Queries
-
-Queries allow deferred processing. Rather than process a sequence of verbs immediately, they can be stored as a query. The query can then be *serialized* to be stored or transferred, or later *evaluated* against an Arquero table.
-
-#
-aq.query([tableName]) · [Source](https://github.com/uwdata/arquero/blob/master/src/query/query.js)
-
-Create a new query builder instance. The optional *tableName* string argument indicates the default name of a table the query should process, and is used only when evaluating a query against a catalog of tables. The resulting query builder includes the same [verb](verbs) methods as a normal Arquero table. However, rather than evaluating verbs immediately, they are stored as a list of verbs to be evaluated later.
-
-The method *query.evaluate(table, catalog)* will evaluate the query against an Arquero table. If provided, the optional *catalog* argument should be a function that takes a table name string as input and returns a corresponding Arquero table instance. The catalog will be used to lookup tables referenced by name for multi-table operations such as joins, or to lookup the primary table to process when the *table* argument to evaluate is `null` or `undefined`.
-
-Use the query *toObject()* method to serialize a query to a JSON-compatible object. Use the top-level [queryFrom](#queryFrom) method to parse a serialized query and return a new "live" query instance.
-
-*Examples*
-
-```js
-// create a query, then evaluate it on an input table
-const q = aq.query()
- .derive({ add1: d => d.value + 1 })
- .filter(d => d.add1 > 5 );
-
-const t = q.evaluate(table);
-```
-
-```js
-// serialize a query to a JSON-compatible object
-// the query can be reconstructed using aq.queryFrom
-aq.query()
- .derive({ add1: d => d.value + 1 })
- .filter(d => d.add1 > 5 )
- .toObject();
-```
-
-
-#
-aq.queryFrom(object) · [Source](https://github.com/uwdata/arquero/blob/master/src/query/query.js)
-
-Parse a serialized query *object* and return a new query instance. The input *object* should be a serialized query representation, such as those generated by the query *toObject()* method.
-
-*Examples*
-
-```js
-// round-trip a query to a serialized form and back again
-aq.queryFrom(
- aq.query()
- .derive({ add1: d => d.value + 1 })
- .filter(d => d.add1 > 5 )
- .toObject()
-)
-```
diff --git a/docs/api/op.md b/docs/api/op.md
index 2f5c8627..d2387605 100644
--- a/docs/api/op.md
+++ b/docs/api/op.md
@@ -54,14 +54,6 @@ Merges two or more arrays in sequence, returning a new array.
* *values*: The arrays to merge.
-#
-op.join(array[, delimiter]) · [Source](https://github.com/uwdata/arquero/blob/master/src/op/functions/array.js)
-
-Creates and returns a new string by concatenating all of the elements in an *array* (or an array-like object), separated by commas or a specified *delimiter* string. If the *array* has only one item, then that item will be returned without using the delimiter.
-
-* *array*: The input array value.
-* *join*: The delimiter string (default `','`).
-
#op.includes(array, value[, index]) · [Source](https://github.com/uwdata/arquero/blob/master/src/op/functions/array.js)
@@ -79,6 +71,14 @@ Returns the first index at which a given *value* can be found in the *sequence*
* *sequence*: The input array or string value.
* *value*: The value to search for.
+#
+op.join(array[, delimiter]) · [Source](https://github.com/uwdata/arquero/blob/master/src/op/functions/array.js)
+
+Creates and returns a new string by concatenating all of the elements in an *array* (or an array-like object), separated by commas or a specified *delimiter* string. If the *array* has only one item, then that item will be returned without using the delimiter.
+
+* *array*: The input array value.
+* *delimiter*: The delimiter string (default `','`).
+
#op.lastindexof(sequence, value) · [Source](https://github.com/uwdata/arquero/blob/master/src/op/functions/array.js)
@@ -102,21 +102,12 @@ Returns a new array in which the given *property* has been extracted for each el
* *array*: The input array value.
* *property*: The property name string to extract. Nested properties are not supported: the input `"a.b"` will indicates a property with that exact name, *not* a nested property `"b"` of the object `"a"`.
-#
-op.slice(sequence[, start, end]) · [Source](https://github.com/uwdata/arquero/blob/master/src/op/functions/array.js)
-
-Returns a copy of a portion of the input *sequence* (array or string) selected from *start* to *end* (*end* not included) where *start* and *end* represent the index of items in the sequence.
-
-* *sequence*: The input array or string value.
-* *start*: The starting integer index to copy from (inclusive, default `0`).
-* *end*: The ending integer index to copy from (exclusive, default `sequence.length`).
-
#
-op.reverse(array) · [Source](https://github.com/uwdata/arquero/blob/master/src/op/functions/array.js)
+op.reverse(sequence) · [Source](https://github.com/uwdata/arquero/blob/master/src/op/functions/array.js)
-Returns a new array with the element order reversed: the first *array* element becomes the last, and the last *array* element becomes the first. The input *array* is unchanged.
+Returns a new array or string with the element order reversed: the first *sequence* element becomes the last, and the last *sequence* element becomes the first. The input *sequence* is unchanged.
-* *array*: The input array value.
+* *sequence*: The input array or string value.
#op.sequence([start,] stop[, step]) · [Source](https://github.com/uwdata/arquero/blob/master/src/op/functions/sequence.js)
@@ -127,6 +118,14 @@ Returns an array containing an arithmetic sequence from the *start* value to the
* *stop*: The stopping value of the sequence. The stop value is exclusive; it is not included in the result.
* *step*: The step increment between sequence values (default `1`).
+#
+op.slice(sequence[, start, end]) · [Source](https://github.com/uwdata/arquero/blob/master/src/op/functions/array.js)
+
+Returns a copy of a portion of the input *sequence* (array or string) selected from *start* to *end* (*end* not included) where *start* and *end* represent the index of items in the sequence.
+
+* *sequence*: The input array or string value.
+* *start*: The starting integer index to copy from (inclusive, default `0`).
+* *end*: The ending integer index to copy from (exclusive, default `sequence.length`).
@@ -683,7 +682,7 @@ Compare two values for equality, using join semantics in which `null !== null`.
Returns a boolean indicating whether the *object* has the specified *key* as its own property (as opposed to inheriting it). If the *object* is a [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) or [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) instance, the `has` method will be invoked directly on the object, otherwise `Object.hasOwnProperty` is used.
* *object*: The object, Map, or Set to test for property membership.
-* *property*: The string property name to test for.
+* *key*: The string key (property name) to test for.
#op.keys(object) · [Source](https://github.com/uwdata/arquero/blob/master/src/op/functions/object.js)
@@ -811,6 +810,7 @@ If specified, the *index* looks up a value of the resulting match. If *index* is
* *value*: The input string value.
* *regexp*: The [regular expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) to match against.
+* *index*: The index into the match result array or capture group.
*Examples*
diff --git a/docs/api/table.md b/docs/api/table.md
index d5c7088d..182f2b83 100644
--- a/docs/api/table.md
+++ b/docs/api/table.md
@@ -14,7 +14,7 @@ title: Table \| Arquero API Reference
* [assign](#assign)
* [transform](#transform)
* [Table Columns](#columns)
- * [column](#column), [columnAt](#columnAt), [columnArray](#columnArray)
+ * [column](#column), [columnAt](#columnAt)
* [columnIndex](#columnIndex), [columnName](#columnName), [columnNames](#columnNames)
* [Table Values](#table-values)
* [array](#array), [values](#values)
@@ -23,7 +23,7 @@ title: Table \| Arquero API Reference
* [Table Output](#output)
* [objects](#objects), [object](#object), [Symbol.iterator](#@@iterator)
* [print](#print), [toHTML](#toHTML), [toMarkdown](#toMarkdown)
- * [toArrow](#toArrow), [toArrowBuffer](#toArrowBuffer), [toCSV](#toCSV), [toJSON](#toJSON)
+ * [toArrow](#toArrow), [toArrowIPC](#toArrowIPC), [toCSV](#toCSV), [toJSON](#toJSON)
@@ -235,7 +235,7 @@ aq.table({ a: [1, 2], b: [3, 4] })
Get the column instance with the given *name*, or `undefined` if does not exist. The returned column object provides a lightweight abstraction over the column storage (such as a backing array), providing a *length* property and *get(row)* method.
-A column instance may be used across multiple tables and so does _not_ track a table's filter or orderby critera. To access filtered or ordered values, use the table [get](#get), [getter](#getter), or [columnArray](#columnArray) methods.
+A column instance may be used across multiple tables and so does _not_ track a table's filter or orderby critera. To access filtered or ordered values, use the table [get](#get), [getter](#getter), or [array](#array) methods.
* *name*: The column name.
@@ -260,16 +260,6 @@ const dt = aq.table({ a: [1, 2, 3], b: [4, 5, 6] })
dt.columnAt(1).get(1) // 5
```
-#
-table.columnArray(name[, constructor]) · [Source](https://github.com/uwdata/arquero/blob/master/src/table/table.js)
-
-_This method is a deprecated alias for the table [array()](#array) method. Please use [array()](#array) instead._
-
-Get an array of values contained in the column with the given *name*. Unlike direct access through the table [column](#column) method, the array returned by this method respects any table filter or orderby criteria. By default, a standard [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) is returned; use the *constructor* argument to specify a [typed array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray).
-
-* *name*: The column name.
-* *constructor*: An optional array constructor (default [`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Array)) to use to instantiate the output array. Note that errors or truncated values may occur when assigning to a typed array with an incompatible type.
-
#table.columnIndex(name) · [Source](https://github.com/uwdata/arquero/blob/master/src/table/table.js)
@@ -362,14 +352,14 @@ for (const value of table.values('colA')) {
```
```js
-// slightly less efficient version of table.columnArray('colA')
+// slightly less efficient version of table.array('colA')
const colValues = Array.from(table.values('colA'));
```
#table.data() · [Source](https://github.com/uwdata/arquero/blob/master/src/table/table.js)
-Returns the internal table storage data structure.
+Returns the internal table storage data structure: an object with column names for keys and column arrays for values. This method returns the same structure used by the Table (not a copy) and its contents should not be modified.
#table.get(name[, row]) · [Source](https://github.com/uwdata/arquero/blob/master/src/table/column-table.js)
@@ -438,7 +428,7 @@ Perform a table scan, invoking the provided *callback* function for each row of
* *callback*: Function invoked for each row of the table. The callback is invoked with the following arguments:
* *row*: The table row index.
- * *data*: The backing table data store.
+ * *data*: The backing table data store (as returned by table [`data`](#data) method).
* *stop*: A function to stop the scan early. The callback can invoke *stop()* to prevent future scan calls.
* *order*: A boolean flag (default `false`), indicating if the table should be scanned in the order determined by [orderby](verbs#orderby). This argument has no effect if the table is unordered.
@@ -629,14 +619,15 @@ const at2 = dt.toArrow({
});
```
-#
+#table.toArrowBuffer([options]) · [Source](https://github.com/uwdata/arquero/blob/master/src/arrow/encode/index.js)
Format this table as binary data in the [Apache Arrow](https://arrow.apache.org/docs/js/) IPC format. The binary data may be saved to disk or passed between processes or tools. For example, when using [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers), the output of this method can be passed directly between threads (no data copy) as a [Transferable](https://developer.mozilla.org/en-US/docs/Web/API/Transferable) object. Additionally, Arrow binary data can be loaded in other language environments such as [Python](https://arrow.apache.org/docs/python/) or [R](https://arrow.apache.org/docs/r/).
-This method will throw an error if type inference fails or if the generated columns have differing lengths. This method is a shorthand for `table.toArrow().serialize()`.
+This method will throw an error if type inference fails or if the generated columns have differing lengths.
-* *options*: Options for Arrow encoding, same as [toArrow](#toArrow).
+* *options*: Options for Arrow encoding, same as [toArrow](#toArrow) but with an additional *format* option.
+ * *format*: The Arrow IPC byte format to use. One of `'stream'` (default) or `'file'`.
*Examples*
@@ -652,7 +643,7 @@ const dt = table({
// encode table as a transferable Arrow byte buffer
// here, infers Uint8 for 'x' and Float64 for 'y'
-const bytes = dt.toArrowBuffer();
+const bytes = dt.toArrowIPC();
```
#
diff --git a/docs/index.md b/docs/index.md
index 2a86f6f9..19315224 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -93,13 +93,9 @@ Arquero uses modern JavaScript features, and so will not work with some outdated
### In Node.js or Application Bundles
-First install `arquero` as a dependency, for example via `npm install arquero --save`. Arquero assumes Node version 12 or higher.
-
-Import using CommonJS module syntax:
-
-```js
-const aq = require('arquero');
-```
+First install `arquero` as a dependency, for example via `npm install arquero --save`.
+Arquero assumes Node version 18 or higher.
+As of Arquero version 6, the library uses type `module` and should be loaded using ES module syntax.
Import using ES module syntax, import all exports into a single object:
@@ -113,6 +109,12 @@ Import using ES module syntax, with targeted imports:
import { op, table } from 'arquero';
```
+Dynamic import (e.g., within a Node.js REPL):
+
+```js
+aq = await import('arquero');
+```
+
## Build Instructions
To build and develop Arquero locally:
diff --git a/eslint.config.mjs b/eslint.config.js
similarity index 90%
rename from eslint.config.mjs
rename to eslint.config.js
index 200ae8e1..b0fe3aeb 100644
--- a/eslint.config.mjs
+++ b/eslint.config.js
@@ -6,10 +6,11 @@ export default [
js.configs.recommended,
{
languageOptions: {
- ecmaVersion: 2020,
- sourceType: 'module',
+ ecmaVersion: 2022,
+ sourceType: "module",
globals: {
...globals.browser,
+ ...globals.mocha,
...globals.node,
...globals.es6,
globalThis: false
diff --git a/jsconfig.json b/jsconfig.json
new file mode 100644
index 00000000..c1651dcc
--- /dev/null
+++ b/jsconfig.json
@@ -0,0 +1,12 @@
+{
+ "include": ["src/**/*"],
+ "compilerOptions": {
+ "allowJs": true,
+ "checkJs": true,
+ "noEmit": true,
+ "module": "node16",
+ "moduleResolution": "node16",
+ "target": "es2022",
+ "skipLibCheck": true
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index e219bee0..72bbbdcb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,29 +1,28 @@
{
"name": "arquero",
- "version": "5.4.0",
+ "version": "6.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "arquero",
- "version": "5.4.0",
+ "version": "6.0.0",
"license": "BSD-3-Clause",
"dependencies": {
- "acorn": "^8.12.0",
- "apache-arrow": "^15.0.2",
- "node-fetch": "^2.7.0"
+ "acorn": "^8.12.1",
+ "apache-arrow": "^17.0.0",
+ "node-fetch": "^3.3.2"
},
"devDependencies": {
- "@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4",
- "eslint": "^9.5.0",
- "esm": "^3.2.25",
- "rimraf": "^5.0.7",
- "rollup": "^4.18.0",
+ "eslint": "^9.7.0",
+ "mocha": "^10.6.0",
+ "rimraf": "^6.0.1",
+ "rollup": "^4.18.1",
"rollup-plugin-bundle-size": "^1.0.3",
"tape": "^5.8.1",
- "typescript": "^5.5.2"
+ "typescript": "^5.5.3"
}
},
"node_modules/@75lb/deep-merge": {
@@ -60,23 +59,24 @@
}
},
"node_modules/@eslint-community/regexpp": {
- "version": "4.10.0",
+ "version": "4.11.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz",
+ "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==",
"dev": true,
- "license": "MIT",
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
"node_modules/@eslint/config-array": {
- "version": "0.16.0",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.16.0.tgz",
- "integrity": "sha512-/jmuSd74i4Czf1XXn7wGRWZCuyaUZ330NH1Bek0Pplatt4Sy1S5haN21SCLLdbeKslQ+S0wEJ+++v5YibSi+Lg==",
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.0.tgz",
+ "integrity": "sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/object-schema": "^2.1.4",
"debug": "^4.3.1",
- "minimatch": "^3.0.5"
+ "minimatch": "^3.1.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -107,11 +107,10 @@
}
},
"node_modules/@eslint/js": {
- "version": "9.5.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.5.0.tgz",
- "integrity": "sha512-A7+AOT2ICkodvtsWnxZP4Xxk3NbZ3VMHd8oihydLRGrJgqqdEz1qSeEgXYyT/Cu8h1TWWsQRejIx48mtjZ5y1w==",
+ "version": "9.7.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.7.0.tgz",
+ "integrity": "sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==",
"dev": true,
- "license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
@@ -154,6 +153,8 @@
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -170,6 +171,8 @@
},
"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==",
"dev": true,
"license": "MIT",
"engines": {
@@ -181,6 +184,8 @@
},
"node_modules/@isaacs/cliui/node_modules/strip-ansi": {
"version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -250,6 +255,7 @@
"resolved": "https://registry.npmjs.org/@ljharb/resumer/-/resumer-0.1.3.tgz",
"integrity": "sha512-d+tsDgfkj9X5QTriqM4lKesCkMMJC3IrbPKHvayP00ELx2axdXvDfWkqjxrLXIzGcQzmj7VAUT1wopqARTvafw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@ljharb/through": "^2.3.13",
"call-bind": "^1.0.7"
@@ -263,6 +269,7 @@
"resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz",
"integrity": "sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.7"
},
@@ -304,6 +311,8 @@
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -311,26 +320,6 @@
"node": ">=14"
}
},
- "node_modules/@rollup/plugin-json": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz",
- "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==",
- "dev": true,
- "dependencies": {
- "@rollup/pluginutils": "^5.1.0"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
- },
- "peerDependenciesMeta": {
- "rollup": {
- "optional": true
- }
- }
- },
"node_modules/@rollup/plugin-node-resolve": {
"version": "15.2.3",
"dev": true,
@@ -399,9 +388,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.18.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz",
- "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.1.tgz",
+ "integrity": "sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==",
"cpu": [
"arm"
],
@@ -413,9 +402,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.18.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz",
- "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.1.tgz",
+ "integrity": "sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==",
"cpu": [
"arm64"
],
@@ -427,9 +416,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.18.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz",
- "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.1.tgz",
+ "integrity": "sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==",
"cpu": [
"arm64"
],
@@ -441,9 +430,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.18.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz",
- "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.1.tgz",
+ "integrity": "sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==",
"cpu": [
"x64"
],
@@ -455,9 +444,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.18.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz",
- "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.1.tgz",
+ "integrity": "sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==",
"cpu": [
"arm"
],
@@ -469,9 +458,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.18.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz",
- "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.1.tgz",
+ "integrity": "sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==",
"cpu": [
"arm"
],
@@ -483,9 +472,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.18.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz",
- "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.1.tgz",
+ "integrity": "sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==",
"cpu": [
"arm64"
],
@@ -497,9 +486,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.18.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz",
- "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.1.tgz",
+ "integrity": "sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==",
"cpu": [
"arm64"
],
@@ -511,9 +500,9 @@
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
- "version": "4.18.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz",
- "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.1.tgz",
+ "integrity": "sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==",
"cpu": [
"ppc64"
],
@@ -525,9 +514,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.18.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz",
- "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.1.tgz",
+ "integrity": "sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==",
"cpu": [
"riscv64"
],
@@ -539,9 +528,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.18.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz",
- "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.1.tgz",
+ "integrity": "sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==",
"cpu": [
"s390x"
],
@@ -553,9 +542,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.18.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz",
- "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.1.tgz",
+ "integrity": "sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==",
"cpu": [
"x64"
],
@@ -567,9 +556,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.18.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz",
- "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.1.tgz",
+ "integrity": "sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==",
"cpu": [
"x64"
],
@@ -581,9 +570,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.18.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz",
- "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.1.tgz",
+ "integrity": "sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==",
"cpu": [
"arm64"
],
@@ -595,9 +584,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.18.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz",
- "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.1.tgz",
+ "integrity": "sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==",
"cpu": [
"ia32"
],
@@ -609,9 +598,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.18.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz",
- "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.1.tgz",
+ "integrity": "sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==",
"cpu": [
"x64"
],
@@ -663,9 +652,9 @@
"license": "MIT"
},
"node_modules/acorn": {
- "version": "8.12.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz",
- "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==",
+ "version": "8.12.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
+ "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -701,6 +690,16 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
+ "node_modules/ansi-colors": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
+ "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/ansi-regex": {
"version": "2.1.1",
"dev": true,
@@ -722,19 +721,32 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/apache-arrow": {
- "version": "15.0.2",
- "resolved": "https://registry.npmjs.org/apache-arrow/-/apache-arrow-15.0.2.tgz",
- "integrity": "sha512-RvwlFxLRpO405PLGffx4N2PYLiF7FD86Q1hHl6J2XCWiq+tTCzpb9ngFw0apFDcXZBMpCzMuwAvA7hjyL1/73A==",
- "license": "Apache-2.0",
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "license": "ISC",
"dependencies": {
- "@swc/helpers": "^0.5.2",
- "@types/command-line-args": "^5.2.1",
- "@types/command-line-usage": "^5.0.2",
- "@types/node": "^20.6.0",
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/apache-arrow": {
+ "version": "17.0.0",
+ "resolved": "https://registry.npmjs.org/apache-arrow/-/apache-arrow-17.0.0.tgz",
+ "integrity": "sha512-X0p7auzdnGuhYMVKYINdQssS4EcKec9TCXyez/qtJt32DrIMGbzqiaMiQ0X6fQlQpw8Fl0Qygcv4dfRAr5Gu9Q==",
+ "dependencies": {
+ "@swc/helpers": "^0.5.11",
+ "@types/command-line-args": "^5.2.3",
+ "@types/command-line-usage": "^5.0.4",
+ "@types/node": "^20.13.0",
"command-line-args": "^5.2.1",
"command-line-usage": "^7.0.1",
- "flatbuffers": "^23.5.26",
+ "flatbuffers": "^24.3.25",
"json-bignum": "^0.0.3",
"tslib": "^2.6.2"
},
@@ -837,6 +849,19 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/brace-expansion": {
"version": "1.1.11",
"dev": true,
@@ -846,6 +871,26 @@
"concat-map": "0.0.1"
}
},
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "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"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/buffer-from": {
"version": "1.1.2",
"dev": true,
@@ -867,6 +912,7 @@
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
@@ -891,6 +937,19 @@
"node": ">=6"
}
},
+ "node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/chalk": {
"version": "1.1.3",
"dev": true,
@@ -962,6 +1021,89 @@
"node": ">=0.10.0"
}
},
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chokidar/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "node_modules/cliui/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
"node_modules/color-convert": {
"version": "2.0.1",
"license": "MIT",
@@ -1039,6 +1181,15 @@
"node": ">= 8"
}
},
+ "node_modules/data-uri-to-buffer": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
+ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
"node_modules/data-view-buffer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
@@ -1111,8 +1262,23 @@
}
}
},
+ "node_modules/decamelize": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/deep-equal": {
"version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz",
+ "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1160,6 +1326,7 @@
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
@@ -1174,6 +1341,8 @@
},
"node_modules/define-properties": {
"version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1190,14 +1359,28 @@
},
"node_modules/defined": {
"version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz",
+ "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/diff": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
+ "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
"node_modules/dotignore": {
"version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz",
+ "integrity": "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1214,6 +1397,8 @@
},
"node_modules/eastasianwidth": {
"version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
"dev": true,
"license": "MIT"
},
@@ -1288,6 +1473,7 @@
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"get-intrinsic": "^1.2.4"
},
@@ -1300,12 +1486,15 @@
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-get-iterator": {
"version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
+ "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1369,6 +1558,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/escalade": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
+ "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/escape-string-regexp": {
"version": "1.0.5",
"dev": true,
@@ -1378,17 +1577,16 @@
}
},
"node_modules/eslint": {
- "version": "9.5.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.5.0.tgz",
- "integrity": "sha512-+NAOZFrW/jFTS3dASCGBxX1pkFD0/fsO+hfAkJ4TyYKwgsXZbqzrw+seCYFCcPCYXvnD67tAnglU7GQTz6kcVw==",
+ "version": "9.7.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.7.0.tgz",
+ "integrity": "sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.6.1",
- "@eslint/config-array": "^0.16.0",
+ "@eslint-community/regexpp": "^4.11.0",
+ "@eslint/config-array": "^0.17.0",
"@eslint/eslintrc": "^3.1.0",
- "@eslint/js": "9.5.0",
+ "@eslint/js": "9.7.0",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.3.0",
"@nodelib/fs.walk": "^1.2.8",
@@ -1397,9 +1595,9 @@
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.0.1",
+ "eslint-scope": "^8.0.2",
"eslint-visitor-keys": "^4.0.0",
- "espree": "^10.0.1",
+ "espree": "^10.1.0",
"esquery": "^1.5.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
@@ -1430,11 +1628,10 @@
}
},
"node_modules/eslint-scope": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz",
- "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==",
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz",
+ "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==",
"dev": true,
- "license": "BSD-2-Clause",
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
@@ -1507,14 +1704,6 @@
"node": ">=8"
}
},
- "node_modules/esm": {
- "version": "3.2.25",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/espree": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz",
@@ -1562,7 +1751,6 @@
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
"dev": true,
- "license": "BSD-2-Clause",
"dependencies": {
"estraverse": "^5.2.0"
},
@@ -1621,6 +1809,29 @@
"reusify": "^1.0.4"
}
},
+ "node_modules/fetch-blob": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "node-domexception": "^1.0.0",
+ "web-streams-polyfill": "^3.0.3"
+ },
+ "engines": {
+ "node": "^12.20 || >= 14.13"
+ }
+ },
"node_modules/figures": {
"version": "1.7.0",
"dev": true,
@@ -1646,6 +1857,19 @@
"node": ">=16.0.0"
}
},
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "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"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/find-replace": {
"version": "3.0.0",
"license": "MIT",
@@ -1671,6 +1895,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/flat": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
"node_modules/flat-cache": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
@@ -1686,10 +1920,10 @@
}
},
"node_modules/flatbuffers": {
- "version": "23.5.26",
- "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-23.5.26.tgz",
- "integrity": "sha512-vE+SI9vrJDwi1oETtTIFldC/o9GsVKRM+s6EL0nQgxXlYV1Vc4Tk30hj4xGICftInKQKj1F3up2n8UbIVobISQ==",
- "license": "SEE LICENSE IN LICENSE"
+ "version": "24.3.25",
+ "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-24.3.25.tgz",
+ "integrity": "sha512-3HDgPbgiwWMI9zVB7VYBHaMrbOO7Gm0v+yD2FV/sCKj+9NDeVL7BOBYUuhWAQGKWOzBo8S9WdMvV0eixO233XQ==",
+ "license": "Apache-2.0"
},
"node_modules/flatted": {
"version": "3.3.1",
@@ -1700,6 +1934,8 @@
},
"node_modules/for-each": {
"version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+ "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1707,7 +1943,9 @@
}
},
"node_modules/foreground-child": {
- "version": "3.1.1",
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz",
+ "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -1721,6 +1959,18 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/formdata-polyfill": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+ "license": "MIT",
+ "dependencies": {
+ "fetch-blob": "^3.1.2"
+ },
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
"node_modules/fs.realpath": {
"version": "1.0.0",
"dev": true,
@@ -1767,17 +2017,30 @@
},
"node_modules/functions-have-names": {
"version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
"node_modules/get-intrinsic": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
@@ -1794,6 +2057,8 @@
},
"node_modules/get-package-type": {
"version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1820,6 +2085,9 @@
},
"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",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -1880,6 +2148,8 @@
},
"node_modules/gopd": {
"version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1913,6 +2183,8 @@
},
"node_modules/has-bigints": {
"version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
+ "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
"dev": true,
"license": "MIT",
"funding": {
@@ -1924,6 +2196,7 @@
"resolved": "https://registry.npmjs.org/has-dynamic-import/-/has-dynamic-import-2.1.0.tgz",
"integrity": "sha512-su0anMkNEnJKZ/rB99jn3y6lV/J8Ro96hBJ28YAeVzj5rWxH+YL/AdCyiYYA1HDLV9YhmvqpWSJJj2KLo1MX6g==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.5",
"get-intrinsic": "^1.2.2"
@@ -1947,6 +2220,7 @@
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0"
},
@@ -1969,6 +2243,8 @@
},
"node_modules/has-symbols": {
"version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2006,6 +2282,16 @@
"node": ">= 0.4"
}
},
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "he": "bin/he"
+ }
+ },
"node_modules/ignore": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
@@ -2072,6 +2358,8 @@
},
"node_modules/is-arguments": {
"version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+ "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2104,6 +2392,8 @@
},
"node_modules/is-bigint": {
"version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+ "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2113,8 +2403,23 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-boolean-object": {
"version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+ "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2144,6 +2449,8 @@
},
"node_modules/is-callable": {
"version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2182,6 +2489,8 @@
},
"node_modules/is-date-object": {
"version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
+ "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2222,9 +2531,14 @@
}
},
"node_modules/is-map": {
- "version": "2.0.2",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
"dev": true,
"license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -2247,8 +2561,20 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "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"
+ }
+ },
"node_modules/is-number-object": {
"version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
+ "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2269,8 +2595,20 @@
"node": ">=8"
}
},
+ "node_modules/is-plain-obj": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-regex": {
"version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+ "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2285,9 +2623,14 @@
}
},
"node_modules/is-set": {
- "version": "2.0.2",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
"dev": true,
"license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -2310,6 +2653,8 @@
},
"node_modules/is-string": {
"version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+ "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2324,6 +2669,8 @@
},
"node_modules/is-symbol": {
"version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+ "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2352,15 +2699,33 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-weakmap": {
- "version": "2.0.1",
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
"dev": true,
"license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
"funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/is-weakref": {
+ "node_modules/is-weakmap": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
"integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
@@ -2374,12 +2739,17 @@
}
},
"node_modules/is-weakset": {
- "version": "2.0.2",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz",
+ "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "call-bind": "^1.0.2",
- "get-intrinsic": "^1.1.1"
+ "call-bind": "^1.0.7",
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -2387,6 +2757,8 @@
},
"node_modules/isarray": {
"version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
"dev": true,
"license": "MIT"
},
@@ -2396,14 +2768,16 @@
"license": "ISC"
},
"node_modules/jackspeak": {
- "version": "2.3.6",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz",
+ "integrity": "sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
"engines": {
- "node": ">=14"
+ "node": "20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@@ -2499,15 +2873,61 @@
"dev": true,
"license": "MIT"
},
- "node_modules/lru-cache": {
- "version": "10.0.2",
+ "node_modules/log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
"dev": true,
- "license": "ISC",
+ "license": "MIT",
"dependencies": {
- "semver": "^7.3.5"
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
},
"engines": {
- "node": "14 || >=16.14"
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-symbols/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/log-symbols/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "20 || >=22"
}
},
"node_modules/maxmin": {
@@ -2537,6 +2957,8 @@
},
"node_modules/minimist": {
"version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"license": "MIT",
"funding": {
@@ -2544,15 +2966,135 @@
}
},
"node_modules/minipass": {
- "version": "7.0.4",
+ "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/mocha": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.6.0.tgz",
+ "integrity": "sha512-hxjt4+EEB0SA0ZDygSS015t65lJw/I2yRCS3Ae+SJ5FrbzrXgfYwJr96f0OvIXdj7h4lv/vLCrH3rkiuizFSvw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-colors": "^4.1.3",
+ "browser-stdout": "^1.3.1",
+ "chokidar": "^3.5.3",
+ "debug": "^4.3.5",
+ "diff": "^5.2.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-up": "^5.0.0",
+ "glob": "^8.1.0",
+ "he": "^1.2.0",
+ "js-yaml": "^4.1.0",
+ "log-symbols": "^4.1.0",
+ "minimatch": "^5.1.6",
+ "ms": "^2.1.3",
+ "serialize-javascript": "^6.0.2",
+ "strip-json-comments": "^3.1.1",
+ "supports-color": "^8.1.1",
+ "workerpool": "^6.5.1",
+ "yargs": "^16.2.0",
+ "yargs-parser": "^20.2.9",
+ "yargs-unparser": "^2.0.0"
+ },
+ "bin": {
+ "_mocha": "bin/_mocha",
+ "mocha": "bin/mocha.js"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/mocha/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/mocha/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mocha/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",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/mocha/node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/mocha/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mocha/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
"node_modules/mock-property": {
"version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/mock-property/-/mock-property-1.0.3.tgz",
+ "integrity": "sha512-2emPTb1reeLLYwHxyVx993iYyCHEiRRO+y8NFXFPL5kl5q14sgTK76cXyEKkeKCHeRw35SfdkUJ10Q1KfHuiIQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2582,24 +3124,51 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/node-domexception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "github",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.5.0"
+ }
+ },
"node_modules/node-fetch": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
- "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
"license": "MIT",
"dependencies": {
- "whatwg-url": "^5.0.0"
+ "data-uri-to-buffer": "^4.0.0",
+ "fetch-blob": "^3.1.4",
+ "formdata-polyfill": "^4.0.10"
},
"engines": {
- "node": "4.x || >=6.0.0"
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
- "peerDependencies": {
- "encoding": "^0.1.0"
- },
- "peerDependenciesMeta": {
- "encoding": {
- "optional": true
- }
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/node-fetch"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
}
},
"node_modules/number-is-nan": {
@@ -2619,9 +3188,14 @@
}
},
"node_modules/object-inspect": {
- "version": "1.13.1",
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
+ "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==",
"dev": true,
"license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -2645,6 +3219,8 @@
},
"node_modules/object-keys": {
"version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2656,6 +3232,7 @@
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz",
"integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"call-bind": "^1.0.5",
"define-properties": "^1.2.1",
@@ -2721,6 +3298,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "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"
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -2744,6 +3328,8 @@
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2764,15 +3350,17 @@
"license": "MIT"
},
"node_modules/path-scurry": {
- "version": "1.10.1",
+ "version": "2.0.0",
+ "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": "^9.1.1 || ^10.0.0",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ "lru-cache": "^11.0.0",
+ "minipass": "^7.1.2"
},
"engines": {
- "node": ">=16 || 14 >=14.17"
+ "node": "20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@@ -2856,6 +3444,19 @@
"safe-buffer": "^5.1.0"
}
},
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
"node_modules/regexp.prototype.flags": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
@@ -2875,6 +3476,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/resolve": {
"version": "1.22.2",
"dev": true,
@@ -2911,19 +3522,20 @@
}
},
"node_modules/rimraf": {
- "version": "5.0.7",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.7.tgz",
- "integrity": "sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz",
+ "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==",
"dev": true,
"license": "ISC",
"dependencies": {
- "glob": "^10.3.7"
+ "glob": "^11.0.0",
+ "package-json-from-dist": "^1.0.0"
},
"bin": {
"rimraf": "dist/esm/bin.mjs"
},
"engines": {
- "node": ">=14.18"
+ "node": "20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@@ -2931,6 +3543,8 @@
},
"node_modules/rimraf/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": {
@@ -2938,44 +3552,49 @@
}
},
"node_modules/rimraf/node_modules/glob": {
- "version": "10.3.10",
+ "version": "11.0.0",
+ "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": "^2.3.5",
- "minimatch": "^9.0.1",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
- "path-scurry": "^1.10.1"
+ "jackspeak": "^4.0.1",
+ "minimatch": "^10.0.0",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^2.0.0"
},
"bin": {
"glob": "dist/esm/bin.mjs"
},
"engines": {
- "node": ">=16 || 14 >=14.17"
+ "node": "20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/rimraf/node_modules/minimatch": {
- "version": "9.0.3",
+ "version": "10.0.1",
+ "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"
},
"engines": {
- "node": ">=16 || 14 >=14.17"
+ "node": "20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/rollup": {
- "version": "4.18.0",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz",
- "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.1.tgz",
+ "integrity": "sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2989,22 +3608,22 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.18.0",
- "@rollup/rollup-android-arm64": "4.18.0",
- "@rollup/rollup-darwin-arm64": "4.18.0",
- "@rollup/rollup-darwin-x64": "4.18.0",
- "@rollup/rollup-linux-arm-gnueabihf": "4.18.0",
- "@rollup/rollup-linux-arm-musleabihf": "4.18.0",
- "@rollup/rollup-linux-arm64-gnu": "4.18.0",
- "@rollup/rollup-linux-arm64-musl": "4.18.0",
- "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0",
- "@rollup/rollup-linux-riscv64-gnu": "4.18.0",
- "@rollup/rollup-linux-s390x-gnu": "4.18.0",
- "@rollup/rollup-linux-x64-gnu": "4.18.0",
- "@rollup/rollup-linux-x64-musl": "4.18.0",
- "@rollup/rollup-win32-arm64-msvc": "4.18.0",
- "@rollup/rollup-win32-ia32-msvc": "4.18.0",
- "@rollup/rollup-win32-x64-msvc": "4.18.0",
+ "@rollup/rollup-android-arm-eabi": "4.18.1",
+ "@rollup/rollup-android-arm64": "4.18.1",
+ "@rollup/rollup-darwin-arm64": "4.18.1",
+ "@rollup/rollup-darwin-x64": "4.18.1",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.18.1",
+ "@rollup/rollup-linux-arm-musleabihf": "4.18.1",
+ "@rollup/rollup-linux-arm64-gnu": "4.18.1",
+ "@rollup/rollup-linux-arm64-musl": "4.18.1",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.18.1",
+ "@rollup/rollup-linux-riscv64-gnu": "4.18.1",
+ "@rollup/rollup-linux-s390x-gnu": "4.18.1",
+ "@rollup/rollup-linux-x64-gnu": "4.18.1",
+ "@rollup/rollup-linux-x64-musl": "4.18.1",
+ "@rollup/rollup-win32-arm64-msvc": "4.18.1",
+ "@rollup/rollup-win32-ia32-msvc": "4.18.1",
+ "@rollup/rollup-win32-x64-msvc": "4.18.1",
"fsevents": "~2.3.2"
}
},
@@ -3095,33 +3714,10 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/semver": {
- "version": "7.5.4",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/semver/node_modules/lru-cache": {
- "version": "6.0.0",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/serialize-javascript": {
- "version": "6.0.1",
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -3133,6 +3729,7 @@
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
@@ -3146,13 +3743,16 @@
}
},
"node_modules/set-function-name": {
- "version": "2.0.1",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "define-data-property": "^1.0.1",
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
"functions-have-names": "^1.2.3",
- "has-property-descriptors": "^1.0.0"
+ "has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
@@ -3178,20 +3778,28 @@
}
},
"node_modules/side-channel": {
- "version": "1.0.4",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "call-bind": "^1.0.0",
- "get-intrinsic": "^1.0.2",
- "object-inspect": "^1.9.0"
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "object-inspect": "^1.13.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/signal-exit": {
- "version": "4.0.1",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"dev": true,
"license": "ISC",
"engines": {
@@ -3225,6 +3833,8 @@
},
"node_modules/stop-iteration-iterator": {
"version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
+ "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3243,6 +3853,8 @@
},
"node_modules/string-width": {
"version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3260,6 +3872,8 @@
"node_modules/string-width-cjs": {
"name": "string-width",
"version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3273,6 +3887,8 @@
},
"node_modules/string-width/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==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3284,11 +3900,15 @@
},
"node_modules/string-width/node_modules/emoji-regex": {
"version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true,
"license": "MIT"
},
"node_modules/string-width/node_modules/strip-ansi": {
"version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3367,6 +3987,8 @@
"node_modules/strip-ansi-cjs": {
"name": "strip-ansi",
"version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3378,6 +4000,8 @@
},
"node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
"version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3489,6 +4113,8 @@
},
"node_modules/tape/node_modules/resolve": {
"version": "2.0.0-next.5",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
+ "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3525,9 +4151,18 @@
"dev": true,
"license": "MIT"
},
- "node_modules/tr46": {
- "version": "0.0.3",
- "license": "MIT"
+ "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/tslib": {
"version": "2.6.3",
@@ -3624,9 +4259,9 @@
}
},
"node_modules/typescript": {
- "version": "5.5.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz",
- "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==",
+ "version": "5.5.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz",
+ "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -3676,16 +4311,13 @@
"punycode": "^2.1.0"
}
},
- "node_modules/webidl-conversions": {
- "version": "3.0.1",
- "license": "BSD-2-Clause"
- },
- "node_modules/whatwg-url": {
- "version": "5.0.0",
+ "node_modules/web-streams-polyfill": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
"license": "MIT",
- "dependencies": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
+ "engines": {
+ "node": ">= 8"
}
},
"node_modules/which": {
@@ -3704,6 +4336,8 @@
},
"node_modules/which-boxed-primitive": {
"version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+ "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3718,14 +4352,19 @@
}
},
"node_modules/which-collection": {
- "version": "1.0.1",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "is-map": "^2.0.1",
- "is-set": "^2.0.1",
- "is-weakmap": "^2.0.1",
- "is-weakset": "^2.0.1"
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -3758,8 +4397,17 @@
"node": ">=12.17"
}
},
+ "node_modules/workerpool": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz",
+ "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
"node_modules/wrap-ansi": {
"version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3777,6 +4425,8 @@
"node_modules/wrap-ansi-cjs": {
"name": "wrap-ansi",
"version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3793,6 +4443,8 @@
},
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
"version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3806,6 +4458,8 @@
},
"node_modules/wrap-ansi/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==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3817,6 +4471,8 @@
},
"node_modules/wrap-ansi/node_modules/ansi-styles": {
"version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3828,6 +4484,8 @@
},
"node_modules/wrap-ansi/node_modules/strip-ansi": {
"version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3845,10 +4503,75 @@
"dev": true,
"license": "ISC"
},
- "node_modules/yallist": {
- "version": "4.0.0",
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
- "license": "ISC"
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs-unparser": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
+ "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "camelcase": "^6.0.0",
+ "decamelize": "^4.0.0",
+ "flat": "^5.0.2",
+ "is-plain-obj": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
},
"node_modules/yocto-queue": {
"version": "0.1.0",
diff --git a/package.json b/package.json
index 1592f053..7b259e89 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,7 @@
{
"name": "arquero",
- "version": "5.4.1",
+ "type": "module",
+ "version": "6.0.0",
"description": "Query processing and transformation of array-backed data tables.",
"keywords": [
"data",
@@ -13,14 +14,12 @@
],
"license": "BSD-3-Clause",
"author": "Jeffrey Heer (http://idl.cs.washington.edu)",
- "main": "dist/arquero.node.js",
- "module": "src/index-node.js",
+ "exports": "./src/index.js",
"unpkg": "dist/arquero.min.js",
"jsdelivr": "dist/arquero.min.js",
"types": "dist/types/index.d.ts",
"browser": {
- "./dist/arquero.node.js": "./dist/arquero.min.js",
- "./src/index-node.js": "./src/index.js"
+ "./src/index.js": "./src/index-browser.js"
},
"repository": {
"type": "git",
@@ -28,33 +27,28 @@
},
"scripts": {
"prebuild": "rimraf dist && mkdir dist",
- "build": "rollup -c rollup.config.mjs",
+ "build": "rollup -c rollup.config.js",
"postbuild": "tsc",
- "preperf": "npm run build",
"perf": "TZ=America/Los_Angeles tape 'perf/**/*-perf.js'",
"lint": "eslint src test",
- "test": "TZ=America/Los_Angeles tape 'test/**/*-test.js' --require esm",
- "prepublishOnly": "npm test && npm run lint && npm run build"
+ "test": "TZ=America/Los_Angeles mocha 'test/**/*-test.js' --timeout 5000",
+ "posttest": "npm run lint && tsc --project jsconfig.json",
+ "prepublishOnly": "npm test && npm run build"
},
"dependencies": {
- "acorn": "^8.12.0",
- "apache-arrow": "^15.0.2",
- "node-fetch": "^2.7.0"
+ "acorn": "^8.12.1",
+ "apache-arrow": "^17.0.0",
+ "node-fetch": "^3.3.2"
},
"devDependencies": {
- "@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4",
- "eslint": "^9.5.0",
- "esm": "^3.2.25",
- "rimraf": "^5.0.7",
- "rollup": "^4.18.0",
+ "eslint": "^9.7.0",
+ "mocha": "^10.6.0",
+ "rimraf": "^6.0.1",
+ "rollup": "^4.18.1",
"rollup-plugin-bundle-size": "^1.0.3",
"tape": "^5.8.1",
- "typescript": "^5.5.2"
- },
- "esm": {
- "force": true,
- "mainFields": ["module", "main"]
+ "typescript": "^5.5.3"
}
}
diff --git a/perf/arrow-perf.js b/perf/arrow-perf.js
index 929c0981..9f9c2f53 100644
--- a/perf/arrow-perf.js
+++ b/perf/arrow-perf.js
@@ -1,11 +1,11 @@
-const tape = require('tape');
-const time = require('./time');
-const { bools, floats, ints, sample, strings } = require('./data-gen');
-const { fromArrow, table } = require('..');
-const {
+import tape from 'tape';
+import { time } from './time.js';
+import { bools, floats, ints, sample, strings } from './data-gen.js';
+import { fromArrow, table, toArrow } from '../src/index.js';
+import {
Bool, Dictionary, Float64, Int32, Table, Uint32, Utf8,
- vectorFromArray, tableToIPC
-} = require('apache-arrow');
+ tableToIPC, vectorFromArray
+} from 'apache-arrow';
function process(N, nulls, msg) {
const vectors = {
@@ -76,7 +76,7 @@ function encode(name, type, values) {
const dt = table({ values });
// measure encoding times
- const qt = time(() => tableToIPC(dt.toArrow({ types: { values: type } })));
+ const qt = time(() => tableToIPC(toArrow(dt, { types: { values: type } })));
const at = time(
() => tableToIPC(new Table({ values: vectorFromArray(values, type) }))
);
@@ -86,7 +86,7 @@ function encode(name, type, values) {
const ab = tableToIPC(new Table({
values: vectorFromArray(values, type)
})).length;
- const qb = tableToIPC(dt.toArrow({ types: { values: type }})).length;
+ const qb = tableToIPC(toArrow(dt, { types: { values: type }})).length;
const jb = (new TextEncoder().encode(JSON.stringify(values))).length;
// check that arrow and arquero produce the same result
@@ -111,4 +111,4 @@ process(5e6, 0.05, '5M values, 5% nulls');
// run arrow serialization benchmarks
serialize(1e6, 0, '1M values');
-serialize(1e6, 0.05, '1M values, 5% nulls');
\ No newline at end of file
+serialize(1e6, 0.05, '1M values, 5% nulls');
diff --git a/perf/csv-perf.js b/perf/csv-perf.js
index 65f59a07..fc024d97 100644
--- a/perf/csv-perf.js
+++ b/perf/csv-perf.js
@@ -1,11 +1,11 @@
-const tape = require('tape');
-const time = require('./time');
-const { bools, dates, floats, ints, sample, strings } = require('./data-gen');
-const { fromCSV, table } = require('..');
+import tape from 'tape';
+import { time } from './time.js';
+import { bools, dates, floats, ints, sample, strings } from './data-gen.js';
+import { toCSV as _toCSV, fromCSV, table } from '../src/index.js';
function toCSV(...values) {
const cols = values.map((v, i) => [`col${i}`, v]);
- return table(cols).toCSV();
+ return _toCSV(table(cols));
}
function parse(csv, opt) {
@@ -37,4 +37,4 @@ function run(N, nulls, msg) {
}
run(1e5, 0, '100k values');
-run(1e5, 0.05, '100k values, 5% nulls');
\ No newline at end of file
+run(1e5, 0.05, '100k values, 5% nulls');
diff --git a/perf/data-gen.js b/perf/data-gen.js
index 26b0570c..7f353285 100644
--- a/perf/data-gen.js
+++ b/perf/data-gen.js
@@ -1,4 +1,4 @@
-function rint(min, max) {
+export function rint(min, max) {
let delta = min;
if (max === undefined) {
min = 0;
@@ -8,7 +8,7 @@ function rint(min, max) {
return (min + delta * Math.random()) | 0;
}
-function ints(n, min, max, nullf) {
+export function ints(n, min, max, nullf) {
const data = [];
for (let i = 0; i < n; ++i) {
const v = nullf && Math.random() < nullf ? null : rint(min, max);
@@ -17,7 +17,7 @@ function ints(n, min, max, nullf) {
return data;
}
-function floats(n, min, max, nullf) {
+export function floats(n, min, max, nullf) {
const data = [];
const delta = max - min;
for (let i = 0; i < n; ++i) {
@@ -29,7 +29,7 @@ function floats(n, min, max, nullf) {
return data;
}
-function dates(n, nullf) {
+export function dates(n, nullf) {
const data = [];
for (let i = 0; i < n; ++i) {
const v = nullf && Math.random() < nullf
@@ -40,7 +40,7 @@ function dates(n, nullf) {
return data;
}
-function strings(n) {
+export function strings(n) {
const c = 'bcdfghjlmpqrstvwxyz';
const v = 'aeiou';
const cn = c.length;
@@ -57,7 +57,7 @@ function strings(n) {
return data;
}
-function bools(n, nullf) {
+export function bools(n, nullf) {
const data = [];
for (let i = 0; i < n; ++i) {
const v = nullf && Math.random() < nullf ? null : (Math.random() < 0.5);
@@ -66,7 +66,7 @@ function bools(n, nullf) {
return data;
}
-function sample(n, values, nullf) {
+export function sample(n, values, nullf) {
const data = [];
for (let i = 0; i < n; ++i) {
const v = nullf && Math.random() < nullf
@@ -76,13 +76,3 @@ function sample(n, values, nullf) {
}
return data;
}
-
-module.exports = {
- rint,
- ints,
- floats,
- dates,
- strings,
- bools,
- sample
-};
\ No newline at end of file
diff --git a/perf/derive-perf.js b/perf/derive-perf.js
index 1a0fb185..f8a853a3 100644
--- a/perf/derive-perf.js
+++ b/perf/derive-perf.js
@@ -1,7 +1,7 @@
-const tape = require('tape');
-const time = require('./time');
-const { floats, sample, strings } = require('./data-gen');
-const { table } = require('..');
+import tape from 'tape';
+import { time } from './time.js';
+import { floats, sample, strings } from './data-gen.js';
+import { table } from '../src/index.js';
function run(N, nulls, msg) {
const dt = table({
@@ -39,4 +39,4 @@ function run(N, nulls, msg) {
}
run(1e6, 0, '1M values');
-run(1e6, 0.05, '1M values, 5% nulls');
\ No newline at end of file
+run(1e6, 0.05, '1M values, 5% nulls');
diff --git a/perf/escape-perf.js b/perf/escape-perf.js
index 7f19142e..ed4e323e 100644
--- a/perf/escape-perf.js
+++ b/perf/escape-perf.js
@@ -1,7 +1,7 @@
-const tape = require('tape');
-const time = require('./time');
-const { floats, sample, strings } = require('./data-gen');
-const aq = require('..');
+import tape from 'tape';
+import { time } from './time.js';
+import { floats, sample, strings } from './data-gen.js';
+import * as aq from '../src/index.js';
function run(N, nulls, msg) {
const off = 1;
@@ -25,4 +25,4 @@ function run(N, nulls, msg) {
}
run(1e6, 0, '1M values');
-run(1e6, 0.05, '1M values, 5% nulls');
\ No newline at end of file
+run(1e6, 0.05, '1M values, 5% nulls');
diff --git a/perf/filter-perf.js b/perf/filter-perf.js
index 66151a7a..5584ccd6 100644
--- a/perf/filter-perf.js
+++ b/perf/filter-perf.js
@@ -1,7 +1,7 @@
-const tape = require('tape');
-const time = require('./time');
-const { floats, ints, sample, strings } = require('./data-gen');
-const { table } = require('..');
+import tape from 'tape';
+import { time } from './time.js';
+import { floats, ints, sample, strings } from './data-gen.js';
+import { table } from '../src/index.js';
function run(N, nulls, msg) {
const dt = table({
@@ -19,21 +19,21 @@ function run(N, nulls, msg) {
table: time(() => dt.filter('d.a > 0')),
reify: time(() => dt.filter('d.a > 0').reify()),
object: time(a => a.filter(d => d.a > 0), dt.objects()),
- array: time(a => a.filter(v => v > 0), dt.column('a').data)
+ array: time(a => a.filter(v => v > 0), dt.column('a'))
},
{
type: 'float',
table: time(() => dt.filter('d.b > 0')),
reify: time(() => dt.filter('d.b > 0').reify()),
object: time(a => a.filter(d => d.b > 0), dt.objects()),
- array: time(a => a.filter(v => v > 0), dt.column('b').data)
+ array: time(a => a.filter(v => v > 0), dt.column('b'))
},
{
type: 'string',
table: time(() => dt.filter(`d.c === '${str}'`)),
reify: time(() => dt.filter(`d.c === '${str}'`).reify()),
object: time(a => a.filter(d => d.c === str), dt.objects()),
- array: time(a => a.filter(v => v === str), dt.column('c').data)
+ array: time(a => a.filter(v => v === str), dt.column('c'))
}
]);
t.end();
@@ -41,4 +41,4 @@ function run(N, nulls, msg) {
}
run(1e6, 0, '1M values');
-run(1e6, 0.05, '1M values, 5% nulls');
\ No newline at end of file
+run(1e6, 0.05, '1M values, 5% nulls');
diff --git a/perf/rollup-perf.js b/perf/rollup-perf.js
index 3a2aa96b..8d5e7732 100644
--- a/perf/rollup-perf.js
+++ b/perf/rollup-perf.js
@@ -1,7 +1,7 @@
-const tape = require('tape');
-const time = require('./time');
-const { floats, sample, strings } = require('./data-gen');
-const { table, op } = require('..');
+import tape from 'tape';
+import { time } from './time.js';
+import { floats, sample, strings } from './data-gen.js';
+import { op, table } from '../src/index.js';
function run(N, nulls, msg) {
const dt = table({
@@ -67,4 +67,4 @@ function run(N, nulls, msg) {
}
run(1e6, 0, '1M values');
-run(1e6, 0.05, '1M values, 5% nulls');
\ No newline at end of file
+run(1e6, 0.05, '1M values, 5% nulls');
diff --git a/perf/table-perf.js b/perf/table-perf.js
index cc47a0d6..cb1b63db 100644
--- a/perf/table-perf.js
+++ b/perf/table-perf.js
@@ -1,7 +1,7 @@
-const tape = require('tape');
-const time = require('./time');
-const { floats, ints, sample, strings } = require('./data-gen');
-const { from, table } = require('..');
+import tape from 'tape';
+import { time } from './time.js';
+import { floats, ints, sample, strings } from './data-gen.js';
+import { from, table } from '../src/index.js';
function run(N, nulls, msg) {
const dt = table({
@@ -31,4 +31,4 @@ function run(N, nulls, msg) {
}
run(1e5, 0, '100k values');
-run(1e5, 0.05, '100k values, 5% nulls');
\ No newline at end of file
+run(1e5, 0.05, '100k values, 5% nulls');
diff --git a/perf/time.js b/perf/time.js
index eada5eb8..11bc39f6 100644
--- a/perf/time.js
+++ b/perf/time.js
@@ -1,7 +1,7 @@
-const { performance } = require('perf_hooks');
+import { performance } from 'perf_hooks';
-module.exports = function time(fn, ...args) {
+export function time(fn, ...args) {
const t0 = performance.now();
fn(...args);
return Math.round(performance.now() - t0);
-};
\ No newline at end of file
+};
diff --git a/rollup.config.mjs b/rollup.config.js
similarity index 57%
rename from rollup.config.mjs
rename to rollup.config.js
index 4a1199e2..45d59c88 100644
--- a/rollup.config.mjs
+++ b/rollup.config.js
@@ -1,42 +1,20 @@
-import json from '@rollup/plugin-json';
import bundleSize from 'rollup-plugin-bundle-size';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import terser from '@rollup/plugin-terser';
-function onwarn(warning, defaultHandler) {
- if (warning.code !== 'CIRCULAR_DEPENDENCY') {
- defaultHandler(warning);
- }
-}
-
const name = 'aq';
-const external = [ 'apache-arrow', 'node-fetch' ];
+const external = [ 'apache-arrow' ];
const globals = { 'apache-arrow': 'Arrow' };
const plugins = [
- json(),
bundleSize(),
nodeResolve({ modulesOnly: true })
];
export default [
{
- input: 'src/index-node.js',
- external: ['acorn'].concat(external),
- plugins,
- onwarn,
- output: [
- {
- file: 'dist/arquero.node.js',
- format: 'cjs',
- name
- }
- ]
- },
- {
- input: 'src/index.js',
+ input: 'src/index-browser.js',
external,
plugins,
- onwarn,
output: [
{
file: 'dist/arquero.js',
@@ -54,4 +32,4 @@ export default [
}
]
}
-];
\ No newline at end of file
+];
diff --git a/src/api.js b/src/api.js
new file mode 100644
index 00000000..9960a0b8
--- /dev/null
+++ b/src/api.js
@@ -0,0 +1,32 @@
+// export internal class and method definitions
+export { BitSet } from './table/BitSet.js';
+export { Table } from './table/Table.js';
+export { ColumnTable } from './table/ColumnTable.js';
+export { default as Reducer } from './verbs/reduce/reducer.js';
+export { default as parse } from './expression/parse.js';
+export { default as walk_ast } from './expression/ast/walk.js';
+
+// public API
+export { seed } from './util/random.js';
+export { default as fromArrow } from './arrow/from-arrow.js';
+export { default as fromCSV } from './format/from-csv.js';
+export { default as fromFixed } from './format/from-fixed.js';
+export { default as fromJSON } from './format/from-json.js';
+export { default as toArrow } from './arrow/to-arrow.js';
+export { default as toArrowIPC } from './arrow/to-arrow-ipc.js';
+export { default as toCSV } from './format/to-csv.js';
+export { default as toHTML } from './format/to-html.js';
+export { default as toJSON } from './format/to-json.js';
+export { default as toMarkdown } from './format/to-markdown.js';
+export { default as bin } from './helpers/bin.js';
+export { default as escape } from './helpers/escape.js';
+export { default as desc } from './helpers/desc.js';
+export { default as field } from './helpers/field.js';
+export { default as frac } from './helpers/frac.js';
+export { default as names } from './helpers/names.js';
+export { default as rolling } from './helpers/rolling.js';
+export { all, endswith, matches, not, range, startswith } from './helpers/selection.js';
+export { default as agg } from './verbs/helpers/agg.js';
+export { default as op } from './op/op-api.js';
+export { addAggregateFunction, addFunction, addWindowFunction } from './op/register.js';
+export { table, from } from './table/index.js';
diff --git a/src/arrow/arrow-column.js b/src/arrow/arrow-column.js
index 2bb6e484..a8b43f10 100644
--- a/src/arrow/arrow-column.js
+++ b/src/arrow/arrow-column.js
@@ -1,68 +1,277 @@
-import arrowDictionary from './arrow-dictionary';
-import error from '../util/error';
-import repeat from '../util/repeat';
-import toString from '../util/to-string';
-import unroll from '../util/unroll';
-import { isDict, isFixedSizeList, isList, isStruct, isUtf8 } from './arrow-types';
+import sequence from '../op/functions/sequence.js';
+import error from '../util/error.js';
+import isFunction from '../util/is-function.js';
+import repeat from '../util/repeat.js';
+import toString from '../util/to-string.js';
+import unroll from '../util/unroll.js';
-const isListType = type => isList(type) || isFixedSizeList(type);
+// Hardwire Arrow type ids to sidestep hard dependency
+// https://github.com/apache/arrow/blob/master/js/src/enum.ts
+const isDict = ({ typeId }) => typeId === -1;
+const isInt = ({ typeId }) => typeId === 2;
+const isUtf8 = ({ typeId }) => typeId === 5;
+const isDecimal = ({ typeId }) => typeId === 7;
+const isDate = ({ typeId }) => typeId === 8;
+const isTimestamp = ({ typeId }) => typeId === 10;
+const isStruct = ({ typeId }) => typeId === 13;
+const isLargeUtf8 = ({ typeId }) => typeId === 20;
+const isListType = ({ typeId }) => typeId === 12 || typeId === 16;
/**
* Create an Arquero column that proxies access to an Arrow column.
- * @param {object} arrow An Apache Arrow column.
- * @return {import('../table/column').ColumnType} An Arquero-compatible column.
+ * @param {import('apache-arrow').Vector} vector An Apache Arrow column.
+ * @param {import('./types.js').ArrowColumnOptions} [options]
+ * Arrow conversion options.
+ * @return {import('../table/types.js').ColumnType}
+ * An Arquero-compatible column.
*/
-export default function arrowColumn(vector, nested) {
+export default function arrowColumn(vector, options) {
+ return isDict(vector.type)
+ ? dictionaryColumn(vector)
+ : proxyColumn(vector, options);
+}
+
+/**
+ * Internal method for Arquero column generation for Apache Arrow data
+ * @param {import('apache-arrow').Vector} vector An Apache Arrow column.
+ * @param {import('./types.js').ArrowColumnOptions} [options]
+ * Arrow conversion options.
+ * @return {import('../table/types.js').ColumnType}
+ * An Arquero-compatible column.
+ */
+function proxyColumn(vector, options = {}) {
const { type, length, numChildren } = vector;
- if (isDict(type)) return arrowDictionary(vector);
+ const {
+ convertDate = true,
+ convertDecimal = true,
+ convertTimestamp = true,
+ convertBigInt = false,
+ memoize = true
+ } = options;
- const get = numChildren && nested ? getNested(vector)
- : numChildren ? memoize(getNested(vector))
- : isUtf8(type) ? memoize(row => vector.get(row))
- : null;
+ // create a getter method for retrieving values
+ let get;
+ if (numChildren) {
+ // extract lists/structs to JS objects, possibly memoized
+ get = getNested(vector, options);
+ if (memoize) get = memoized(length, get);
+ } else if (memoize && (isUtf8(type) || isLargeUtf8(type))) {
+ // memoize string extraction
+ get = memoized(length, row => vector.get(row));
+ } else if ((convertDate && isDate(type))
+ || (convertTimestamp && isTimestamp(type))) {
+ // convert to Date type, memoized for object equality
+ get = memoized(length, row => {
+ const v = vector.get(row);
+ return v == null ? null : new Date(vector.get(row));
+ });
+ } else if (convertDecimal && isDecimal(type)) {
+ // map decimal to number
+ const scale = 1 / Math.pow(10, type.scale);
+ get = row => {
+ const v = vector.get(row);
+ return v == null ? null : decimalToNumber(v, scale);
+ };
+ } else if (convertBigInt && isInt(type) && type.bitWidth >= 64) {
+ // map bigint to number
+ get = row => {
+ const v = vector.get(row);
+ return v == null ? null : Number(v);
+ };
+ } else if (!isFunction(vector.at)) {
+ // backwards compatibility with older arrow versions
+ // the vector `at` method was added in Arrow v16
+ get = row => vector.get(row);
+ } else {
+ // use the arrow column directly
+ return vector;
+ }
- return get
- ? { vector, length, get, [Symbol.iterator]: () => iterator(length, get) }
- : vector;
+ // return a column proxy object using custom getter
+ return {
+ length,
+ at: get,
+ [Symbol.iterator]: () => (function* () {
+ for (let i = 0; i < length; ++i) {
+ yield get(i);
+ }
+ })()
+ };
}
-function memoize(get) {
- const values = [];
+/**
+ * Memoize expensive getter calls by caching retrieved values.
+ */
+function memoized(length, get) {
+ const values = Array(length);
return row => {
const v = values[row];
return v !== undefined ? v : (values[row] = get(row));
};
}
-function* iterator(n, get) {
- for (let i = 0; i < n; ++i) {
- yield get(i);
+// generate base values for big integers represented as a Uint32Array
+const BASE32 = Array.from(
+ { length: 8 },
+ (_, i) => Math.pow(2, i * 32)
+);
+
+/**
+ * Convert a fixed point decimal value to a double precision number.
+ * Note: if the value is sufficiently large the conversion may be lossy!
+ * @param {Uint32Array & { signed: boolean }} v a fixed point decimal value
+ * @param {number} scale a scale factor, corresponding to the
+ * number of fractional decimal digits in the fixed point value
+ * @return {number} the resulting number
+ */
+function decimalToNumber(v, scale) {
+ const n = v.length;
+ let x = 0;
+ if (v.signed && (v[n - 1] | 0) < 0) {
+ for (let i = 0; i < n; ++i) {
+ x += ~v[i] * BASE32[i];
+ }
+ x = -(x + 1);
+ } else {
+ for (let i = 0; i < n; ++i) {
+ x += v[i] * BASE32[i];
+ }
}
+ return x * scale;
}
-const arrayFrom = vector => vector.numChildren
- ? repeat(vector.length, getNested(vector))
- : vector.nullCount ? [...vector]
- : vector.toArray();
+// get an array for a given vector
+function arrayFrom(vector, options) {
+ return vector.numChildren ? repeat(vector.length, getNested(vector, options))
+ : vector.nullCount ? [...vector]
+ : vector.toArray();
+}
-const getNested = vector => isListType(vector.type) ? getList(vector)
- : isStruct(vector.type) ? getStruct(vector)
- : error(`Unsupported Arrow type: ${toString(vector.VectorName)}`);
+// generate a getter for a nested data type
+function getNested(vector, options) {
+ return isListType(vector.type) ? getList(vector, options)
+ : isStruct(vector.type) ? getStruct(vector, options)
+ : error(`Unsupported Arrow type: ${toString(vector.VectorName)}`);
+}
-const getList = vector => vector.nullCount
- ? row => vector.isValid(row) ? arrayFrom(vector.get(row)) : null
- : row => arrayFrom(vector.get(row));
+// generate a getter for a list data type
+function getList(vector, options) {
+ return vector.nullCount
+ ? row => vector.isValid(row)
+ ? arrayFrom(vector.get(row), options)
+ : null
+ : row => arrayFrom(vector.get(row), options);
+}
-function getStruct(vector) {
+// generate a getter for a struct (object) data type
+function getStruct(vector, options) {
+ // disable memoization for nested columns as we extract JS objects
+ const opt = { ...options, memoize: false };
const props = [];
const code = [];
vector.type.children.forEach((field, i) => {
- props.push(arrowColumn(vector.getChildAt(i), true));
- code.push(`${toString(field.name)}:_${i}.get(row)`);
+ props.push(arrowColumn(vector.getChildAt(i), opt));
+ code.push(`${toString(field.name)}:_${i}.at(row)`);
});
const get = unroll('row', '({' + code + '})', props);
return vector.nullCount
? row => vector.isValid(row) ? get(row) : null
: get;
-}
\ No newline at end of file
+}
+
+/**
+ * Create a new Arquero column that proxies access to an
+ * Apache Arrow dictionary column.
+ * @param {import('apache-arrow').Vector} vector
+ * An Apache Arrow dictionary column.
+ */
+function dictionaryColumn(vector) {
+ const { data, length, nullCount } = vector;
+ const dictionary = data[data.length - 1].dictionary;
+ const size = dictionary.length;
+ const keys = dictKeys(data || [vector], length, nullCount, size);
+ const get = memoized(size,
+ k => k == null || k < 0 || k >= size ? null : dictionary.get(k)
+ );
+
+ return {
+ vector,
+ length,
+ at: row => get(keys[row]),
+ key: row => keys[row],
+ keyFor(value) {
+ if (value === null) return nullCount ? size : -1;
+ for (let i = 0; i < size; ++i) {
+ if (get(i) === value) return i;
+ }
+ return -1;
+ },
+ groups(names) {
+ const s = size + (nullCount ? 1 : 0);
+ return { keys, get: [get], names, rows: sequence(0, s), size: s };
+ },
+ [Symbol.iterator]() {
+ return vector[Symbol.iterator]();
+ }
+ };
+}
+
+/**
+ * Generate a dictionary key array.
+ * @param {readonly any[]} chunks Arrow column chunks
+ * @param {number} length The length of the Arrow column
+ * @param {number} nulls The count of column null values
+ * @param {number} size The backing dictionary size
+ */
+function dictKeys(chunks, length, nulls, size) {
+ const v = chunks.length > 1 || nulls
+ ? flatten(chunks, length, chunks[0].type.indices)
+ : chunks[0].values;
+ return nulls ? nullKeys(chunks, v, size) : v;
+}
+
+/**
+ * Flatten Arrow column chunks into a single array.
+ */
+function flatten(chunks, length, type) {
+ const array = new type.ArrayType(length);
+ const n = chunks.length;
+ for (let i = 0, idx = 0, len; i < n; ++i) {
+ len = chunks[i].length;
+ array.set(chunks[i].values.subarray(0, len), idx);
+ idx += len;
+ }
+ return array;
+}
+
+/**
+ * Encode null values as an additional dictionary key.
+ * Returns a new key array with null values added.
+ * TODO: safeguard against integer overflow?
+ */
+function nullKeys(chunks, keys, key) {
+ // iterate over null bitmaps, encode null values as key
+ const n = chunks.length;
+ for (let i = 0, idx = 0, m, base, bits, byte; i < n; ++i) {
+ bits = chunks[i].nullBitmap;
+ m = chunks[i].length >> 3;
+ if (bits && bits.length) {
+ for (let j = 0; j <= m; ++j) {
+ if ((byte = bits[j]) !== 255) {
+ base = idx + (j << 3);
+ if ((byte & (1 << 0)) === 0) keys[base + 0] = key;
+ if ((byte & (1 << 1)) === 0) keys[base + 1] = key;
+ if ((byte & (1 << 2)) === 0) keys[base + 2] = key;
+ if ((byte & (1 << 3)) === 0) keys[base + 3] = key;
+ if ((byte & (1 << 4)) === 0) keys[base + 4] = key;
+ if ((byte & (1 << 5)) === 0) keys[base + 5] = key;
+ if ((byte & (1 << 6)) === 0) keys[base + 6] = key;
+ if ((byte & (1 << 7)) === 0) keys[base + 7] = key;
+ }
+ }
+ }
+ idx += chunks[i].length;
+ }
+ return keys;
+}
diff --git a/src/arrow/arrow-dictionary.js b/src/arrow/arrow-dictionary.js
deleted file mode 100644
index 95d98ff2..00000000
--- a/src/arrow/arrow-dictionary.js
+++ /dev/null
@@ -1,104 +0,0 @@
-import sequence from '../op/functions/sequence';
-
-/**
- * Create a new Arquero column that proxies access to an
- * Apache Arrow dictionary column.
- * @param {object} vector An Apache Arrow dictionary column.
- */
-export default function(vector) {
- const { data, length, nullCount } = vector;
- const dictionary = data[data.length - 1].dictionary;
- const size = dictionary.length;
- const keys = dictKeys(data || [vector], length, nullCount, size);
- const values = Array(size);
-
- const value = k => k == null || k < 0 || k >= size ? null
- : values[k] !== undefined ? values[k]
- : (values[k] = dictionary.get(k));
-
- return {
- vector,
- length,
-
- get: row => value(keys[row]),
-
- key: row => keys[row],
-
- keyFor(value) {
- if (value === null) return nullCount ? size : -1;
- for (let i = 0; i < size; ++i) {
- if (values[i] === undefined) values[i] = dictionary.get(i);
- if (values[i] === value) return i;
- }
- return -1;
- },
-
- groups(names) {
- const s = size + (nullCount ? 1 : 0);
- return { keys, get: [value], names, rows: sequence(0, s), size: s };
- },
-
- [Symbol.iterator]() {
- return vector[Symbol.iterator]();
- }
- };
-}
-
-/**
- * Generate a dictionary key array
- * @param {object[]} chunks Arrow column chunks
- * @param {number} length The length of the Arrow column
- * @param {number} nulls The count of column null values
- * @param {number} size The backing dictionary size
- */
-function dictKeys(chunks, length, nulls, size) {
- const v = chunks.length > 1 || nulls
- ? flatten(chunks, length, chunks[0].type.indices)
- : chunks[0].values;
- return nulls ? nullKeys(chunks, v, size) : v;
-}
-
-/**
- * Flatten Arrow column chunks into a single array.
- */
-function flatten(chunks, length, type) {
- const array = new type.ArrayType(length);
- const n = chunks.length;
- for (let i = 0, idx = 0, len; i < n; ++i) {
- len = chunks[i].length;
- array.set(chunks[i].values.subarray(0, len), idx);
- idx += len;
- }
- return array;
-}
-
-/**
- * Encode null values as an additional dictionary key.
- * Returns a new key array with null values added.
- * TODO: safeguard against integer overflow?
- */
-function nullKeys(chunks, keys, key) {
- // iterate over null bitmaps, encode null values as key
- const n = chunks.length;
- for (let i = 0, idx = 0, m, base, bits, byte; i < n; ++i) {
- bits = chunks[i].nullBitmap;
- m = chunks[i].length >> 3;
- if (bits && bits.length) {
- for (let j = 0; j <= m; ++j) {
- if ((byte = bits[j]) !== 255) {
- base = idx + (j << 3);
- if ((byte & (1 << 0)) === 0) keys[base + 0] = key;
- if ((byte & (1 << 1)) === 0) keys[base + 1] = key;
- if ((byte & (1 << 2)) === 0) keys[base + 2] = key;
- if ((byte & (1 << 3)) === 0) keys[base + 3] = key;
- if ((byte & (1 << 4)) === 0) keys[base + 4] = key;
- if ((byte & (1 << 5)) === 0) keys[base + 5] = key;
- if ((byte & (1 << 6)) === 0) keys[base + 6] = key;
- if ((byte & (1 << 7)) === 0) keys[base + 7] = key;
- }
- }
- }
- idx += chunks[i].length;
- }
- return keys;
-}
\ No newline at end of file
diff --git a/src/arrow/arrow-table.js b/src/arrow/arrow-table.js
index fec65fbf..de93d647 100644
--- a/src/arrow/arrow-table.js
+++ b/src/arrow/arrow-table.js
@@ -1,27 +1,38 @@
-import { Table, tableFromIPC } from 'apache-arrow';
-import error from '../util/error';
+import { Table, tableFromIPC, tableToIPC } from 'apache-arrow';
+import error from '../util/error.js';
-const fail = () => error(
+const fail = (cause) => error(
'Apache Arrow not imported, ' +
- 'see https://github.com/uwdata/arquero#usage'
+ 'see https://github.com/uwdata/arquero#usage',
+ cause
);
-export function table() {
+export function arrowTable(...args) {
// trap access to provide a helpful message
// when Apache Arrow has not been imported
try {
- return Table;
- } catch (err) { // eslint-disable-line no-unused-vars
- fail();
+ return new Table(...args);
+ } catch (err) {
+ fail(err);
}
}
-export function fromIPC() {
+export function arrowTableFromIPC(bytes) {
// trap access to provide a helpful message
// when Apache Arrow has not been imported
try {
- return tableFromIPC;
- } catch (err) { // eslint-disable-line no-unused-vars
- fail();
+ return tableFromIPC(bytes);
+ } catch (err) {
+ fail(err);
}
-}
\ No newline at end of file
+}
+
+export function arrowTableToIPC(table, format) {
+ // trap access to provide a helpful message
+ // when Apache Arrow has not been imported
+ try {
+ return tableToIPC(table, format);
+ } catch (err) {
+ fail(err);
+ }
+}
diff --git a/src/arrow/arrow-types.js b/src/arrow/arrow-types.js
deleted file mode 100644
index 6cba8a81..00000000
--- a/src/arrow/arrow-types.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Hardwire Arrow type ids to sidestep dependency
-// https://github.com/apache/arrow/blob/master/js/src/enum.ts
-
-export const isDict = ({ typeId }) => typeId === -1;
-export const isUtf8 = ({ typeId }) => typeId === 5;
-export const isList = ({ typeId }) => typeId === 12;
-export const isStruct = ({ typeId }) => typeId === 13;
-export const isFixedSizeList = ({ typeId }) => typeId === 16;
\ No newline at end of file
diff --git a/src/arrow/builder/array-builder.js b/src/arrow/builder/array-builder.js
index c4e99910..c9de3a49 100644
--- a/src/arrow/builder/array-builder.js
+++ b/src/arrow/builder/array-builder.js
@@ -1,4 +1,4 @@
-import { array } from './util';
+import { array } from './util.js';
export default function(type, length) {
const data = array(type.ArrayType, length);
@@ -6,4 +6,4 @@ export default function(type, length) {
set(value, index) { data[index] = value; },
data: () => ({ type, length, buffers: [null, data] })
};
-}
\ No newline at end of file
+}
diff --git a/src/arrow/builder/bool-builder.js b/src/arrow/builder/bool-builder.js
index c12a159a..a327ff32 100644
--- a/src/arrow/builder/bool-builder.js
+++ b/src/arrow/builder/bool-builder.js
@@ -1,4 +1,4 @@
-import { array } from './util';
+import { array } from './util.js';
export default function(type, length) {
const data = array(type.ArrayType, length / 8);
@@ -8,4 +8,4 @@ export default function(type, length) {
},
data: () => ({ type, length, buffers: [null, data] })
};
-}
\ No newline at end of file
+}
diff --git a/src/arrow/builder/date-day-builder.js b/src/arrow/builder/date-day-builder.js
index 6a7cd6f6..d7bb0129 100644
--- a/src/arrow/builder/date-day-builder.js
+++ b/src/arrow/builder/date-day-builder.js
@@ -1,4 +1,4 @@
-import { array } from './util';
+import { array } from './util.js';
export default function(type, length) {
const data = array(type.ArrayType, length);
@@ -6,4 +6,4 @@ export default function(type, length) {
set(value, index) { data[index] = (value / 86400000) | 0; },
data: () => ({ type, length, buffers: [null, data] })
};
-}
\ No newline at end of file
+}
diff --git a/src/arrow/builder/date-millis-builder.js b/src/arrow/builder/date-millis-builder.js
index a7f031b5..1bcdac1a 100644
--- a/src/arrow/builder/date-millis-builder.js
+++ b/src/arrow/builder/date-millis-builder.js
@@ -1,13 +1,9 @@
-import { array } from './util';
+import { array } from './util.js';
export default function(type, length) {
- const data = array(type.ArrayType, length << 1);
+ const data = array(type.ArrayType, length);
return {
- set(value, index) {
- const i = index << 1;
- data[ i] = (value % 4294967296) | 0;
- data[i+1] = (value / 4294967296) | 0;
- },
+ set(value, index) { data[index] = BigInt(value); },
data: () => ({ type, length, buffers: [null, data] })
};
-}
\ No newline at end of file
+}
diff --git a/src/arrow/builder/default-builder.js b/src/arrow/builder/default-builder.js
index d70f5a64..d0301ec9 100644
--- a/src/arrow/builder/default-builder.js
+++ b/src/arrow/builder/default-builder.js
@@ -9,4 +9,4 @@ export default function(type) {
set(value, index) { b.set(index, value); },
data: () => b.finish().flush()
};
-}
\ No newline at end of file
+}
diff --git a/src/arrow/builder/dictionary-builder.js b/src/arrow/builder/dictionary-builder.js
index 70058995..c301a7ae 100644
--- a/src/arrow/builder/dictionary-builder.js
+++ b/src/arrow/builder/dictionary-builder.js
@@ -1,5 +1,5 @@
-import utf8Builder from './utf8-builder';
-import { array, arrowVector } from './util';
+import utf8Builder from './utf8-builder.js';
+import { array, arrowVector } from './util.js';
export default function(type, length) {
const values = [];
@@ -33,4 +33,4 @@ function dictionary(type, values, strlen) {
const b = utf8Builder(type, values.length, strlen);
values.forEach(b.set);
return arrowVector(b.data());
-}
\ No newline at end of file
+}
diff --git a/src/arrow/builder/index.js b/src/arrow/builder/index.js
index 65a5423a..2e9dde71 100644
--- a/src/arrow/builder/index.js
+++ b/src/arrow/builder/index.js
@@ -1,11 +1,11 @@
import { Type } from 'apache-arrow';
-import arrayBuilder from './array-builder';
-import boolBuilder from './bool-builder';
-import dateDayBuilder from './date-day-builder';
-import dateMillisBuilder from './date-millis-builder';
-import defaultBuilder from './default-builder';
-import dictionaryBuilder from './dictionary-builder';
-import validBuilder from './valid-builder';
+import arrayBuilder from './array-builder.js';
+import boolBuilder from './bool-builder.js';
+import dateDayBuilder from './date-day-builder.js';
+import dateMillisBuilder from './date-millis-builder.js';
+import defaultBuilder from './default-builder.js';
+import dictionaryBuilder from './dictionary-builder.js';
+import validBuilder from './valid-builder.js';
export default function(type, nrows, nullable = true) {
let method;
@@ -37,4 +37,4 @@ export default function(type, nrows, nullable = true) {
return method == null ? defaultBuilder(type)
: nullable ? validBuilder(method(type, nrows), nrows)
: method(type, nrows);
-}
\ No newline at end of file
+}
diff --git a/src/arrow/builder/resolve-type.js b/src/arrow/builder/resolve-type.js
index c300dc6b..14eb3415 100644
--- a/src/arrow/builder/resolve-type.js
+++ b/src/arrow/builder/resolve-type.js
@@ -26,8 +26,8 @@ import {
Uint8,
Utf8
} from 'apache-arrow';
-import error from '../../util/error';
-import toString from '../../util/to-string';
+import error from '../../util/error.js';
+import toString from '../../util/to-string.js';
export default function(type) {
if (type instanceof DataType || type == null) {
@@ -94,4 +94,4 @@ export default function(type) {
'Use a data type constructor instead?'
);
}
-}
\ No newline at end of file
+}
diff --git a/src/arrow/builder/utf8-builder.js b/src/arrow/builder/utf8-builder.js
index 8a43dd5b..b8fb35d9 100644
--- a/src/arrow/builder/utf8-builder.js
+++ b/src/arrow/builder/utf8-builder.js
@@ -1,4 +1,4 @@
-import { array, ceil64Bytes, writeUtf8 } from './util';
+import { array, ceil64Bytes, writeUtf8 } from './util.js';
export default function(type, length, strlen) {
const offset = array(Int32Array, length + 1);
@@ -18,4 +18,4 @@ export default function(type, length, strlen) {
return { type, length, buffers: [offset, data] };
}
};
-}
\ No newline at end of file
+}
diff --git a/src/arrow/builder/util.js b/src/arrow/builder/util.js
index 2f9f0ca0..c3cc25f8 100644
--- a/src/arrow/builder/util.js
+++ b/src/arrow/builder/util.js
@@ -30,4 +30,4 @@ export function encodeInto(data, idx, str) {
return encoder.encodeInto(str, data.subarray(idx)).written;
}
-export const writeUtf8 = encoder.encodeInto ? encodeInto : encode;
\ No newline at end of file
+export const writeUtf8 = encoder.encodeInto ? encodeInto : encode;
diff --git a/src/arrow/builder/valid-builder.js b/src/arrow/builder/valid-builder.js
index d2f04188..ee21daec 100644
--- a/src/arrow/builder/valid-builder.js
+++ b/src/arrow/builder/valid-builder.js
@@ -1,4 +1,4 @@
-import { array } from './util';
+import { array } from './util.js';
export default function(builder, length) {
const valid = array(Uint8Array, length / 8);
@@ -22,4 +22,4 @@ export default function(builder, length) {
return d;
}
};
-}
\ No newline at end of file
+}
diff --git a/src/arrow/encode/data-from-objects.js b/src/arrow/encode/data-from-objects.js
index 334de681..af913308 100644
--- a/src/arrow/encode/data-from-objects.js
+++ b/src/arrow/encode/data-from-objects.js
@@ -1,6 +1,6 @@
-import { dataFromScan } from './data-from';
-import { profile } from './profiler';
-import resolveType from '../builder/resolve-type';
+import { dataFromScan } from './data-from.js';
+import { profile } from './profiler.js';
+import resolveType from '../builder/resolve-type.js';
export default function(data, name, nrows, scan, type, nullable = true) {
type = resolveType(type);
@@ -13,4 +13,4 @@ export default function(data, name, nrows, scan, type, nullable = true) {
}
return dataFromScan(nrows, scan, name, type, nullable);
-}
\ No newline at end of file
+}
diff --git a/src/arrow/encode/data-from-table.js b/src/arrow/encode/data-from-table.js
index 1e3324d5..85fe155e 100644
--- a/src/arrow/encode/data-from-table.js
+++ b/src/arrow/encode/data-from-table.js
@@ -3,10 +3,10 @@ import {
Int16, Int32, Int64, Int8,
Uint16, Uint32, Uint64, Uint8, Vector
} from 'apache-arrow';
-import { dataFromArray, dataFromScan } from './data-from';
-import { profile } from './profiler';
-import resolveType from '../builder/resolve-type';
-import isTypedArray from '../../util/is-typed-array';
+import { dataFromArray, dataFromScan } from './data-from.js';
+import { profile } from './profiler.js';
+import resolveType from '../builder/resolve-type.js';
+import isTypedArray from '../../util/is-typed-array.js';
export default function(table, name, nrows, scan, type, nullable = true) {
type = resolveType(type);
@@ -66,4 +66,4 @@ function typeFromArray(data) {
function typeCompatible(a, b) {
return !a || !b ? true : a.compareTo(b);
-}
\ No newline at end of file
+}
diff --git a/src/arrow/encode/data-from.js b/src/arrow/encode/data-from.js
index 06564da5..8b0a0ecd 100644
--- a/src/arrow/encode/data-from.js
+++ b/src/arrow/encode/data-from.js
@@ -1,5 +1,5 @@
-import builder from '../builder';
-import { arrowData, ceil64Bytes } from '../builder/util';
+import builder from '../builder/index.js';
+import { arrowData, ceil64Bytes } from '../builder/util.js';
export function dataFromArray(array, type) {
const length = array.length;
@@ -18,4 +18,4 @@ export function dataFromScan(nrows, scan, column, type, nullable = true) {
const b = builder(type, nrows, nullable);
scan(column, b.set);
return arrowData(b.data());
-}
\ No newline at end of file
+}
diff --git a/src/arrow/encode/index.js b/src/arrow/encode/index.js
deleted file mode 100644
index 8bdd2255..00000000
--- a/src/arrow/encode/index.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import { Table } from 'apache-arrow'; // eslint-disable-line no-unused-vars
-
-import dataFromObjects from './data-from-objects';
-import dataFromTable from './data-from-table';
-import { scanArray, scanTable } from './scan';
-import { table } from '../arrow-table';
-import error from '../../util/error';
-import isArray from '../../util/is-array';
-import isFunction from '../../util/is-function';
-
-/**
- * Options for Arrow encoding.
- * @typedef {object} ArrowFormatOptions
- * @property {number} [limit=Infinity] The maximum number of rows to include.
- * @property {number} [offset=0] The row offset indicating how many initial
- * rows to skip.
- * @property {string[]|(data: object) => string[]} [columns] Ordered list of
- * column names to include. If function-valued, the function should accept
- * a dataset as input and return an array of column name strings.
- * @property {object} [types] The Arrow data types to use. If specified,
- * the input should be an object with column names for keys and Arrow data
- * types for values. If a column type is not explicitly provided, type
- * inference will be performed to guess an appropriate type.
- */
-
-/**
- * Create an Apache Arrow table for an input dataset.
- * @param {Array|object} data An input dataset to convert to Arrow format.
- * If array-valued, the data should consist of an array of objects where
- * each entry represents a row and named properties represent columns.
- * Otherwise, the input data should be an Arquero table.
- * @param {ArrowFormatOptions} [options] Encoding options, including
- * column data types.
- * @return {Table} An Apache Arrow Table instance.
- */
-export default function(data, options = {}) {
- const { types = {} } = options;
- const { dataFrom, names, nrows, scan } = init(data, options);
- const cols = {};
- names.forEach(name => {
- const col = dataFrom(data, name, nrows, scan, types[name]);
- if (col.length !== nrows) {
- error('Column length mismatch');
- }
- cols[name] = col;
- });
- const T = table();
- return new T(cols);
-}
-
-function init(data, options) {
- const { columns, limit = Infinity, offset = 0 } = options;
- const names = isFunction(columns) ? columns(data)
- : isArray(columns) ? columns
- : null;
- if (isArray(data)) {
- return {
- dataFrom: dataFromObjects,
- names: names || Object.keys(data[0]),
- nrows: Math.min(limit, data.length - offset),
- scan: scanArray(data, limit, offset)
- };
- } else if (isTable(data)) {
- return {
- dataFrom: dataFromTable,
- names: names || data.columnNames(),
- nrows: Math.min(limit, data.numRows() - offset),
- scan: scanTable(data, limit, offset)
- };
- } else {
- error('Unsupported input data type');
- }
-}
-
-function isTable(data) {
- return data && isFunction(data.reify);
-}
\ No newline at end of file
diff --git a/src/arrow/encode/profiler.js b/src/arrow/encode/profiler.js
index 29c3883f..16d1b7ff 100644
--- a/src/arrow/encode/profiler.js
+++ b/src/arrow/encode/profiler.js
@@ -1,9 +1,9 @@
import { Field, FixedSizeList, List, Struct, Type } from 'apache-arrow';
-import resolveType from '../builder/resolve-type';
-import error from '../../util/error';
-import isArrayType from '../../util/is-array-type';
-import isDate from '../../util/is-date';
-import isExactUTCDate from '../../util/is-exact-utc-date';
+import resolveType from '../builder/resolve-type.js';
+import error from '../../util/error.js';
+import isArrayType from '../../util/is-array-type.js';
+import isDate from '../../util/is-date.js';
+import isExactUTCDate from '../../util/is-exact-utc-date.js';
export function profile(scan, column) {
const p = profiler();
@@ -100,6 +100,7 @@ function infer(p) {
return Type.Float64;
}
else if (p.bigints === valid) {
+ // @ts-ignore
const v = -p.min > p.max ? -p.min - 1n : p.max;
return p.min < 0
? v < 2 ** 63 ? Type.Int64
@@ -134,4 +135,4 @@ function infer(p) {
else {
error('Type inference failure');
}
-}
\ No newline at end of file
+}
diff --git a/src/arrow/encode/scan.js b/src/arrow/encode/scan.js
index 6d1991f6..b12a5886 100644
--- a/src/arrow/encode/scan.js
+++ b/src/arrow/encode/scan.js
@@ -1,4 +1,4 @@
-import isArrayType from '../../util/is-array-type';
+import isArrayType from '../../util/is-array-type.js';
export function scanArray(data, limit, offset) {
const n = Math.min(data.length, offset + limit);
@@ -14,12 +14,16 @@ export function scanTable(table, limit, offset) {
&& !table.isFiltered() && !table.isOrdered();
return (column, visit) => {
+ const isArray = isArrayType(column);
let i = -1;
- scanAll && isArrayType(column.data)
- ? column.data.forEach(visit)
+ scanAll && isArray
+ ? column.forEach(visit)
: table.scan(
- row => visit(column.get(row), ++i),
+ // optimize column value access
+ isArray
+ ? row => visit(column[row], ++i)
+ : row => visit(column.at(row), ++i),
true, limit, offset
);
};
-}
\ No newline at end of file
+}
diff --git a/src/arrow/from-arrow.js b/src/arrow/from-arrow.js
new file mode 100644
index 00000000..cd9a0602
--- /dev/null
+++ b/src/arrow/from-arrow.js
@@ -0,0 +1,39 @@
+import { arrowTableFromIPC } from './arrow-table.js';
+import arrowColumn from './arrow-column.js';
+import resolve, { all } from '../helpers/selection.js';
+import { columnSet } from '../table/ColumnSet.js';
+import { ColumnTable } from '../table/ColumnTable.js';
+
+/**
+ * Create a new table backed by an Apache Arrow table instance.
+ * @param {import('./types.js').ArrowInput} arrow
+ * An Apache Arrow data table or Arrow IPC byte buffer.
+ * @param {import('./types.js').ArrowOptions} [options]
+ * Options for Arrow import.
+ * @return {ColumnTable} A new table containing the imported values.
+ */
+export default function(arrow, options) {
+ if (arrow instanceof ArrayBuffer || ArrayBuffer.isView(arrow)) {
+ arrow = arrowTableFromIPC(arrow);
+ }
+
+ const {
+ columns = all(),
+ ...columnOptions
+ } = options || {};
+
+ // resolve column selection
+ const fields = arrow.schema.fields.map(f => f.name);
+ const sel = resolve({
+ columnNames: test => test ? fields.filter(test) : fields.slice(),
+ columnIndex: name => fields.indexOf(name)
+ }, columns);
+
+ // build Arquero columns for backing Arrow columns
+ const cols = columnSet();
+ sel.forEach((name, key) => {
+ cols.add(name, arrowColumn(arrow.getChild(key), columnOptions));
+ });
+
+ return new ColumnTable(cols.data, cols.names);
+}
diff --git a/src/arrow/to-arrow-ipc.js b/src/arrow/to-arrow-ipc.js
new file mode 100644
index 00000000..9c079ec6
--- /dev/null
+++ b/src/arrow/to-arrow-ipc.js
@@ -0,0 +1,18 @@
+import { arrowTableToIPC } from './arrow-table.js';
+import toArrow from './to-arrow.js';
+
+/**
+ * Format a table as binary data in the Apache Arrow IPC format.
+ * @param {object[]|import('../table/Table.js').Table} data The table data
+ * @param {import('./types.js').ArrowIPCFormatOptions} [options]
+ * The Arrow IPC formatting options. Set the *format* option to `'stream'`
+ * or `'file'` to specify the IPC format.
+ * @return {Uint8Array} A new Uint8Array of Arrow-encoded binary data.
+ */
+export default function(data, options = {}) {
+ const { format = 'stream', ...toArrowOptions } = options;
+ if (!['stream', 'file'].includes(format)) {
+ throw Error('Unrecognised Arrow IPC output format');
+ }
+ return arrowTableToIPC(toArrow(data, toArrowOptions), format);
+}
diff --git a/src/arrow/to-arrow.js b/src/arrow/to-arrow.js
new file mode 100644
index 00000000..e4adb132
--- /dev/null
+++ b/src/arrow/to-arrow.js
@@ -0,0 +1,59 @@
+import { arrowTable } from './arrow-table.js';
+import dataFromObjects from './encode/data-from-objects.js';
+import dataFromTable from './encode/data-from-table.js';
+import { scanArray, scanTable } from './encode/scan.js';
+import error from '../util/error.js';
+import isArray from '../util/is-array.js';
+import isFunction from '../util/is-function.js';
+
+/**
+ * Create an Apache Arrow table for an input dataset.
+ * @param {object[]|import('../table/Table.js').Table} data An input dataset
+ * to convert to Arrow format. If array-valued, the data should consist of an
+ * array of objects where each entry represents a row and named properties
+ * represent columns. Otherwise, the input data should be an Arquero table.
+ * @param {import('./types.js').ArrowFormatOptions} [options]
+ * Encoding options, including column data types.
+ * @return {import('apache-arrow').Table} An Apache Arrow Table instance.
+ */
+export default function(data, options = {}) {
+ const { types = {} } = options;
+ const { dataFrom, names, nrows, scan } = init(data, options);
+ const cols = {};
+ names.forEach(name => {
+ const col = dataFrom(data, name, nrows, scan, types[name]);
+ if (col.length !== nrows) {
+ error('Column length mismatch');
+ }
+ cols[name] = col;
+ });
+ return arrowTable(cols);
+}
+
+function init(data, options) {
+ const { columns, limit = Infinity, offset = 0 } = options;
+ const names = isFunction(columns) ? columns(data)
+ : isArray(columns) ? columns
+ : null;
+ if (isArray(data)) {
+ return {
+ dataFrom: dataFromObjects,
+ names: names || Object.keys(data[0]),
+ nrows: Math.min(limit, data.length - offset),
+ scan: scanArray(data, limit, offset)
+ };
+ } else if (isTable(data)) {
+ return {
+ dataFrom: dataFromTable,
+ names: names || data.columnNames(),
+ nrows: Math.min(limit, data.numRows() - offset),
+ scan: scanTable(data, limit, offset)
+ };
+ } else {
+ error('Unsupported input data type');
+ }
+}
+
+function isTable(data) {
+ return data && isFunction(data.reify);
+}
diff --git a/src/arrow/types.ts b/src/arrow/types.ts
new file mode 100644
index 00000000..f3cf8f10
--- /dev/null
+++ b/src/arrow/types.ts
@@ -0,0 +1,91 @@
+import { DataType, Table } from 'apache-arrow';
+import type { Select, TypedArray } from '../table/types.js';
+
+/** Arrow input data as bytes or loaded table. */
+export type ArrowInput =
+ | ArrayBuffer
+ | TypedArray
+ | Table;
+
+/** Options for Apache Arrow column conversion. */
+export interface ArrowColumnOptions {
+ /**
+ * Flag (default `true`) to convert Arrow date values to JavaScript Date
+ * objects. If false, defaults to what the Arrow implementation provides,
+ * typically timestamps as number values.
+ */
+ convertDate?: boolean;
+ /**
+ * Flag (default `true`) to convert Arrow fixed point decimal values to
+ * JavaScript numbers. If false, defaults to what the Arrow implementation
+ * provides, typically byte arrays. The conversion will be lossy if the
+ * decimal can not be exactly represented as a double-precision floating
+ * point number.
+ */
+ convertDecimal?: boolean;
+ /**
+ * Flag (default `true`) to convert Arrow timestamp values to JavaScript
+ * Date objects. If false, defaults to what the Arrow implementation
+ * provides, typically timestamps as number values.
+ */
+ convertTimestamp?: boolean;
+ /**
+ * Flag (default `false`) to convert Arrow integers with bit widths of 64
+ * bits or higher to JavaScript numbers. If false, defaults to what the
+ * Arrow implementation provides, typically `BigInt` values. The conversion
+ * will be lossy if the integer is so large it can not be exactly
+ * represented as a double-precision floating point number.
+ */
+ convertBigInt?: boolean;
+ /**
+ * A hint (default `true`) to enable memoization of expensive conversions.
+ * If true, memoization is applied for string and nested (list, struct)
+ * types, caching extracted values to enable faster access. Memoization
+ * is also applied to converted Date values, in part to ensure exact object
+ * equality. This hint is ignored for dictionary columns, whose values are
+ * always memoized.
+ */
+ memoize?: boolean;
+}
+
+/** Options for Apache Arrow import. */
+export interface ArrowOptions extends ArrowColumnOptions {
+ /**
+ * An ordered set of columns to import. The input may consist of column name
+ * strings, column integer indices, objects with current column names as
+ * keys and new column names as values (for renaming), or selection helper
+ * functions such as *all*, *not*, or *range*.
+ */
+ columns?: Select;
+}
+
+/** Options for Arrow encoding. */
+export interface ArrowFormatOptions {
+ /** The maximum number of rows to include (default `Infinity`). */
+ limit?: number;
+ /**
+ * The row offset (default `0`) indicating how many initial rows to skip.
+ */
+ offset?: number;
+ /**
+ * Ordered list of column names to include. If function-valued, the
+ * function should accept a dataset as input and return an array of
+ * column name strings. If unspecified all columns are included.
+ */
+ columns?: string[] | ((data: any) => string[]);
+ /**
+ * The Arrow data types to use. If specified, the input should be an
+ * object with column names for keys and Arrow data types for values.
+ * If a column type is not explicitly provided, type inference will be
+ * performed to guess an appropriate type.
+ */
+ types?: Record;
+}
+
+/** Options for Arrow IPC encoding. */
+export interface ArrowIPCFormatOptions extends ArrowFormatOptions {
+ /**
+ * The Arrow IPC byte format to use. One of `'stream'` (default) or `'file'`.
+ */
+ format?: 'stream' | 'file';
+}
diff --git a/src/engine/derive.js b/src/engine/derive.js
deleted file mode 100644
index dc05121a..00000000
--- a/src/engine/derive.js
+++ /dev/null
@@ -1,78 +0,0 @@
-import { window } from './window/window';
-import { aggregate } from './reduce/util';
-import { hasWindow } from '../op';
-import columnSet from '../table/column-set';
-import repeat from '../util/repeat';
-
-function isWindowed(op) {
- return hasWindow(op.name) ||
- op.frame && (
- Number.isFinite(op.frame[0]) ||
- Number.isFinite(op.frame[1])
- );
-}
-
-export default function(table, { names, exprs, ops }, options = {}) {
- // instantiate output data
- const total = table.totalRows();
- const cols = columnSet(options.drop ? null : table);
- const data = names.map(name => cols.add(name, Array(total)));
-
- // analyze operations, compute non-windowed aggregates
- const [ aggOps, winOps ] = segmentOps(ops);
-
- const size = table.isGrouped() ? table.groups().size : 1;
- const result = aggregate(
- table, aggOps,
- repeat(ops.length, () => Array(size))
- );
-
- // perform table scans to generate output values
- winOps.length
- ? window(table, data, exprs, result, winOps)
- : output(table, data, exprs, result);
-
- return table.create(cols);
-}
-
-function segmentOps(ops) {
- const aggOps = [];
- const winOps = [];
- const n = ops.length;
-
- for (let i = 0; i < n; ++i) {
- const op = ops[i];
- op.id = i;
- (isWindowed(op) ? winOps : aggOps).push(op);
- }
-
- return [aggOps, winOps];
-}
-
-function output(table, cols, exprs, result) {
- const bits = table.mask();
- const data = table.data();
- const { keys } = table.groups() || {};
- const op = keys
- ? (id, row) => result[id][keys[row]]
- : id => result[id][0];
-
- const m = cols.length;
- for (let j = 0; j < m; ++j) {
- const get = exprs[j];
- const col = cols[j];
-
- // inline the following for performance:
- // table.scan((i, data) => col[i] = get(i, data, op));
- if (bits) {
- for (let i = bits.next(0); i >= 0; i = bits.next(i + 1)) {
- col[i] = get(i, data, op);
- }
- } else {
- const n = table.totalRows();
- for (let i = 0; i < n; ++i) {
- col[i] = get(i, data, op);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/engine/filter.js b/src/engine/filter.js
deleted file mode 100644
index 26777821..00000000
--- a/src/engine/filter.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import BitSet from '../table/bit-set';
-
-export default function(table, predicate) {
- const n = table.totalRows();
- const bits = table.mask();
- const data = table.data();
- const filter = new BitSet(n);
-
- // inline the following for performance:
- // table.scan((row, data) => { if (predicate(row, data)) filter.set(row); });
- if (bits) {
- for (let i = bits.next(0); i >= 0; i = bits.next(i + 1)) {
- if (predicate(i, data)) filter.set(i);
- }
- } else {
- for (let i = 0; i < n; ++i) {
- if (predicate(i, data)) filter.set(i);
- }
- }
-
- return table.create({ filter });
-}
\ No newline at end of file
diff --git a/src/engine/fold.js b/src/engine/fold.js
deleted file mode 100644
index 865f396c..00000000
--- a/src/engine/fold.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import unroll from './unroll';
-import { aggregateGet } from './reduce/util';
-
-export default function(table, { names = [], exprs = [], ops = [] }, options = {}) {
- if (names.length === 0) return table;
-
- const [k = 'key', v = 'value'] = options.as || [];
- const vals = aggregateGet(table, ops, exprs);
-
- return unroll(
- table,
- {
- names: [k, v],
- exprs: [() => names, (row, data) => vals.map(fn => fn(row, data))]
- },
- { ...options, drop: names }
- );
-}
\ No newline at end of file
diff --git a/src/engine/groupby.js b/src/engine/groupby.js
deleted file mode 100644
index 3d5914c7..00000000
--- a/src/engine/groupby.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import { aggregateGet } from './reduce/util';
-import keyFunction from '../util/key-function';
-
-export default function(table, exprs) {
- return table.create({
- groups: createGroups(table, exprs)
- });
-}
-
-function createGroups(table, { names = [], exprs = [], ops = [] }) {
- const n = names.length;
- if (n === 0) return null;
-
- // check for optimized path when grouping by a single field
- // use pre-calculated groups if available
- if (n === 1 && !table.isFiltered() && exprs[0].field) {
- const col = table.column(exprs[0].field);
- if (col.groups) return col.groups(names);
- }
-
- let get = aggregateGet(table, ops, exprs);
- const getKey = keyFunction(get);
- const nrows = table.totalRows();
- const keys = new Uint32Array(nrows);
- const index = {};
- const rows = [];
-
- // inline table scan for performance
- const data = table.data();
- const bits = table.mask();
- if (bits) {
- for (let i = bits.next(0); i >= 0; i = bits.next(i + 1)) {
- const key = getKey(i, data) + '';
- const val = index[key];
- keys[i] = val != null ? val : (index[key] = rows.push(i) - 1);
- }
- } else {
- for (let i = 0; i < nrows; ++i) {
- const key = getKey(i, data) + '';
- const val = index[key];
- keys[i] = val != null ? val : (index[key] = rows.push(i) - 1);
- }
- }
-
- if (!ops.length) {
- // capture data in closure, so no interaction with select
- get = get.map(f => row => f(row, data));
- }
-
- return { keys, get, names, rows, size: rows.length };
-}
\ No newline at end of file
diff --git a/src/engine/impute.js b/src/engine/impute.js
deleted file mode 100644
index d4d40026..00000000
--- a/src/engine/impute.js
+++ /dev/null
@@ -1,134 +0,0 @@
-import { aggregateGet } from './reduce/util';
-import columnSet from '../table/column-set';
-import isValid from '../util/is-valid';
-import keyFunction from '../util/key-function';
-import unroll from '../util/unroll';
-
-export default function(table, values, keys, arrays) {
- const write = keys && keys.length;
- return impute(
- write ? expand(table, keys, arrays) : table,
- values,
- write
- );
-}
-
-function impute(table, { names, exprs, ops }, write) {
- const gets = aggregateGet(table, ops, exprs);
- const cols = write ? null : columnSet(table);
- const rows = table.totalRows();
-
- names.forEach((name, i) => {
- const col = table.column(name);
- const out = write ? col.data : cols.add(name, Array(rows));
- const get = gets[i];
-
- table.scan(idx => {
- const v = col.get(idx);
- out[idx] = !isValid(v) ? get(idx) : v;
- });
- });
-
- return write ? table : table.create(cols);
-}
-
-function expand(table, keys, values) {
- const groups = table.groups();
- const data = table.data();
-
- // expansion keys and accessors
- const keyNames = (groups ? groups.names : []).concat(keys);
- const keyGet = (groups ? groups.get : [])
- .concat(keys.map(key => table.getter(key)));
-
- // build hash of existing rows
- const hash = new Set();
- const keyTable = keyFunction(keyGet);
- table.scan((idx, data) => hash.add(keyTable(idx, data)));
-
- // initialize output table data
- const names = table.columnNames();
- const cols = columnSet();
- const out = names.map(name => cols.add(name, []));
- names.forEach((name, i) => {
- const old = data[name];
- const col = out[i];
- table.scan(row => col.push(old.get(row)));
- });
-
- // enumerate expanded value sets and augment output table
- const keyEnum = keyFunction(keyGet.map((k, i) => a => a[i]));
- const set = unroll(
- 'v',
- '{' + out.map((_, i) => `_${i}.push(v[$${i}]);`).join('') + '}',
- out, names.map(name => keyNames.indexOf(name))
- );
-
- if (groups) {
- let row = groups.keys.length;
- const prod = values.reduce((p, a) => p * a.length, groups.size);
- const keys = new Uint32Array(prod + (row - hash.size));
- keys.set(groups.keys);
- enumerate(groups, values, (vec, idx) => {
- if (!hash.has(keyEnum(vec))) {
- set(vec);
- keys[row++] = idx[0];
- }
- });
- cols.groupby({ ...groups, keys });
- } else {
- enumerate(groups, values, vec => {
- if (!hash.has(keyEnum(vec))) set(vec);
- });
- }
-
- return table.create(cols.new());
-}
-
-function enumerate(groups, values, callback) {
- const offset = groups ? groups.get.length : 0;
- const pad = groups ? 1 : 0;
- const len = pad + values.length;
- const lens = new Int32Array(len);
- const idxs = new Int32Array(len);
- const set = [];
-
- if (groups) {
- const { get, rows, size } = groups;
- lens[0] = size;
- set.push((vec, idx) => {
- const row = rows[idx];
- for (let i = 0; i < offset; ++i) {
- vec[i] = get[i](row);
- }
- });
- }
-
- values.forEach((a, i) => {
- const j = i + offset;
- lens[i + pad] = a.length;
- set.push((vec, idx) => vec[j] = a[idx]);
- });
-
- const vec = Array(offset + values.length);
-
- // initialize value vector
- for (let i = 0; i < len; ++i) {
- set[i](vec, 0);
- }
- callback(vec, idxs);
-
- // enumerate all combinations of values
- for (let i = len - 1; i >= 0;) {
- const idx = ++idxs[i];
- if (idx < lens[i]) {
- set[i](vec, idx);
- callback(vec, idxs);
- i = len - 1;
- } else {
- idxs[i] = 0;
- set[i](vec, 0);
- --i;
- }
- }
-}
\ No newline at end of file
diff --git a/src/engine/join-filter.js b/src/engine/join-filter.js
deleted file mode 100644
index e71ca35e..00000000
--- a/src/engine/join-filter.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import { rowLookup } from './join/lookup';
-import BitSet from '../table/bit-set';
-import isArray from '../util/is-array';
-
-export default function(tableL, tableR, predicate, options = {}) {
- // calculate semi-join filter mask
- const filter = new BitSet(tableL.totalRows());
- const join = isArray(predicate) ? hashSemiJoin : loopSemiJoin;
- join(filter, tableL, tableR, predicate);
-
- // if anti-join, negate the filter
- if (options.anti) {
- filter.not().and(tableL.mask());
- }
-
- return tableL.create({ filter });
-}
-
-function hashSemiJoin(filter, tableL, tableR, [keyL, keyR]) {
- // build lookup table
- const lut = rowLookup(tableR, keyR);
-
- // scan table, update filter with matches
- tableL.scan((rowL, data) => {
- const rowR = lut.get(keyL(rowL, data));
- if (rowR >= 0) filter.set(rowL);
- });
-}
-
-function loopSemiJoin(filter, tableL, tableR, predicate) {
- const nL = tableL.numRows();
- const nR = tableR.numRows();
- const dataL = tableL.data();
- const dataR = tableR.data();
-
- if (tableL.isFiltered() || tableR.isFiltered()) {
- // use indices as at least one table is filtered
- const idxL = tableL.indices(false);
- const idxR = tableR.indices(false);
- for (let i = 0; i < nL; ++i) {
- const rowL = idxL[i];
- for (let j = 0; j < nR; ++j) {
- if (predicate(rowL, dataL, idxR[j], dataR)) {
- filter.set(rowL);
- break;
- }
- }
- }
- } else {
- // no filters, enumerate row indices directly
- for (let i = 0; i < nL; ++i) {
- for (let j = 0; j < nR; ++j) {
- if (predicate(i, dataL, j, dataR)) {
- filter.set(i);
- break;
- }
- }
- }
- }
-}
-
-// export default function(tableL, tableR, predicate, options = {}) {
-// const filter = new BitSet(tableL.totalRows());
-// const nL = tableL.numRows();
-// const nR = tableR.numRows();
-// const dataL = tableL.data();
-// const dataR = tableR.data();
-
-// if (tableL.isFiltered() || tableR.isFiltered()) {
-// // use indices as at least one table is filtered
-// const idxL = tableL.indices(false);
-// const idxR = tableR.indices(false);
-// for (let i = 0; i < nL; ++i) {
-// const rowL = idxL[i];
-// for (let j = 0; j < nR; ++j) {
-// if (predicate(rowL, dataL, idxR[j], dataR)) {
-// filter.set(rowL);
-// break;
-// }
-// }
-// }
-// } else {
-// // no filters, enumerate row indices directly
-// for (let i = 0; i < nL; ++i) {
-// for (let j = 0; j < nR; ++j) {
-// if (predicate(i, dataL, j, dataR)) {
-// filter.set(i);
-// break;
-// }
-// }
-// }
-// }
-
-// // if anti-join, negate the filter
-// if (options.anti) {
-// filter.not().and(tableL.mask());
-// }
-
-// return tableL.create({ filter });
-// }
\ No newline at end of file
diff --git a/src/engine/join.js b/src/engine/join.js
deleted file mode 100644
index e242402f..00000000
--- a/src/engine/join.js
+++ /dev/null
@@ -1,110 +0,0 @@
-import { indexLookup } from './join/lookup';
-import columnSet from '../table/column-set';
-import concat from '../util/concat';
-import isArray from '../util/is-array';
-import unroll from '../util/unroll';
-
-function emitter(columns, getters) {
- const args = ['i', 'a', 'j', 'b'];
- return unroll(
- args,
- '{' + concat(columns, (_, i) => `_${i}.push($${i}(${args}));`) + '}',
- columns, getters
- );
-}
-
-export default function(tableL, tableR, predicate, { names, exprs }, options = {}) {
- // initialize data for left table
- const dataL = tableL.data();
- const idxL = tableL.indices(false);
- const nL = idxL.length;
- const hitL = new Int32Array(nL);
-
- // initialize data for right table
- const dataR = tableR.data();
- const idxR = tableR.indices(false);
- const nR = idxR.length;
- const hitR = new Int32Array(nR);
-
- // initialize output data
- const ncols = names.length;
- const cols = columnSet();
- const columns = Array(ncols);
- const getters = Array(ncols);
- for (let i = 0; i < names.length; ++i) {
- columns[i] = cols.add(names[i], []);
- getters[i] = exprs[i];
- }
- const emit = emitter(columns, getters);
-
- // perform join
- const join = isArray(predicate) ? hashJoin : loopJoin;
- join(emit, predicate, dataL, dataR, idxL, idxR, hitL, hitR, nL, nR);
-
- if (options.left) {
- for (let i = 0; i < nL; ++i) {
- if (!hitL[i]) {
- emit(idxL[i], dataL, -1, dataR);
- }
- }
- }
-
- if (options.right) {
- for (let j = 0; j < nR; ++j) {
- if (!hitR[j]) {
- emit(-1, dataL, idxR[j], dataR);
- }
- }
- }
-
- return tableL.create(cols.new());
-}
-
-function loopJoin(emit, predicate, dataL, dataR, idxL, idxR, hitL, hitR, nL, nR) {
- // perform nested-loops join
- for (let i = 0; i < nL; ++i) {
- const rowL = idxL[i];
- for (let j = 0; j < nR; ++j) {
- const rowR = idxR[j];
- if (predicate(rowL, dataL, rowR, dataR)) {
- emit(rowL, dataL, rowR, dataR);
- hitL[i] = 1;
- hitR[j] = 1;
- }
- }
- }
-}
-
-function hashJoin(emit, [keyL, keyR], dataL, dataR, idxL, idxR, hitL, hitR, nL, nR) {
- // determine which table to hash
- let dataScan, keyScan, hitScan, idxScan;
- let dataHash, keyHash, hitHash, idxHash;
- let emitScan = emit;
- if (nL >= nR) {
- dataScan = dataL; keyScan = keyL; hitScan = hitL; idxScan = idxL;
- dataHash = dataR; keyHash = keyR; hitHash = hitR; idxHash = idxR;
- } else {
- dataScan = dataR; keyScan = keyR; hitScan = hitR; idxScan = idxR;
- dataHash = dataL; keyHash = keyL; hitHash = hitL; idxHash = idxL;
- emitScan = (i, a, j, b) => emit(j, b, i, a);
- }
-
- // build lookup table
- const lut = indexLookup(idxHash, dataHash, keyHash);
-
- // scan other table
- const m = idxScan.length;
- for (let j = 0; j < m; ++j) {
- const rowScan = idxScan[j];
- const list = lut.get(keyScan(rowScan, dataScan));
- if (list) {
- const n = list.length;
- for (let k = 0; k < n; ++k) {
- const i = list[k];
- emitScan(rowScan, dataScan, idxHash[i], dataHash);
- hitHash[i] = 1;
- }
- hitScan[j] = 1;
- }
- }
-}
\ No newline at end of file
diff --git a/src/engine/lookup.js b/src/engine/lookup.js
deleted file mode 100644
index ee435580..00000000
--- a/src/engine/lookup.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import { rowLookup } from './join/lookup';
-import { aggregateGet } from './reduce/util';
-import columnSet from '../table/column-set';
-import NULL from '../util/null';
-import concat from '../util/concat';
-import unroll from '../util/unroll';
-
-export default function(tableL, tableR, [keyL, keyR], { names, exprs, ops }) {
- // instantiate output data
- const cols = columnSet(tableL);
- const total = tableL.totalRows();
- names.forEach(name => cols.add(name, Array(total).fill(NULL)));
-
- // build lookup table
- const lut = rowLookup(tableR, keyR);
-
- // generate setter function for lookup match
- const set = unroll(
- ['lr', 'rr', 'data'],
- '{' + concat(names, (_, i) => `_[${i}][lr] = $[${i}](rr, data);`) + '}',
- names.map(name => cols.data[name]),
- aggregateGet(tableR, ops, exprs)
- );
-
- // find matching rows, set values on match
- const dataR = tableR.data();
- tableL.scan((lrow, data) => {
- const rrow = lut.get(keyL(lrow, data));
- if (rrow >= 0) set(lrow, rrow, dataR);
- });
-
- return tableL.create(cols);
-}
\ No newline at end of file
diff --git a/src/engine/orderby.js b/src/engine/orderby.js
deleted file mode 100644
index 23adbdda..00000000
--- a/src/engine/orderby.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function(table, comparator) {
- return table.create({ order: comparator });
-}
diff --git a/src/engine/pivot.js b/src/engine/pivot.js
deleted file mode 100644
index bb2e1562..00000000
--- a/src/engine/pivot.js
+++ /dev/null
@@ -1,109 +0,0 @@
-import { aggregate, aggregateGet, groupOutput } from './reduce/util';
-import columnSet from '../table/column-set';
-
-const opt = (value, defaultValue) => value != null ? value : defaultValue;
-
-export default function(table, on, values, options = {}) {
- const { keys, keyColumn } = pivotKeys(table, on, options);
- const vsep = opt(options.valueSeparator, '_');
- const namefn = values.names.length > 1
- ? (i, name) => name + vsep + keys[i]
- : i => keys[i];
-
- // perform separate aggregate operations for each key
- // if keys do not match, emit NaN so aggregate skips it
- // use custom toString method for proper field resolution
- const results = keys.map(
- k => aggregate(table, values.ops.map(op => {
- if (op.name === 'count') { // fix #273
- const fn = r => k === keyColumn[r] ? 1 : NaN;
- fn.toString = () => k + ':1';
- return { ...op, name: 'sum', fields: [fn] };
- }
- const fields = op.fields.map(f => {
- const fn = (r, d) => k === keyColumn[r] ? f(r, d) : NaN;
- fn.toString = () => k + ':' + f;
- return fn;
- });
- return { ...op, fields };
- }))
- );
-
- return table.create(output(values, namefn, table.groups(), results));
-}
-
-function pivotKeys(table, on, options) {
- const limit = options.limit > 0 ? +options.limit : Infinity;
- const sort = opt(options.sort, true);
- const ksep = opt(options.keySeparator, '_');
-
- // construct key accessor function
- const get = aggregateGet(table, on.ops, on.exprs);
- const key = get.length === 1
- ? get[0]
- : (row, data) => get.map(fn => fn(row, data)).join(ksep);
-
- // generate vector of per-row key values
- const kcol = Array(table.totalRows());
- table.scan((row, data) => kcol[row] = key(row, data));
-
- // collect unique key values
- const uniq = aggregate(
- table.ungroup(),
- [ {
- id: 0,
- name: 'array_agg_distinct',
- fields: [(row => kcol[row])], params: []
- } ]
- )[0][0];
-
- // get ordered set of unique key values
- const keys = sort ? uniq.sort() : uniq;
-
- // return key values
- return {
- keys: Number.isFinite(limit) ? keys.slice(0, limit) : keys,
- keyColumn: kcol
- };
-}
-
-function output({ names, exprs }, namefn, groups, results) {
- const size = groups ? groups.size : 1;
- const cols = columnSet();
- const m = results.length;
- const n = names.length;
-
- let result;
- const op = (id, row) => result[id][row];
-
- // write groupby fields to output
- if (groups) groupOutput(cols, groups);
-
- // write pivot values to output
- for (let i = 0; i < n; ++i) {
- const get = exprs[i];
- if (get.field != null) {
- // if expression is op only, use aggregates directly
- for (let j = 0; j < m; ++j) {
- cols.add(namefn(j, names[i]), results[j][get.field]);
- }
- } else if (size > 1) {
- // if multiple groups, evaluate expression for each
- for (let j = 0; j < m; ++j) {
- result = results[j];
- const col = cols.add(namefn(j, names[i]), Array(size));
- for (let k = 0; k < size; ++k) {
- col[k] = get(k, null, op);
- }
- }
- } else {
- // if only one group, no need to loop
- for (let j = 0; j < m; ++j) {
- result = results[j];
- cols.add(namefn(j, names[i]), [ get(0, null, op) ]);
- }
- }
- }
-
- return cols.new();
-}
\ No newline at end of file
diff --git a/src/engine/rollup.js b/src/engine/rollup.js
deleted file mode 100644
index f439673b..00000000
--- a/src/engine/rollup.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import { aggregate, groupOutput } from './reduce/util';
-import columnSet from '../table/column-set';
-
-export default function(table, { names, exprs, ops }) {
- // output data
- const cols = columnSet();
- const groups = table.groups();
-
- // write groupby fields to output
- if (groups) groupOutput(cols, groups);
-
- // compute and write aggregate output
- output(names, exprs, groups, aggregate(table, ops), cols);
-
- // return output table
- return table.create(cols.new());
-}
-
-function output(names, exprs, groups, result = [], cols) {
- if (!exprs.length) return;
- const size = groups ? groups.size : 1;
- const op = (id, row) => result[id][row];
- const n = names.length;
-
- for (let i = 0; i < n; ++i) {
- const get = exprs[i];
- if (get.field != null) {
- // if expression is op only, use aggregates directly
- cols.add(names[i], result[get.field]);
- } else if (size > 1) {
- // if multiple groups, evaluate expression for each
- const col = cols.add(names[i], Array(size));
- for (let j = 0; j < size; ++j) {
- col[j] = get(j, null, op);
- }
- } else {
- // if only one group, no need to loop
- cols.add(names[i], [ get(0, null, op) ]);
- }
- }
-}
\ No newline at end of file
diff --git a/src/engine/sample.js b/src/engine/sample.js
deleted file mode 100644
index 64f1f590..00000000
--- a/src/engine/sample.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import sample from '../util/sample';
-import _shuffle from '../util/shuffle';
-
-export default function(table, size, weight, options = {}) {
- const { replace, shuffle } = options;
- const parts = table.partitions(false);
-
- let total = 0;
- size = parts.map((idx, group) => {
- let s = size(group);
- total += (s = (replace ? s : Math.min(idx.length, s)));
- return s;
- });
-
- const samples = new Uint32Array(total);
- let curr = 0;
-
- parts.forEach((idx, group) => {
- const sz = size[group];
- const buf = samples.subarray(curr, curr += sz);
-
- if (!replace && sz === idx.length) {
- // sample size === data size, no replacement
- // no need to sample, just copy indices
- buf.set(idx);
- } else {
- sample(buf, replace, idx, weight);
- }
- });
-
- if (shuffle !== false && (parts.length > 1 || !replace)) {
- // sampling with replacement methods shuffle, so in
- // that case a single partition is already good to go
- _shuffle(samples);
- }
-
- return table.reify(samples);
-}
\ No newline at end of file
diff --git a/src/engine/select.js b/src/engine/select.js
deleted file mode 100644
index aa0c5c11..00000000
--- a/src/engine/select.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import columnSet from '../table/column-set';
-import error from '../util/error';
-import isString from '../util/is-string';
-
-export default function(table, columns) {
- const cols = columnSet();
-
- columns.forEach((value, curr) => {
- const next = isString(value) ? value : curr;
- if (next) {
- const col = table.column(curr) || error(`Unrecognized column: ${curr}`);
- cols.add(next, col);
- }
- });
-
- return table.create(cols);
-}
\ No newline at end of file
diff --git a/src/engine/spread.js b/src/engine/spread.js
deleted file mode 100644
index f12d1e78..00000000
--- a/src/engine/spread.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import { aggregateGet } from './reduce/util';
-import columnSet from '../table/column-set';
-import NULL from '../util/null';
-import toArray from '../util/to-array';
-
-export default function(table, { names, exprs, ops = [] }, options = {}) {
- if (names.length === 0) return table;
-
- // ignore 'as' if there are multiple field names
- const as = (names.length === 1 && options.as) || [];
- const drop = options.drop == null ? true : !!options.drop;
- const limit = options.limit == null
- ? as.length || Infinity
- : Math.max(1, +options.limit || 1);
-
- const get = aggregateGet(table, ops, exprs);
- const cols = columnSet();
- const map = names.reduce((map, name, i) => map.set(name, i), new Map());
-
- const add = (index, name) => {
- const columns = spread(table, get[index], limit);
- const n = columns.length;
- for (let i = 0; i < n; ++i) {
- cols.add(as[i] || `${name}_${i + 1}`, columns[i]);
- }
- };
-
- table.columnNames().forEach(name => {
- if (map.has(name)) {
- if (!drop) cols.add(name, table.column(name));
- add(map.get(name), name);
- map.delete(name);
- } else {
- cols.add(name, table.column(name));
- }
- });
-
- map.forEach(add);
-
- return table.create(cols);
-}
-
-function spread(table, get, limit) {
- const nrows = table.totalRows();
- const columns = [];
-
- table.scan((row, data) => {
- const values = toArray(get(row, data));
- const n = Math.min(values.length, limit);
- while (columns.length < n) {
- columns.push(Array(nrows).fill(NULL));
- }
- for (let i = 0; i < n; ++i) {
- columns[i][row] = values[i];
- }
- });
-
- return columns;
-}
\ No newline at end of file
diff --git a/src/engine/unroll.js b/src/engine/unroll.js
deleted file mode 100644
index c8c40845..00000000
--- a/src/engine/unroll.js
+++ /dev/null
@@ -1,117 +0,0 @@
-import { aggregateGet } from './reduce/util';
-import columnSet from '../table/column-set';
-import toArray from '../util/to-array';
-
-export default function(table, { names = [], exprs = [], ops = [] }, options = {}) {
- if (!names.length) return table;
-
- const limit = options.limit > 0 ? +options.limit : Infinity;
- const index = options.index
- ? options.index === true ? 'index' : options.index + ''
- : null;
- const drop = new Set(options.drop);
- const get = aggregateGet(table, ops, exprs);
-
- // initialize output columns
- const cols = columnSet();
- const nset = new Set(names);
- const priors = [];
- const copies = [];
- const unroll = [];
-
- // original and copied columns
- table.columnNames().forEach(name => {
- if (!drop.has(name)) {
- const col = cols.add(name, []);
- if (!nset.has(name)) {
- priors.push(table.column(name));
- copies.push(col);
- }
- }
- });
-
- // unrolled output columns
- names.forEach(name => {
- if (!drop.has(name)) {
- if (!cols.has(name)) cols.add(name, []);
- unroll.push(cols.data[name]);
- }
- });
-
- // index column, if requested
- const icol = index ? cols.add(index, []) : null;
-
- let start = 0;
- const m = priors.length;
- const n = unroll.length;
-
- const copy = (row, maxlen) => {
- for (let i = 0; i < m; ++i) {
- copies[i].length = start + maxlen;
- copies[i].fill(priors[i].get(row), start, start + maxlen);
- }
- };
-
- const indices = icol
- ? (row, maxlen) => {
- for (let i = 0; i < maxlen; ++i) {
- icol[row + i] = i;
- }
- }
- : () => {};
-
- if (n === 1) {
- // optimize common case of one array-valued column
- const fn = get[0];
- const col = unroll[0];
-
- table.scan((row, data) => {
- // extract array data
- const array = toArray(fn(row, data));
- const maxlen = Math.min(array.length, limit);
-
- // copy original table data
- copy(row, maxlen);
-
- // copy unrolled array data
- for (let j = 0; j < maxlen; ++j) {
- col[start + j] = array[j];
- }
-
- // fill in array indices
- indices(start, maxlen);
-
- start += maxlen;
- });
- } else {
- table.scan((row, data) => {
- let maxlen = 0;
-
- // extract parallel array data
- const arrays = get.map(fn => {
- const value = toArray(fn(row, data));
- maxlen = Math.min(Math.max(maxlen, value.length), limit);
- return value;
- });
-
- // copy original table data
- copy(row, maxlen);
-
- // copy unrolled array data
- for (let i = 0; i < n; ++i) {
- const col = unroll[i];
- const arr = arrays[i];
- for (let j = 0; j < maxlen; ++j) {
- col[start + j] = arr[j];
- }
- }
-
- // fill in array indices
- indices(start, maxlen);
-
- start += maxlen;
- });
- }
-
- return table.create(cols.new());
-}
\ No newline at end of file
diff --git a/src/expression/ast/clean.js b/src/expression/ast/clean.js
index 0b41c31c..f28f1065 100644
--- a/src/expression/ast/clean.js
+++ b/src/expression/ast/clean.js
@@ -1,4 +1,4 @@
-import walk from './walk';
+import walk from './walk.js';
function strip(node) {
delete node.start;
@@ -21,4 +21,4 @@ export default function(ast) {
Default: strip
});
return ast;
-}
\ No newline at end of file
+}
diff --git a/src/expression/ast/constants.js b/src/expression/ast/constants.js
index b2c8eadf..35105b7c 100644
--- a/src/expression/ast/constants.js
+++ b/src/expression/ast/constants.js
@@ -13,4 +13,4 @@ export const Constant = 'Constant';
export const Dictionary = 'Dictionary';
export const Function = 'Function';
export const Parameter = 'Parameter';
-export const Op = 'Op';
\ No newline at end of file
+export const Op = 'Op';
diff --git a/src/expression/ast/util.js b/src/expression/ast/util.js
index fc6b4f72..a94aad6f 100644
--- a/src/expression/ast/util.js
+++ b/src/expression/ast/util.js
@@ -1,4 +1,4 @@
-import { ArrowFunctionExpression, FunctionExpression } from './constants';
+import { ArrowFunctionExpression, FunctionExpression } from './constants.js';
export function is(type, node) {
return node && node.type === type;
@@ -7,4 +7,4 @@ export function is(type, node) {
export function isFunctionExpression(node) {
return is(FunctionExpression, node)
|| is(ArrowFunctionExpression, node);
-}
\ No newline at end of file
+}
diff --git a/src/expression/ast/walk.js b/src/expression/ast/walk.js
index 198768ee..8a3428d2 100644
--- a/src/expression/ast/walk.js
+++ b/src/expression/ast/walk.js
@@ -113,4 +113,4 @@ const walkers = {
Program: (node, ctx, visitors) => {
walk(node.body[0], ctx, visitors, node);
}
-};
\ No newline at end of file
+};
diff --git a/src/expression/codegen.js b/src/expression/codegen.js
index 9121ebbc..0c16ee45 100644
--- a/src/expression/codegen.js
+++ b/src/expression/codegen.js
@@ -1,5 +1,5 @@
-import error from '../util/error';
-import toString from '../util/to-string';
+import error from '../util/error.js';
+import toString from '../util/to-string.js';
const visit = (node, opt) => {
const f = visitors[node.type];
@@ -33,9 +33,14 @@ const ref = (node, opt, method) => {
return `data${table}${name(node)}.${method}(${opt.index}${table})`;
};
+const get = (node, opt) => {
+ const table = node.table || '';
+ return `data${table}${name(node)}[${opt.index}${table}]`;
+};
+
const visitors = {
Constant: node => node.raw,
- Column: (node, opt) => ref(node, opt, 'get'),
+ Column: (node, opt) => node.array ? get(node, opt) : ref(node, opt, 'at'),
Dictionary: (node, opt) => ref(node, opt, 'key'),
Function: node => `fn.${node.name}`,
Parameter: node => `$${name(node)}`,
@@ -135,4 +140,4 @@ const visitors = {
export default function(node, opt = { index: 'row' }) {
return visit(node, opt);
-}
\ No newline at end of file
+}
diff --git a/src/expression/compare.js b/src/expression/compare.js
index 30c349c9..399bd6a7 100644
--- a/src/expression/compare.js
+++ b/src/expression/compare.js
@@ -1,6 +1,6 @@
-import codegen from './codegen';
-import parse from './parse';
-import { aggregate } from '../engine/reduce/util';
+import codegen from './codegen.js';
+import parse from './parse.js';
+import { aggregate } from '../verbs/reduce/util.js';
// generate code to compare a single field
const _compare = (u, v, lt, gt) =>
@@ -58,4 +58,4 @@ export default function(table, fields) {
// instantiate and return comparator function
return Function('op', 'keys', 'fn', 'data', code)(op, keys, fn, table.data());
-}
\ No newline at end of file
+}
diff --git a/src/expression/compile.js b/src/expression/compile.js
index 2bb767fb..4c36a5c5 100644
--- a/src/expression/compile.js
+++ b/src/expression/compile.js
@@ -1,4 +1,4 @@
-import { functions as fn } from '../op';
+import { functions as fn } from '../op/index.js';
function compile(code, fn, params) {
code = `"use strict"; return ${code};`;
@@ -11,4 +11,4 @@ export default {
expr2: (code, params) => compile(`(row0,data0,row,data)=>${code}`, fn, params),
join: (code, params) => compile(`(row1,data1,row2,data2)=>${code}`, fn, params),
param: (code, params) => compile(code, fn, params)
-};
\ No newline at end of file
+};
diff --git a/src/expression/constants.js b/src/expression/constants.js
index 0aff57cb..d56dda9c 100644
--- a/src/expression/constants.js
+++ b/src/expression/constants.js
@@ -10,4 +10,4 @@ export default {
PI: 'Math.PI',
SQRT1_2: 'Math.SQRT1_2',
SQRT2: 'Math.SQRT2'
-};
\ No newline at end of file
+};
diff --git a/src/expression/parse-escape.js b/src/expression/parse-escape.js
index e483b4ec..22233c93 100644
--- a/src/expression/parse-escape.js
+++ b/src/expression/parse-escape.js
@@ -1,7 +1,7 @@
-import compile from './compile';
-import { rowObjectCode } from './row-object';
-import error from '../util/error';
-import toFunction from '../util/to-function';
+import compile from './compile.js';
+import { rowObjectCode } from './row-object.js';
+import error from '../util/error.js';
+import toFunction from '../util/to-function.js';
const ERROR_ESC_AGGRONLY = 'Escaped functions are not valid as rollup or pivot values.';
@@ -9,9 +9,7 @@ export default function(ctx, spec, params) {
if (ctx.aggronly) error(ERROR_ESC_AGGRONLY);
// generate escaped function invocation code
- const code = '(row,data)=>fn('
- + rowObjectCode(ctx.table.columnNames())
- + ',$)';
+ const code = `(row,data)=>fn(${rowObjectCode(ctx.table)},$)`;
return { escape: compile.escape(code, toFunction(spec.expr), params) };
-}
\ No newline at end of file
+}
diff --git a/src/expression/parse-expression.js b/src/expression/parse-expression.js
index 1805df38..71d196f3 100644
--- a/src/expression/parse-expression.js
+++ b/src/expression/parse-expression.js
@@ -10,22 +10,22 @@ import {
Op,
Parameter,
Property
-} from './ast/constants';
-import { is, isFunctionExpression } from './ast/util';
-import walk from './ast/walk';
-import constants from './constants';
-import rewrite from './rewrite';
-import { ROW_OBJECT, rowObjectExpression } from './row-object';
+} from './ast/constants.js';
+import { is, isFunctionExpression } from './ast/util.js';
+import walk from './ast/walk.js';
+import constants from './constants.js';
+import rewrite from './rewrite.js';
+import { ROW_OBJECT, rowObjectExpression } from './row-object.js';
import {
getAggregate, getWindow,
hasAggregate, hasFunction, hasWindow
-} from '../op';
+} from '../op/index.js';
-import error from '../util/error';
-import has from '../util/has';
-import isArray from '../util/is-array';
-import isNumber from '../util/is-number';
-import toString from '../util/to-string';
+import error from '../util/error.js';
+import has from '../util/has.js';
+import isArray from '../util/is-array.js';
+import isNumber from '../util/is-number.js';
+import toString from '../util/to-string.js';
const PARSER_OPT = { ecmaVersion: 11 };
const DEFAULT_PARAM_ID = '$';
@@ -93,9 +93,10 @@ function parseAST(expr) {
const code = expr.field ? fieldRef(expr)
: isArray(expr) ? toString(expr)
: expr;
+ // @ts-ignore
return parse(`expr=(${code})`, PARSER_OPT).body[0].expression.right;
- } catch (err) {
- error(`Expression parse error: ${expr+''}`, err);
+ } catch (err) { // eslint-disable-line no-unused-vars
+ error(`Expression parse error: ${expr+''}`);
}
}
@@ -378,7 +379,7 @@ function updateFunctionNode(node, name, ctx) {
if (name === ROW_OBJECT) {
const t = ctx.table;
if (!t) ctx.error(node, ERROR_ROW_OBJECT);
- rowObjectExpression(node,
+ rowObjectExpression(node, t,
node.arguments.length
? node.arguments.map(node => {
const col = ctx.param(node);
@@ -403,4 +404,4 @@ function handleDeclaration(node, ctx) {
} else {
ctx.error(node.id, ERROR_DECLARATION);
}
-}
\ No newline at end of file
+}
diff --git a/src/expression/parse.js b/src/expression/parse.js
index fecf5afa..9fd73462 100644
--- a/src/expression/parse.js
+++ b/src/expression/parse.js
@@ -1,14 +1,14 @@
-import { Column, Literal, Op } from './ast/constants';
-import clean from './ast/clean';
-import { is } from './ast/util';
-import codegen from './codegen';
-import compile from './compile';
-import entries from '../util/entries';
-import error from '../util/error';
-import isFunction from '../util/is-function';
-import isObject from '../util/is-object';
-import parseEscape from './parse-escape';
-import parseExpression from './parse-expression';
+import { Column, Literal, Op } from './ast/constants.js';
+import clean from './ast/clean.js';
+import { is } from './ast/util.js';
+import codegen from './codegen.js';
+import compile from './compile.js';
+import entries from '../util/entries.js';
+import error from '../util/error.js';
+import isFunction from '../util/is-function.js';
+import isObject from '../util/is-object.js';
+import parseEscape from './parse-escape.js';
+import parseExpression from './parse-expression.js';
const ANNOTATE = { [Column]: 1, [Op]: 1 };
@@ -115,4 +115,4 @@ function getParams(opt) {
function getTableParams(table) {
return table && isFunction(table.params) ? table.params() : {};
-}
\ No newline at end of file
+}
diff --git a/src/expression/rewrite.js b/src/expression/rewrite.js
index f9046cbb..87d63c33 100644
--- a/src/expression/rewrite.js
+++ b/src/expression/rewrite.js
@@ -1,5 +1,6 @@
-import { Column, Dictionary, Literal } from './ast/constants';
-import isFunction from '../util/is-function';
+import { Column, Dictionary, Literal } from './ast/constants.js';
+import isArrayType from '../util/is-array-type.js';
+import isFunction from '../util/is-function.js';
const dictOps = {
'==': 1,
@@ -13,15 +14,20 @@ const dictOps = {
* Additionally optimizes dictionary column operations.
* @param {object} ref AST node to rewrite to a column reference.
* @param {string} name The name of the column.
- * @param {number} index The table index of the column.
- * @param {object} col The actual table column instance.
- * @param {object} op Parent AST node operating on the column reference.
+ * @param {number} [index] The table index of the column.
+ * @param {object} [col] The actual table column instance.
+ * @param {object} [op] Parent AST node operating on the column reference.
*/
-export default function(ref, name, index = 0, col, op) {
+export default function(ref, name, index = 0, col = undefined, op = undefined) {
ref.type = Column;
ref.name = name;
ref.table = index;
+ // annotate arrays as such for optimized access
+ if (isArrayType(col)) {
+ ref.array = true;
+ }
+
// proceed only if has parent op and is a dictionary column
if (op && col && isFunction(col.keyFor)) {
// get other arg if op is an optimizeable operation
@@ -56,4 +62,4 @@ function rewriteDictionary(op, ref, lit, key) {
}
return true;
-}
\ No newline at end of file
+}
diff --git a/src/expression/row-object.js b/src/expression/row-object.js
index 6071328e..b12d0ff7 100644
--- a/src/expression/row-object.js
+++ b/src/expression/row-object.js
@@ -1,14 +1,18 @@
-import { Literal, ObjectExpression, Property } from './ast/constants';
-import codegen from './codegen';
-import compile from './compile';
-import rewrite from './rewrite';
-import entries from '../util/entries';
-import isArray from '../util/is-array';
-import toString from '../util/to-string';
+import { Literal, ObjectExpression, Property } from './ast/constants.js';
+import codegen from './codegen.js';
+import compile from './compile.js';
+import rewrite from './rewrite.js';
+import entries from '../util/entries.js';
+import isArray from '../util/is-array.js';
+import toString from '../util/to-string.js';
export const ROW_OBJECT = 'row_object';
-export function rowObjectExpression(node, props) {
+export function rowObjectExpression(
+ node,
+ table,
+ props = table.columnNames())
+{
node.type = ObjectExpression;
const p = node.properties = [];
@@ -17,17 +21,17 @@ export function rowObjectExpression(node, props) {
p.push({
type: Property,
key: { type: Literal, raw: toString(key) },
- value: rewrite({ computed: true }, name)
+ value: rewrite({ computed: true }, name, 0, table.column(name))
});
}
return node;
}
-export function rowObjectCode(props) {
- return codegen(rowObjectExpression({}, props));
+export function rowObjectCode(table, props) {
+ return codegen(rowObjectExpression({}, table, props));
}
-export function rowObjectBuilder(props) {
- return compile.expr(rowObjectCode(props));
-}
\ No newline at end of file
+export function rowObjectBuilder(table, props) {
+ return compile.expr(rowObjectCode(table, props));
+}
diff --git a/src/format/from-arrow.js b/src/format/from-arrow.js
deleted file mode 100644
index 0a35d53b..00000000
--- a/src/format/from-arrow.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import { fromIPC } from '../arrow/arrow-table';
-import arrowColumn from '../arrow/arrow-column';
-import resolve, { all } from '../helpers/selection';
-import columnSet from '../table/column-set';
-import ColumnTable from '../table/column-table';
-
-/**
- * Options for Apache Arrow import.
- * @typedef {object} ArrowOptions
- * @property {import('../table/transformable').Select} columns
- * An ordered set of columns to import. The input may consist of column name
- * strings, column integer indices, objects with current column names as keys
- * and new column names as values (for renaming), or selection helper
- * functions such as {@link all}, {@link not}, or {@link range}.
- */
-
-/**
- * Create a new table backed by an Apache Arrow table instance.
- * @param {object} arrow An Apache Arrow data table or byte buffer.
- * @param {ArrowOptions} options Options for Arrow import.
- * @return {ColumnTable} A new table containing the imported values.
- */
-export default function(arrow, options = {}) {
- if (arrow && !arrow.batches) {
- arrow = fromIPC()(arrow);
- }
-
- // resolve column selection
- const fields = arrow.schema.fields.map(f => f.name);
- const sel = resolve({
- columnNames: test => test ? fields.filter(test) : fields.slice(),
- columnIndex: name => fields.indexOf(name)
- }, options.columns || all());
-
- // build Arquero columns for backing Arrow columns
- const cols = columnSet();
- sel.forEach((name, key) => {
- cols.add(name, arrowColumn(arrow.getChild(key)));
- });
-
- return new ColumnTable(cols.data, cols.names);
-}
\ No newline at end of file
diff --git a/src/format/from-csv.js b/src/format/from-csv.js
index 490eae96..044fd1eb 100644
--- a/src/format/from-csv.js
+++ b/src/format/from-csv.js
@@ -1,7 +1,5 @@
-import ColumnTable from '../table/column-table'; // eslint-disable-line no-unused-vars
-
-import fromTextRows from './from-text-rows';
-import parseDelimited from './parse/parse-delimited';
+import fromTextRows from './from-text-rows.js';
+import parseDelimited from './parse/parse-delimited.js';
/**
* Options for CSV parsing.
@@ -34,8 +32,9 @@ import parseDelimited from './parse/parse-delimited';
* behavior, set the autoType option to false. To perform custom parsing
* of input column values, use the parse option.
* @param {string} text A string in a delimited-value format.
- * @param {CSVParseOptions} options The formatting options.
- * @return {ColumnTable} A new table containing the parsed values.
+ * @param {CSVParseOptions} [options] The formatting options.
+ * @return {import('../table/ColumnTable.js').ColumnTable} A new table
+ * containing the parsed values.
*/
export default function(text, options = {}) {
const next = parseDelimited(text, options);
@@ -44,4 +43,4 @@ export default function(text, options = {}) {
options.header !== false ? next() : options.names,
options
);
-}
\ No newline at end of file
+}
diff --git a/src/format/from-fixed.js b/src/format/from-fixed.js
index 958c8029..ccfecacb 100644
--- a/src/format/from-fixed.js
+++ b/src/format/from-fixed.js
@@ -1,8 +1,6 @@
-import ColumnTable from '../table/column-table'; // eslint-disable-line no-unused-vars
-
-import fromTextRows from './from-text-rows';
-import parseLines from './parse/parse-lines';
-import error from '../util/error';
+import fromTextRows from './from-text-rows.js';
+import parseLines from './parse/parse-lines.js';
+import error from '../util/error.js';
/**
* Options for fixed width file parsing.
@@ -34,7 +32,8 @@ import error from '../util/error';
* parsing of input column values, use the parse option.
* @param {string} text A string in a fixed-width file format.
* @param {FixedParseOptions} options The formatting options.
- * @return {ColumnTable} A new table containing the parsed values.
+ * @return {import('../table/ColumnTable.js').ColumnTable} A new table
+ * containing the parsed values.
*/
export default function(text, options = {}) {
const read = parseLines(text, options);
@@ -51,10 +50,10 @@ export default function(text, options = {}) {
);
}
-function positions({ positions, widths }) {
+function positions({ positions = undefined, widths = undefined }) {
if (!positions && !widths) {
error('Fixed width files require a "positions" or "widths" option');
}
let i = 0;
return positions || widths.map(w => [i, i += w]);
-}
\ No newline at end of file
+}
diff --git a/src/format/from-json.js b/src/format/from-json.js
index a3441ba3..75279588 100644
--- a/src/format/from-json.js
+++ b/src/format/from-json.js
@@ -1,10 +1,10 @@
-import ColumnTable from '../table/column-table';
-import defaultTrue from '../util/default-true';
-import isArrayType from '../util/is-array-type';
-import isDigitString from '../util/is-digit-string';
-import isISODateString from '../util/is-iso-date-string';
-import isObject from '../util/is-object';
-import isString from '../util/is-string';
+import { ColumnTable } from '../table/ColumnTable.js';
+import defaultTrue from '../util/default-true.js';
+import isArrayType from '../util/is-array-type.js';
+import isDigitString from '../util/is-digit-string.js';
+import isISODateString from '../util/is-iso-date-string.js';
+import isObject from '../util/is-object.js';
+import isString from '../util/is-string.js';
/**
* Options for JSON parsing.
@@ -27,7 +27,7 @@ import isString from '../util/is-string';
* The data payload can also be provided as the "data" property of an
* enclosing object, with an optional "schema" property containing table
* metadata such as a "fields" array of ordered column information.
- * @param {string|object} data A string in JSON format, or pre-parsed object.
+ * @param {string|object} json A string in JSON format, or pre-parsed object.
* @param {JSONParseOptions} options The formatting options.
* @return {ColumnTable} A new table containing the parsed values.
*/
@@ -73,4 +73,4 @@ export default function(json, options = {}) {
}
return new ColumnTable(data, names);
-}
\ No newline at end of file
+}
diff --git a/src/format/from-text-rows.js b/src/format/from-text-rows.js
index 6ea4bae5..bdcda246 100644
--- a/src/format/from-text-rows.js
+++ b/src/format/from-text-rows.js
@@ -1,8 +1,8 @@
-import ColumnTable from '../table/column-table';
-import identity from '../util/identity';
-import isFunction from '../util/is-function';
-import repeat from '../util/repeat';
-import valueParser from '../util/parse-values';
+import { ColumnTable } from '../table/ColumnTable.js';
+import identity from '../util/identity.js';
+import isFunction from '../util/is-function.js';
+import repeat from '../util/repeat.js';
+import valueParser from '../util/parse-values.js';
function defaultNames(n, off = 0) {
return repeat(n - off, i => `col${i + off + 1}`);
@@ -44,6 +44,7 @@ export default function(next, names, options) {
}
}
+ /** @type {import('../table/types.js').ColumnData} */
const columns = {};
names.forEach((name, i) => columns[name] = values[i]);
return new ColumnTable(columns, names);
@@ -58,4 +59,4 @@ function getParsers(names, values, options) {
: noParse ? identity
: valueParser(values[i], options)
);
-}
\ No newline at end of file
+}
diff --git a/src/format/infer.js b/src/format/infer.js
index 2492e45c..be203e98 100644
--- a/src/format/infer.js
+++ b/src/format/infer.js
@@ -1,4 +1,4 @@
-import isDate from '../util/is-date';
+import isDate from '../util/is-date.js';
function isExactDateUTC(d) {
return d.getUTCHours() === 0
@@ -47,4 +47,4 @@ export default function(scan, options = {}) {
digits: Math.min(digits, options.maxdigits || 6)
}
};
-}
\ No newline at end of file
+}
diff --git a/src/format/load-file.js b/src/format/load-file.js
index 8341c211..db5975a0 100644
--- a/src/format/load-file.js
+++ b/src/format/load-file.js
@@ -1,14 +1,15 @@
-import ColumnTable from '../table/column-table'; // eslint-disable-line no-unused-vars
-
-import fromArrow from './from-arrow';
-import fromCSV from './from-csv';
-import fromFixed from './from-fixed';
-import fromJSON from './from-json';
-import { from } from '../table';
-import isArray from '../util/is-array';
-
import fetch from 'node-fetch';
import { readFile } from 'fs';
+import fromCSV from './from-csv.js';
+import fromFixed from './from-fixed.js';
+import fromJSON from './from-json.js';
+import fromArrow from '../arrow/from-arrow.js';
+import { from } from '../table/index.js';
+import isArray from '../util/is-array.js';
+
+/**
+ * @typedef {import('../table/ColumnTable.js').ColumnTable} ColumnTable
+ */
/**
* Options for file loading.
@@ -28,7 +29,7 @@ import { readFile } from 'fs';
* otherwise CSV format is assumed. The options to this method are
* passed as the second argument to the format parser.
* @param {string} path The URL or file path to load.
- * @param {LoadOptions & object} options The loading and formatting options.
+ * @param {LoadOptions & object} [options] The loading and formatting options.
* @return {Promise} A Promise for an Arquero table.
* @example aq.load('data/table.csv')
* @example aq.load('data/table.json', { using: aq.fromJSON })
@@ -66,7 +67,8 @@ function loadFile(file, options, parse) {
/**
* Load an Arrow file from a URL and return a Promise for an Arquero table.
* @param {string} path The URL or file path to load.
- * @param {LoadOptions & import('./from-arrow').ArrowOptions} options Arrow format options.
+ * @param {LoadOptions & import('../arrow/types.js').ArrowOptions} [options]
+ * Arrow format options.
* @return {Promise} A Promise for an Arquero table.
* @example aq.loadArrow('data/table.arrow')
*/
@@ -77,7 +79,8 @@ export function loadArrow(path, options) {
/**
* Load a CSV file from a URL and return a Promise for an Arquero table.
* @param {string} path The URL or file path to load.
- * @param {LoadOptions & import('./from-csv').CSVParseOptions} options CSV format options.
+ * @param {LoadOptions & import('./from-csv.js').CSVParseOptions} [options]
+ * CSV format options.
* @return {Promise} A Promise for an Arquero table.
* @example aq.loadCSV('data/table.csv')
* @example aq.loadTSV('data/table.tsv', { delimiter: '\t' })
@@ -89,7 +92,8 @@ export function loadCSV(path, options) {
/**
* Load a fixed width file from a URL and return a Promise for an Arquero table.
* @param {string} path The URL or file path to load.
- * @param {LoadOptions & import('./from-fixed').FixedParseOptions} options Fixed width format options.
+ * @param {LoadOptions & import('./from-fixed.js').FixedParseOptions} [options]
+ * Fixed width format options.
* @return {Promise} A Promise for an Arquero table.
* @example aq.loadFixedWidth('data/table.txt', { names: ['name', 'city', state'], widths: [10, 20, 2] })
*/
@@ -103,7 +107,8 @@ export function loadCSV(path, options) {
* and the aq.from method is used to construct the table. Otherwise, a
* column object format is assumed and aq.fromJSON is applied.
* @param {string} path The URL or file path to load.
- * @param {LoadOptions & import('./from-json').JSONParseOptions} options JSON format options.
+ * @param {LoadOptions & import('./from-json.js').JSONParseOptions} [options]
+ * JSON format options.
* @return {Promise} A Promise for an Arquero table.
* @example aq.loadJSON('data/table.json')
*/
@@ -113,4 +118,4 @@ export function loadJSON(path, options) {
function parseJSON(data, options) {
return isArray(data) ? from(data) : fromJSON(data, options);
-}
\ No newline at end of file
+}
diff --git a/src/format/load-url.js b/src/format/load-url.js
index e2054e5b..aded0fd4 100644
--- a/src/format/load-url.js
+++ b/src/format/load-url.js
@@ -1,11 +1,13 @@
-import ColumnTable from '../table/column-table'; // eslint-disable-line no-unused-vars
+import fromArrow from '../arrow/from-arrow.js';
+import fromCSV from './from-csv.js';
+import fromFixed from './from-fixed.js';
+import fromJSON from './from-json.js';
+import { from } from '../table/index.js';
+import isArray from '../util/is-array.js';
-import fromArrow from './from-arrow';
-import fromCSV from './from-csv';
-import fromFixed from './from-fixed';
-import fromJSON from './from-json';
-import { from } from '../table';
-import isArray from '../util/is-array';
+/**
+ * @typedef {import('../table/ColumnTable.js').ColumnTable} ColumnTable
+ */
/**
* Options for file loading.
@@ -41,7 +43,8 @@ export function load(url, options = {}) {
/**
* Load an Arrow file from a URL and return a Promise for an Arquero table.
* @param {string} url The URL to load.
- * @param {LoadOptions & import('./from-arrow').ArrowOptions} options Arrow format options.
+ * @param {LoadOptions & import('../arrow/types.js').ArrowOptions} [options]
+ * Arrow format options.
* @return {Promise} A Promise for an Arquero table.
* @example aq.loadArrow('data/table.arrow')
*/
@@ -52,7 +55,8 @@ export function loadArrow(url, options) {
/**
* Load a CSV file from a URL and return a Promise for an Arquero table.
* @param {string} url The URL to load.
- * @param {LoadOptions & import('./from-csv').CSVParseOptions} options CSV format options.
+ * @param {LoadOptions & import('./from-csv.js').CSVParseOptions} [options]
+ * CSV format options.
* @return {Promise} A Promise for an Arquero table.
* @example aq.loadCSV('data/table.csv')
* @example aq.loadTSV('data/table.tsv', { delimiter: '\t' })
@@ -64,7 +68,8 @@ export function loadCSV(url, options) {
/**
* Load a fixed width file from a URL and return a Promise for an Arquero table.
* @param {string} url The URL to load.
- * @param {LoadOptions & import('./from-fixed').FixedParseOptions} options Fixed width format options.
+ * @param {LoadOptions & import('./from-fixed.js').FixedParseOptions} [options]
+ * Fixed width format options.
* @return {Promise} A Promise for an Arquero table.
* @example aq.loadFixedWidth('data/table.txt', { names: ['name', 'city', state'], widths: [10, 20, 2] })
*/
@@ -78,7 +83,8 @@ export function loadCSV(url, options) {
* and the aq.from method is used to construct the table. Otherwise, a
* column object format is assumed and aq.fromJSON is applied.
* @param {string} url The URL to load.
- * @param {LoadOptions & import('./from-json').JSONParseOptions} options JSON format options.
+ * @param {LoadOptions & import('./from-json.js').JSONParseOptions} [options]
+ * JSON format options.
* @return {Promise} A Promise for an Arquero table.
* @example aq.loadJSON('data/table.json')
*/
@@ -88,4 +94,4 @@ export function loadJSON(url, options) {
function parseJSON(data, options) {
return isArray(data) ? from(data) : fromJSON(data, options);
-}
\ No newline at end of file
+}
diff --git a/src/format/parse/constants.js b/src/format/parse/constants.js
index 303d7aaa..9c5bfc4d 100644
--- a/src/format/parse/constants.js
+++ b/src/format/parse/constants.js
@@ -2,4 +2,4 @@ export const EOL = {};
export const EOF = {};
export const QUOTE = 34;
export const NEWLINE = 10;
-export const RETURN = 13;
\ No newline at end of file
+export const RETURN = 13;
diff --git a/src/format/parse/parse-delimited.js b/src/format/parse/parse-delimited.js
index 340b59e2..ae622841 100644
--- a/src/format/parse/parse-delimited.js
+++ b/src/format/parse/parse-delimited.js
@@ -1,6 +1,6 @@
-import { EOF, EOL, NEWLINE, QUOTE, RETURN } from './constants';
-import filter from './text-filter';
-import error from '../../util/error';
+import { EOF, EOL, NEWLINE, QUOTE, RETURN } from './constants.js';
+import filter from './text-filter.js';
+import error from '../../util/error.js';
// Adapted from d3-dsv: https://github.com/d3/d3-dsv/blob/master/src/dsv.js
// Copyright 2013-2016 Mike Bostock
@@ -26,7 +26,11 @@ import error from '../../util/error';
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-export default function(text, { delimiter = ',', skip, comment }) {
+export default function(text, {
+ delimiter = ',',
+ skip = 0,
+ comment = undefined
+}) {
if (delimiter.length !== 1) {
error(`Text "delimiter" should be a single character, found "${delimiter}"`);
}
@@ -81,4 +85,4 @@ export default function(text, { delimiter = ',', skip, comment }) {
next, skip,
comment && (x => (x && x[0] || '').startsWith(comment))
);
-}
\ No newline at end of file
+}
diff --git a/src/format/parse/parse-lines.js b/src/format/parse/parse-lines.js
index 7764c608..4a1d2eca 100644
--- a/src/format/parse/parse-lines.js
+++ b/src/format/parse/parse-lines.js
@@ -1,7 +1,7 @@
-import { NEWLINE, RETURN } from './constants';
-import filter from './text-filter';
+import { NEWLINE, RETURN } from './constants.js';
+import filter from './text-filter.js';
-export default function(text, { skip, comment }) {
+export default function(text, { skip = 0, comment = undefined }) {
let N = text.length;
let I = 0; // current character index
@@ -31,4 +31,4 @@ export default function(text, { skip, comment }) {
read, skip,
comment && (x => (x || '').startsWith(comment))
);
-}
\ No newline at end of file
+}
diff --git a/src/format/to-arrow.js b/src/format/to-arrow.js
deleted file mode 100644
index 875a3827..00000000
--- a/src/format/to-arrow.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import toArrow from '../arrow/encode';
-import { tableToIPC } from 'apache-arrow';
-
-export default toArrow;
-
-export function toArrowIPC(table, options = {}) {
- const { format: format, ...toArrowOptions } = options;
- const outputFormat = format ? format : 'stream';
- if (!['stream', 'file'].includes(outputFormat)) {
- throw Error('Unrecognised output format');
- }
- return tableToIPC(toArrow(table, toArrowOptions), format);
-}
diff --git a/src/format/to-csv.js b/src/format/to-csv.js
index 3948be74..db7ff675 100644
--- a/src/format/to-csv.js
+++ b/src/format/to-csv.js
@@ -1,8 +1,6 @@
-import ColumnTable from '../table/column-table'; // eslint-disable-line no-unused-vars
-
-import { columns, scan } from './util';
-import { formatUTCDate } from '../util/format-date';
-import isDate from '../util/is-date';
+import { columns, scan } from './util.js';
+import { formatUTCDate } from '../util/format-date.js';
+import isDate from '../util/is-date.js';
/**
* Options for CSV formatting.
@@ -10,7 +8,7 @@ import isDate from '../util/is-date';
* @property {string} [delimiter=','] The delimiter between values.
* @property {number} [limit=Infinity] The maximum number of rows to print.
* @property {number} [offset=0] The row offset indicating how many initial rows to skip.
- * @property {import('./util').ColumnSelectOptions} [columns] Ordered list
+ * @property {import('./util.js').ColumnSelectOptions} [columns] Ordered list
* of column names to include. If function-valued, the function should
* accept a table as input and return an array of column name strings.
* @property {Object. any>} [format] Object of column
@@ -23,7 +21,7 @@ import isDate from '../util/is-date';
* Format a table as a comma-separated values (CSV) string. Other
* delimiters, such as tabs or pipes ('|'), can be specified using
* the options argument.
- * @param {ColumnTable} table The table to format.
+ * @param {import('../table/Table.js').Table} table The table to format.
* @param {CSVFormatOptions} options The formatting options.
* @return {string} A delimited-value format string.
*/
@@ -51,4 +49,4 @@ export default function(table, options = {}) {
});
return text + vals.join(delim);
-}
\ No newline at end of file
+}
diff --git a/src/format/to-html.js b/src/format/to-html.js
index 82fd1c8f..54b7b264 100644
--- a/src/format/to-html.js
+++ b/src/format/to-html.js
@@ -1,9 +1,7 @@
-import ColumnTable from '../table/column-table'; // eslint-disable-line no-unused-vars
-
-import formatValue from './value';
-import { columns, formats, scan } from './util';
-import isFunction from '../util/is-function';
-import mapObject from '../util/map-object';
+import formatValue from './value.js';
+import { columns, formats, scan } from './util.js';
+import isFunction from '../util/is-function.js';
+import mapObject from '../util/map-object.js';
/**
* Null format function.
@@ -30,14 +28,14 @@ import mapObject from '../util/map-object';
* @typedef {object} HTMLFormatOptions
* @property {number} [limit=Infinity] The maximum number of rows to print.
* @property {number} [offset=0] The row offset indicating how many initial rows to skip.
- * @property {import('./util').ColumnSelectOptions} [columns] Ordered list
+ * @property {import('./util.js').ColumnSelectOptions} [columns] Ordered list
* of column names to include. If function-valued, the function should
* accept a table as input and return an array of column name strings.
- * @property {import('./util').ColumnAlignOptions} [align] Object of column
+ * @property {import('./util.js').ColumnAlignOptions} [align] Object of column
* alignment options. The object keys should be column names. The object
* values should be aligment strings, one of 'l' (left), 'c' (center), or
* 'r' (right). If specified, these override automatically inferred options.
- * @property {import('./util').ColumnFormatOptions} [format] Object of column
+ * @property {import('./util.js').ColumnFormatOptions} [format] Object of column
* format options. The object keys should be column names. The object values
* should be formatting functions or specification objects. If specified,
* these override automatically inferred options.
@@ -57,7 +55,7 @@ import mapObject from '../util/map-object';
/**
* Format a table as an HTML table string.
- * @param {ColumnTable} table The table to format.
+ * @param {import('../table/Table.js').Table} table The table to format.
* @param {HTMLFormatOptions} options The formatting options.
* @return {string} An HTML table string.
*/
@@ -113,4 +111,4 @@ function styles(options) {
options.style,
value => isFunction(value) ? value : () => value
);
-}
\ No newline at end of file
+}
diff --git a/src/format/to-json.js b/src/format/to-json.js
index 09283012..e9e51075 100644
--- a/src/format/to-json.js
+++ b/src/format/to-json.js
@@ -1,9 +1,7 @@
-import ColumnTable from '../table/column-table'; // eslint-disable-line no-unused-vars
-
-import { columns } from './util';
-import { formatUTCDate } from '../util/format-date';
-import defaultTrue from '../util/default-true';
-import isDate from '../util/is-date';
+import { columns } from './util.js';
+import { formatUTCDate } from '../util/format-date.js';
+import defaultTrue from '../util/default-true.js';
+import isDate from '../util/is-date.js';
/**
* Options for JSON formatting.
@@ -14,7 +12,7 @@ import isDate from '../util/is-date';
* @property {boolean} [schema=true] Flag indicating if table schema metadata
* should be included in the JSON output. If false, only the data payload
* is included.
- * @property {import('./util').ColumnSelectOptions} [columns] Ordered list
+ * @property {import('./util.js').ColumnSelectOptions} [columns] Ordered list
* of column names to include. If function-valued, the function should
* accept a table as input and return an array of column name strings.
* @property {Object. any>} [format] Object of column
@@ -29,7 +27,7 @@ const defaultFormatter = value => isDate(value)
/**
* Format a table as a JavaScript Object Notation (JSON) string.
- * @param {ColumnTable} table The table to format.
+ * @param {import('../table/Table.js').Table} table The table to format.
* @param {JSONFormatOptions} options The formatting options.
* @return {string} A JSON string.
*/
@@ -52,7 +50,7 @@ export default function(table, options = {}) {
const formatter = format[name] || defaultFormatter;
let r = -1;
table.scan(row => {
- const value = column.get(row);
+ const value = column.at(row);
text += (++r ? ',' : '') + JSON.stringify(formatter(value));
}, true, options.limit, options.offset);
@@ -60,4 +58,4 @@ export default function(table, options = {}) {
});
return text + '}' + (schema ? '}' : '');
-}
\ No newline at end of file
+}
diff --git a/src/format/to-markdown.js b/src/format/to-markdown.js
index d7cb5fd8..70d5c251 100644
--- a/src/format/to-markdown.js
+++ b/src/format/to-markdown.js
@@ -1,21 +1,20 @@
-import ColumnTable from '../table/column-table'; // eslint-disable-line no-unused-vars
-
-import formatValue from './value';
-import { columns, formats, scan } from './util';
+import formatValue from './value.js';
+import { columns, formats, scan } from './util.js';
/**
* Options for Markdown formatting.
* @typedef {object} MarkdownFormatOptions
* @property {number} [limit=Infinity] The maximum number of rows to print.
- * @property {number} [offset=0] The row offset indicating how many initial rows to skip.
- * @property {import('./util').ColumnSelectOptions} [columns] Ordered list
+ * @property {number} [offset=0] The row offset indicating how many initial
+ * rows to skip.
+ * @property {import('./util.js').ColumnSelectOptions} [columns] Ordered list
* of column names to include. If function-valued, the function should
* accept a table as input and return an array of column name strings.
- * @property {import('./util').ColumnAlignOptions} [align] Object of column
+ * @property {import('./util.js').ColumnAlignOptions} [align] Object of column
* alignment options. The object keys should be column names. The object
* values should be aligment strings, one of 'l' (left), 'c' (center), or
* 'r' (right). If specified, these override automatically inferred options.
- * @property {import('./util').ColumnFormatOptions} [format] Object of column
+ * @property {import('./util.js').ColumnFormatOptions} [format] Object of column
* format options. The object keys should be column names. The object values
* should be formatting functions or specification objects. If specified,
* these override automatically inferred options.
@@ -26,7 +25,7 @@ import { columns, formats, scan } from './util';
/**
* Format a table as a GitHub-Flavored Markdown table string.
- * @param {ColumnTable} table The table to format.
+ * @param {import('../table/Table.js').Table} table The table to format.
* @param {MarkdownFormatOptions} options The formatting options.
* @return {string} A GitHub-Flavored Markdown table string.
*/
@@ -53,4 +52,4 @@ export default function(table, options = {}) {
});
return text + '\n';
-}
\ No newline at end of file
+}
diff --git a/src/format/util.js b/src/format/util.js
index 1d855c37..082fa599 100644
--- a/src/format/util.js
+++ b/src/format/util.js
@@ -1,11 +1,9 @@
-import Table from '../table/table'; // eslint-disable-line no-unused-vars
-
-import inferFormat from './infer';
-import isFunction from '../util/is-function';
+import inferFormat from './infer.js';
+import isFunction from '../util/is-function.js';
/**
* Column selection function.
- * @typedef {(table: Table) => string[]} ColumnSelectFunction
+ * @typedef {(table: import('../table/Table.js').Table) => string[]} ColumnSelectFunction
*/
/**
@@ -17,7 +15,7 @@ import isFunction from '../util/is-function';
* Column format options. The object keys should be column names.
* The object values should be formatting functions or objects.
* If specified, these override any automatically inferred options.
- * @typedef {Object.} ColumnFormatOptions
*/
/**
@@ -51,7 +49,7 @@ export function formats(table, names, options) {
function values(table, columnName) {
const column = table.column(columnName);
- return fn => table.scan(row => fn(column.get(row)));
+ return fn => table.scan(row => fn(column.at(row)));
}
export function scan(table, names, limit = 100, offset, ctx) {
@@ -61,7 +59,7 @@ export function scan(table, names, limit = 100, offset, ctx) {
ctx.row(row);
for (let i = 0; i < n; ++i) {
const name = names[i];
- ctx.cell(data[names[i]].get(row), name, i);
+ ctx.cell(data[names[i]].at(row), name, i);
}
}, true, limit, offset);
-}
\ No newline at end of file
+}
diff --git a/src/format/value.js b/src/format/value.js
index 10adea64..a3a180c9 100644
--- a/src/format/value.js
+++ b/src/format/value.js
@@ -1,7 +1,7 @@
-import { formatDate, formatUTCDate } from '../util/format-date';
-import isDate from '../util/is-date';
-import isFunction from '../util/is-function';
-import isTypedArray from '../util/is-typed-array';
+import { formatDate, formatUTCDate } from '../util/format-date.js';
+import isDate from '../util/is-date.js';
+import isFunction from '../util/is-function.js';
+import isTypedArray from '../util/is-typed-array.js';
/**
* Column format object.
@@ -32,6 +32,7 @@ import isTypedArray from '../util/is-typed-array';
*/
export default function(v, options = {}) {
if (isFunction(options)) {
+ // @ts-ignore
return options(v) + '';
}
@@ -39,18 +40,22 @@ export default function(v, options = {}) {
if (type === 'object') {
if (isDate(v)) {
+ // @ts-ignore
return options.utc ? formatUTCDate(v) : formatDate(v);
} else {
const s = JSON.stringify(
v,
+ // @ts-ignore
(k, v) => isTypedArray(v) ? Array.from(v) : v
);
+ // @ts-ignore
const maxlen = options.maxlen || 30;
return s.length > maxlen
? s.slice(0, 28) + '\u2026' + (s[0] === '[' ? ']' : '}')
: s;
}
} else if (type === 'number') {
+ // @ts-ignore
const digits = options.digits || 0;
let a;
return v !== 0 && ((a = Math.abs(v)) >= 1e18 || a < Math.pow(10, -digits))
@@ -59,4 +64,4 @@ export default function(v, options = {}) {
} else {
return v + '';
}
-}
\ No newline at end of file
+}
diff --git a/src/helpers/bin.js b/src/helpers/bin.js
index 5b62f567..1f476719 100644
--- a/src/helpers/bin.js
+++ b/src/helpers/bin.js
@@ -31,4 +31,4 @@ export default function(name, options = {}) {
const a = args.length ? ', ' + args.map(a => a + '').join(', ') : '';
return `d => op.bin(${field}, ...op.bins(${field}${a}), ${offset || 0})`;
-}
\ No newline at end of file
+}
diff --git a/src/helpers/desc.js b/src/helpers/desc.js
index 08a57de2..956057b9 100644
--- a/src/helpers/desc.js
+++ b/src/helpers/desc.js
@@ -1,4 +1,4 @@
-import wrap from './wrap';
+import wrap from './wrap.js';
/**
* Annotate a table expression to indicate descending sort order.
@@ -9,4 +9,4 @@ import wrap from './wrap';
*/
export default function(expr) {
return wrap(expr, { desc: true });
-}
\ No newline at end of file
+}
diff --git a/src/helpers/escape.js b/src/helpers/escape.js
index 030f45e7..690f8ecc 100644
--- a/src/helpers/escape.js
+++ b/src/helpers/escape.js
@@ -1,5 +1,5 @@
-import wrap from './wrap';
-import error from '../util/error';
+import wrap from './wrap.js';
+import error from '../util/error.js';
/**
* Escape a function or value to prevent it from being parsed and recompiled.
@@ -17,4 +17,4 @@ export default function(value) {
escape: true,
toString() { error('Escaped values can not be serialized.'); }
});
-}
\ No newline at end of file
+}
diff --git a/src/helpers/field.js b/src/helpers/field.js
index 7a06d40b..101dd6ba 100644
--- a/src/helpers/field.js
+++ b/src/helpers/field.js
@@ -1,4 +1,4 @@
-import wrap from './wrap';
+import wrap from './wrap.js';
/**
* Annotate an expression to indicate it is a string field reference.
@@ -17,4 +17,4 @@ export default function(expr, name, table = 0) {
expr,
name ? { expr: name, ...props } : props
);
-}
\ No newline at end of file
+}
diff --git a/src/helpers/frac.js b/src/helpers/frac.js
index 808c333c..507aa5a5 100644
--- a/src/helpers/frac.js
+++ b/src/helpers/frac.js
@@ -8,4 +8,4 @@
*/
export default function(fraction) {
return `() => op.round(${+fraction} * op.count())`;
-}
\ No newline at end of file
+}
diff --git a/src/helpers/names.js b/src/helpers/names.js
index 1f1eae32..4167d321 100644
--- a/src/helpers/names.js
+++ b/src/helpers/names.js
@@ -21,4 +21,4 @@ export default function(...names) {
}
return m;
};
-}
\ No newline at end of file
+}
diff --git a/src/helpers/rolling.js b/src/helpers/rolling.js
index 627fb3db..213f055e 100644
--- a/src/helpers/rolling.js
+++ b/src/helpers/rolling.js
@@ -1,4 +1,4 @@
-import wrap from './wrap';
+import wrap from './wrap.js';
/**
* Annotate a table expression to compute rolling aggregate or window
@@ -28,4 +28,4 @@ export default function(expr, frame, includePeers) {
peers: !!includePeers
}
});
-}
\ No newline at end of file
+}
diff --git a/src/helpers/selection.js b/src/helpers/selection.js
index 9732af5a..7032cf25 100644
--- a/src/helpers/selection.js
+++ b/src/helpers/selection.js
@@ -1,12 +1,12 @@
-import assign from '../util/assign';
-import error from '../util/error';
-import escapeRegExp from '../util/escape-regexp';
-import isArray from '../util/is-array';
-import isFunction from '../util/is-function';
-import isNumber from '../util/is-number';
-import isObject from '../util/is-object';
-import isString from '../util/is-string';
-import toString from '../util/to-string';
+import assign from '../util/assign.js';
+import error from '../util/error.js';
+import escapeRegExp from '../util/escape-regexp.js';
+import isArray from '../util/is-array.js';
+import isFunction from '../util/is-function.js';
+import isNumber from '../util/is-number.js';
+import isObject from '../util/is-object.js';
+import isString from '../util/is-string.js';
+import toString from '../util/to-string.js';
export default function resolve(table, sel, map = new Map()) {
sel = isNumber(sel) ? table.columnName(sel) : sel;
@@ -39,7 +39,7 @@ function toObject(value) {
/**
* Proxy type for SelectHelper function.
- * @typedef {import('../table/transformable').SelectHelper} SelectHelper
+ * @typedef {import('../table/types.js').SelectHelper} SelectHelper
*/
/**
@@ -98,7 +98,9 @@ export function range(start, end) {
export function matches(pattern) {
if (isString(pattern)) pattern = RegExp(escapeRegExp(pattern));
return decorate(
+ // @ts-ignore
table => table.columnNames(name => pattern.test(name)),
+ // @ts-ignore
() => ({ matches: [pattern.source, pattern.flags] })
);
}
@@ -119,4 +121,4 @@ export function startswith(string) {
*/
export function endswith(string) {
return matches(RegExp(escapeRegExp(string) + '$'));
-}
\ No newline at end of file
+}
diff --git a/src/helpers/slice.js b/src/helpers/slice.js
index 23fd7807..0285940b 100644
--- a/src/helpers/slice.js
+++ b/src/helpers/slice.js
@@ -18,4 +18,4 @@ export default function(start = 0, end = Infinity) {
function prep(index) {
return index < 0 ? `count() + ${index}` : index;
-}
\ No newline at end of file
+}
diff --git a/src/helpers/wrap.js b/src/helpers/wrap.js
index eb52a5a2..24ff1276 100644
--- a/src/helpers/wrap.js
+++ b/src/helpers/wrap.js
@@ -1,4 +1,4 @@
-import isFunction from '../util/is-function';
+import isFunction from '../util/is-function.js';
/**
* Annotate an expression in an object wrapper.
@@ -27,4 +27,4 @@ class Wrapper {
...(isFunction(this.expr) ? { func: true } : {})
};
}
-}
\ No newline at end of file
+}
diff --git a/src/index-node.js b/src/index-browser.js
similarity index 57%
rename from src/index-node.js
rename to src/index-browser.js
index f9409583..7dcd8f9c 100644
--- a/src/index-node.js
+++ b/src/index-browser.js
@@ -1,2 +1,2 @@
-export * from './index';
-export { load, loadArrow, loadCSV, loadFixed, loadJSON } from './format/load-file';
\ No newline at end of file
+export * from './api.js';
+export { load, loadArrow, loadCSV, loadFixed, loadJSON } from './format/load-url.js';
diff --git a/src/index.js b/src/index.js
index f89313b3..275a5a8c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,47 +1,2 @@
-// export internal class definitions
-import Table from './table/table';
-import { columnFactory } from './table/column';
-import ColumnTable from './table/column-table';
-import Transformable from './table/transformable';
-import Reducer from './engine/reduce/reducer';
-import parse from './expression/parse';
-import walk_ast from './expression/ast/walk';
-import Query from './query/query';
-import { Verb, Verbs } from './query/verb';
-
-export const internal = {
- Table,
- ColumnTable,
- Transformable,
- Query,
- Reducer,
- Verb,
- Verbs,
- columnFactory,
- parse,
- walk_ast
-};
-
-// export public API
-import pkg from '../package.json';
-export const version = pkg.version;
-export { seed } from './util/random';
-export { default as fromArrow } from './format/from-arrow';
-export { default as fromCSV } from './format/from-csv';
-export { default as fromFixed } from './format/from-fixed';
-export { default as fromJSON } from './format/from-json';
-export { load, loadArrow, loadCSV, loadFixed, loadJSON } from './format/load-url';
-export { default as toArrow } from './arrow/encode';
-export { default as bin } from './helpers/bin';
-export { default as escape } from './helpers/escape';
-export { default as desc } from './helpers/desc';
-export { default as field } from './helpers/field';
-export { default as frac } from './helpers/frac';
-export { default as names } from './helpers/names';
-export { default as rolling } from './helpers/rolling';
-export { all, endswith, matches, not, range, startswith } from './helpers/selection';
-export { default as agg } from './verbs/helpers/agg';
-export { default as op } from './op/op-api';
-export { query, queryFrom } from './query/query';
-export * from './register';
-export * from './table';
+export * from './api.js';
+export { load, loadArrow, loadCSV, loadFixed, loadJSON } from './format/load-file.js';
diff --git a/src/op/aggregate-functions.js b/src/op/aggregate-functions.js
index e9341486..e122fe46 100644
--- a/src/op/aggregate-functions.js
+++ b/src/op/aggregate-functions.js
@@ -1,9 +1,9 @@
-import bins from '../util/bins';
-import distinctMap from '../util/distinct-map';
-import isBigInt from '../util/is-bigint';
-import noop from '../util/no-op';
-import NULL from '../util/null';
-import product from '../util/product';
+import bins from '../util/bins.js';
+import distinctMap from '../util/distinct-map.js';
+import isBigInt from '../util/is-bigint.js';
+import noop from '../util/no-op.js';
+import NULL from '../util/null.js';
+import product from '../util/product.js';
/**
* Initialize an aggregate operator.
@@ -54,8 +54,8 @@ function initProduct(s, value) {
* An operator instance for an aggregate function.
* @typedef {object} AggregateOperator
* @property {AggregateInit} init Initialize the operator.
- * @property {AggregateAdd} add Add a value to the operator state.
- * @property {AggregateRem} rem Remove a value from the operator state.
+ * @property {AggregateAdd} [add] Add a value to the operator state.
+ * @property {AggregateRem} [rem] Remove a value from the operator state.
* @property {AggregateValue} value Retrieve an output value.
*/
@@ -390,4 +390,4 @@ export default {
param: [1, 4],
req: ['min', 'max']
}
-};
\ No newline at end of file
+};
diff --git a/src/op/functions/array.js b/src/op/functions/array.js
index 3b5335ac..14e3167e 100644
--- a/src/op/functions/array.js
+++ b/src/op/functions/array.js
@@ -1,25 +1,128 @@
-import NULL from '../../util/null';
-import isArrayType from '../../util/is-array-type';
-import isString from '../../util/is-string';
-import isValid from '../../util/is-valid';
+import NULL from '../../util/null.js';
+import isArrayType from '../../util/is-array-type.js';
+import isString from '../../util/is-string.js';
+import isValid from '../../util/is-valid.js';
const isSeq = (seq) => isArrayType(seq) || isString(seq);
export default {
- compact: (arr) => isArrayType(arr) ? arr.filter(v => isValid(v)) : arr,
- concat: (...values) => [].concat(...values),
- includes: (seq, value, index) => isSeq(seq)
- ? seq.includes(value, index)
- : false,
- indexof: (seq, value) => isSeq(seq) ? seq.indexOf(value) : -1,
- join: (arr, delim) => isArrayType(arr) ? arr.join(delim) : NULL,
- lastindexof: (seq, value) => isSeq(seq) ? seq.lastIndexOf(value) : -1,
- length: (seq) => isSeq(seq) ? seq.length : 0,
- pluck: (arr, prop) => isArrayType(arr)
- ? arr.map(v => isValid(v) ? v[prop] : NULL)
- : NULL,
- reverse: (seq) => isArrayType(seq) ? seq.slice().reverse()
- : isString(seq) ? seq.split('').reverse().join('')
- : NULL,
- slice: (seq, start, end) => isSeq(seq) ? seq.slice(start, end) : NULL
+ /**
+ * Returns a new compacted array with invalid values
+ * (`null`, `undefined`, `NaN`) removed.
+ * @template T
+ * @param {T[]} array The input array.
+ * @return {T[]} A compacted array.
+ */
+ compact: (array) => isArrayType(array)
+ ? array.filter(v => isValid(v))
+ : array,
+
+ /**
+ * Merges two or more arrays in sequence, returning a new array.
+ * @template T
+ * @param {...(T|T[])} values The arrays to merge.
+ * @return {T[]} The merged array.
+ */
+ concat: (...values) => [].concat(...values),
+
+ /**
+ * Determines whether an *array* includes a certain *value* among its
+ * entries, returning `true` or `false` as appropriate.
+ * @template T
+ * @param {T[]} sequence The input array value.
+ * @param {T} value The value to search for.
+ * @param {number} [index=0] The integer index to start searching
+ * from (default `0`).
+ * @return {boolean} True if the value is included, false otherwise.
+ */
+ includes: (sequence, value, index) => isSeq(sequence)
+ ? sequence.includes(value, index)
+ : false,
+
+ /**
+ * Returns the first index at which a given *value* can be found in the
+ * *sequence* (array or string), or -1 if it is not present.
+ * @template T
+ * @param {T[]|string} sequence The input array or string value.
+ * @param {T} value The value to search for.
+ * @return {number} The index of the value, or -1 if not present.
+ */
+ indexof: (sequence, value) => isSeq(sequence)
+ // @ts-ignore
+ ? sequence.indexOf(value)
+ : -1,
+
+ /**
+ * Creates and returns a new string by concatenating all of the elements
+ * in an *array* (or an array-like object), separated by commas or a
+ * specified *delimiter* string. If the *array* has only one item, then
+ * that item will be returned without using the delimiter.
+ * @template T
+ * @param {T[]} array The input array value.
+ * @param {string} delim The delimiter string (default `','`).
+ * @return {string} The joined string.
+ */
+ join: (array, delim) => isArrayType(array) ? array.join(delim) : NULL,
+
+ /**
+ * Returns the last index at which a given *value* can be found in the
+ * *sequence* (array or string), or -1 if it is not present.
+ * @template T
+ * @param {T[]|string} sequence The input array or string value.
+ * @param {T} value The value to search for.
+ * @return {number} The last index of the value, or -1 if not present.
+ */
+ lastindexof: (sequence, value) => isSeq(sequence)
+ // @ts-ignore
+ ? sequence.lastIndexOf(value)
+ : -1,
+
+ /**
+ * Returns the length of the input *sequence* (array or string).
+ * @param {Array|string} sequence The input array or string value.
+ * @return {number} The length of the sequence.
+ */
+ length: (sequence) => isSeq(sequence) ? sequence.length : 0,
+
+ /**
+ * Returns a new array in which the given *property* has been extracted
+ * for each element in the input *array*.
+ * @param {Array} array The input array value.
+ * @param {string} property The property name string to extract. Nested
+ * properties are not supported: the input `"a.b"` will indicates a
+ * property with that exact name, *not* a nested property `"b"` of
+ * the object `"a"`.
+ * @return {Array} An array of plucked properties.
+ */
+ pluck: (array, property) => isArrayType(array)
+ ? array.map(v => isValid(v) ? v[property] : NULL)
+ : NULL,
+
+ /**
+ * Returns a new array or string with the element order reversed: the first
+ * *sequence* element becomes the last, and the last *sequence* element
+ * becomes the first. The input *sequence* is unchanged.
+ * @template T
+ * @param {T[]|string} sequence The input array or string value.
+ * @return {T[]|string} The reversed sequence.
+ */
+ reverse: (sequence) => isArrayType(sequence) ? sequence.slice().reverse()
+ : isString(sequence) ? sequence.split('').reverse().join('')
+ : NULL,
+
+ /**
+ * Returns a copy of a portion of the input *sequence* (array or string)
+ * selected from *start* to *end* (*end* not included) where *start* and
+ * *end* represent the index of items in the sequence.
+ * @template T
+ * @param {T[]|string} sequence The input array or string value.
+ * @param {number} [start=0] The starting integer index to copy from
+ * (inclusive, default `0`).
+ * @param {number} [end] The ending integer index to copy from (exclusive,
+ * default `sequence.length`).
+ * @return {T[]|string} The sliced sequence.
+ */
+ slice: (sequence, start, end) => isSeq(sequence)
+ ? sequence.slice(start, end)
+ : NULL
};
diff --git a/src/op/functions/bin.js b/src/op/functions/bin.js
index 1c243d71..7206343c 100644
--- a/src/op/functions/bin.js
+++ b/src/op/functions/bin.js
@@ -3,11 +3,11 @@
* Useful for creating equal-width histograms.
* Values outside the [min, max] range will be mapped to
* -Infinity (< min) or +Infinity (> max).
- * @param {number} value - The value to bin.
- * @param {number} min - The minimum bin boundary.
- * @param {number} max - The maximum bin boundary.
- * @param {number} step - The step size between bin boundaries.
- * @param {number} [offset=0] - Offset in steps by which to adjust
+ * @param {number} value The value to bin.
+ * @param {number} min The minimum bin boundary.
+ * @param {number} max The maximum bin boundary.
+ * @param {number} step The step size between bin boundaries.
+ * @param {number} [offset=0] Offset in steps by which to adjust
* the bin value. An offset of 1 will return the next boundary.
*/
export default function(value, min, max, step, offset) {
@@ -18,4 +18,4 @@ export default function(value, min, max, step, offset) {
value = Math.max(min, Math.min(value, max)),
min + step * Math.floor(1e-14 + (value - min) / step + (offset || 0))
);
-}
\ No newline at end of file
+}
diff --git a/src/op/functions/date.js b/src/op/functions/date.js
index 51ac4d7b..1b7971b2 100644
--- a/src/op/functions/date.js
+++ b/src/op/functions/date.js
@@ -1,5 +1,5 @@
-import { formatDate, formatUTCDate } from '../../util/format-date';
-import parseIsoDate from '../../util/parse-iso-date';
+import { formatDate, formatUTCDate } from '../../util/format-date.js';
+import parseIsoDate from '../../util/parse-iso-date.js';
const msMinute = 6e4;
const msDay = 864e5;
@@ -22,7 +22,7 @@ const t = d => (
* @param {number} [minutes=0] The minute within the hour.
* @param {number} [seconds=0] The second within the minute.
* @param {number} [milliseconds=0] The milliseconds within the second.
- * @return {date} The resuting Date value.
+ * @return {Date} The resuting Date value.
*/
function datetime(year, month, date, hours, minutes, seconds, milliseconds) {
return !arguments.length
@@ -48,7 +48,7 @@ function datetime(year, month, date, hours, minutes, seconds, milliseconds) {
* @param {number} [minutes=0] The minute within the hour.
* @param {number} [seconds=0] The second within the minute.
* @param {number} [milliseconds=0] The milliseconds within the second.
- * @return {date} The resuting Date value.
+ * @return {Date} The resuting Date value.
*/
function utcdatetime(year, month, date, hours, minutes, seconds, milliseconds) {
return !arguments.length
@@ -64,6 +64,12 @@ function utcdatetime(year, month, date, hours, minutes, seconds, milliseconds) {
));
}
+/**
+ * Return the current day of the year in local time as a number
+ * between 1 and 366.
+ * @param {Date|number} date A date or timestamp.
+ * @return {number} The day of the year in local time.
+ */
function dayofyear(date) {
t1.setTime(+date);
t1.setHours(0, 0, 0, 0);
@@ -71,16 +77,28 @@ function dayofyear(date) {
t0.setMonth(0);
t0.setDate(1);
const tz = (t1.getTimezoneOffset() - t0.getTimezoneOffset()) * msMinute;
- return Math.floor(1 + ((t1 - t0) - tz) / msDay);
+ return Math.floor(1 + ((+t1 - +t0) - tz) / msDay);
}
+/**
+ * Return the current day of the year in UTC time as a number
+ * between 1 and 366.
+ * @param {Date|number} date A date or timestamp.
+ * @return {number} The day of the year in UTC time.
+ */
function utcdayofyear(date) {
t1.setTime(+date);
t1.setUTCHours(0, 0, 0, 0);
const t0 = Date.UTC(t1.getUTCFullYear(), 0, 1);
- return Math.floor(1 + (t1 - t0) / msDay);
+ return Math.floor(1 + (+t1 - t0) / msDay);
}
+/**
+ * Return the current week of the year in local time as a number
+ * between 1 and 52.
+ * @param {Date|number} date A date or timestamp.
+ * @return {number} The week of the year in local time.
+ */
function week(date, firstday) {
const i = firstday || 0;
t1.setTime(+date);
@@ -92,9 +110,15 @@ function week(date, firstday) {
t0.setDate(1 - (t0.getDay() + 7 - i) % 7);
t0.setHours(0, 0, 0, 0);
const tz = (t1.getTimezoneOffset() - t0.getTimezoneOffset()) * msMinute;
- return Math.floor((1 + (t1 - t0) - tz) / msWeek);
+ return Math.floor((1 + (+t1 - +t0) - tz) / msWeek);
}
+/**
+ * Return the current week of the year in UTC time as a number
+ * between 1 and 52.
+ * @param {Date|number} date A date or timestamp.
+ * @return {number} The week of the year in UTC time.
+ */
function utcweek(date, firstday) {
const i = firstday || 0;
t1.setTime(+date);
@@ -105,36 +129,263 @@ function utcweek(date, firstday) {
t0.setUTCDate(1);
t0.setUTCDate(1 - (t0.getUTCDay() + 7 - i) % 7);
t0.setUTCHours(0, 0, 0, 0);
- return Math.floor((1 + (t1 - t0)) / msWeek);
+ return Math.floor((1 + (+t1 - +t0)) / msWeek);
}
export default {
- format_date: (date, shorten) => formatDate(t(date), !shorten),
- format_utcdate: (date, shorten) => formatUTCDate(t(date), !shorten),
- timestamp: (date) => +t(date),
- year: (date) => t(date).getFullYear(),
- quarter: (date) => Math.floor(t(date).getMonth() / 3),
- month: (date) => t(date).getMonth(),
- date: (date) => t(date).getDate(),
- dayofweek: (date) => t(date).getDay(),
- hours: (date) => t(date).getHours(),
- minutes: (date) => t(date).getMinutes(),
- seconds: (date) => t(date).getSeconds(),
- milliseconds: (date) => t(date).getMilliseconds(),
- utcyear: (date) => t(date).getUTCFullYear(),
- utcquarter: (date) => Math.floor(t(date).getUTCMonth() / 3),
- utcmonth: (date) => t(date).getUTCMonth(),
- utcdate: (date) => t(date).getUTCDate(),
- utcdayofweek: (date) => t(date).getUTCDay(),
- utchours: (date) => t(date).getUTCHours(),
- utcminutes: (date) => t(date).getUTCMinutes(),
- utcseconds: (date) => t(date).getUTCSeconds(),
- utcmilliseconds: (date) => t(date).getUTCMilliseconds(),
+ /**
+ * Returns an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) formatted
+ * string for the given *date* in local timezone. The resulting string is
+ * compatible with *parse_date* and JavaScript's built-in *Date.parse*.
+ * @param {Date | number} date The input Date or timestamp value.
+ * @param {boolean} [shorten=false] A boolean flag (default `false`)
+ * indicating if the formatted string should be shortened if possible.
+ * For example, the local date `2001-01-01` will shorten from
+ * `"2001-01-01T00:00:00.000"` to `"2001-01-01T00:00"`.
+ * @return {string} The formatted date string in local time.
+ */
+ format_date: (date, shorten) => formatDate(t(date), !shorten),
+
+ /**
+ * Returns an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) formatted
+ * string for the given *date* in Coordinated Universal Time (UTC). The
+ * resulting string is compatible with *parse_date* and JavaScript's
+ * built-in *Date.parse*.
+ * @param {Date | number} date The input Date or timestamp value.
+ * @param {boolean} [shorten=false] A boolean flag (default `false`)
+ * indicating if the formatted string should be shortened if possible.
+ * For example, the the UTC date `2001-01-01` will shorten from
+ * `"2001-01-01T00:00:00.000Z"` to `"2001-01-01"`
+ * @return {string} The formatted date string in UTC time.
+ */
+ format_utcdate: (date, shorten) => formatUTCDate(t(date), !shorten),
+
+ /**
+ * Returns the number of milliseconds elapsed since midnight, January 1,
+ * 1970 Universal Coordinated Time (UTC).
+ * @return {number} The timestamp for now.
+ */
+ now: Date.now,
+
+ /**
+ * Returns the timestamp for a *date* as the number of milliseconds elapsed
+ * since January 1, 1970 00:00:00 UTC.
+ * @param {Date | number} date The input Date value.
+ * @return {number} The timestamp value.
+ */
+ timestamp: (date) => +t(date),
+
+ /**
+ * Creates and returns a new Date value. If no arguments are provided,
+ * the current date and time are used.
+ * @param {number} [year] The year.
+ * @param {number} [month=0] The (zero-based) month.
+ * @param {number} [date=1] The date within the month.
+ * @param {number} [hours=0] The hour within the day.
+ * @param {number} [minutes=0] The minute within the hour.
+ * @param {number} [seconds=0] The second within the minute.
+ * @param {number} [milliseconds=0] The milliseconds within the second.
+ * @return {Date} The Date value.
+ */
datetime,
- dayofyear,
+
+ /**
+ * Returns the year of the specified *date* according to local time.
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The year value in local time.
+ */
+ year: (date) => t(date).getFullYear(),
+
+ /**
+ * Returns the zero-based quarter of the specified *date* according to
+ * local time.
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The quarter value in local time.
+ */
+ quarter: (date) => Math.floor(t(date).getMonth() / 3),
+
+ /**
+ * Returns the zero-based month of the specified *date* according to local
+ * time. A value of `0` indicates January, `1` indicates February, and so on.
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The month value in local time.
+ */
+ month: (date) => t(date).getMonth(),
+
+ /**
+ * Returns the week number of the year (0-53) for the specified *date*
+ * according to local time. By default, Sunday is used as the first day
+ * of the week. All days in a new year preceding the first Sunday are
+ * considered to be in week 0.
+ * @param {Date | number} date The input Date or timestamp value.
+ * @param {number} firstday The number of first day of the week (default
+ * `0` for Sunday, `1` for Monday and so on).
+ * @return {number} The week of the year in local time.
+ */
week,
+
+ /**
+ * Returns the date (day of month) of the specified *date* according
+ * to local time.
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The date (day of month) value.
+ */
+ date: (date) => t(date).getDate(),
+
+ /**
+ * Returns the day of the year (1-366) of the specified *date* according
+ * to local time.
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The day of the year in local time.
+ */
+ dayofyear,
+
+ /**
+ * Returns the Sunday-based day of the week (0-6) of the specified *date*
+ * according to local time. A value of `0` indicates Sunday, `1` indicates
+ * Monday, and so on.
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The day of the week value in local time.
+ */
+ dayofweek: (date) => t(date).getDay(),
+
+ /**
+ * Returns the hour of the day for the specified *date* according
+ * to local time.
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The hour value in local time.
+ */
+ hours: (date) => t(date).getHours(),
+
+ /**
+ * Returns the minute of the hour for the specified *date* according
+ * to local time.
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The minutes value in local time.
+ */
+ minutes: (date) => t(date).getMinutes(),
+
+ /**
+ * Returns the seconds of the minute for the specified *date* according
+ * to local time.
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The seconds value in local time.
+ */
+ seconds: (date) => t(date).getSeconds(),
+
+ /**
+ * Returns the milliseconds of the second for the specified *date* according
+ * to local time.
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The milliseconds value in local time.
+ */
+ milliseconds: (date) => t(date).getMilliseconds(),
+
+ /**
+ * Creates and returns a new Date value using Coordinated Universal Time
+ * (UTC). If no arguments are provided, the current date and time are used.
+ * @param {number} [year] The year.
+ * @param {number} [month=0] The (zero-based) month.
+ * @param {number} [date=1] The date within the month.
+ * @param {number} [hours=0] The hour within the day.
+ * @param {number} [minutes=0] The minute within the hour.
+ * @param {number} [seconds=0] The second within the minute.
+ * @param {number} [milliseconds=0] The milliseconds within the second.
+ * @return {Date} The Date value.
+ */
utcdatetime,
- utcdayofyear,
+
+ /**
+ * Returns the year of the specified *date* according to Coordinated
+ * Universal Time (UTC).
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The year value in UTC time.
+ */
+ utcyear: (date) => t(date).getUTCFullYear(),
+
+ /**
+ * Returns the zero-based quarter of the specified *date* according to
+ * Coordinated Universal Time (UTC)
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The quarter value in UTC time.
+ */
+ utcquarter: (date) => Math.floor(t(date).getUTCMonth() / 3),
+
+ /**
+ * Returns the zero-based month of the specified *date* according to
+ * Coordinated Universal Time (UTC). A value of `0` indicates January,
+ * `1` indicates February, and so on.
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The month value in UTC time.
+ */
+ utcmonth: (date) => t(date).getUTCMonth(),
+
+ /**
+ * Returns the week number of the year (0-53) for the specified *date*
+ * according to Coordinated Universal Time (UTC). By default, Sunday is
+ * used as the first day of the week. All days in a new year preceding the
+ * first Sunday are considered to be in week 0.
+ * @param {Date | number} date The input Date or timestamp value.
+ * @param {number} firstday The number of first day of the week (default
+ * `0` for Sunday, `1` for Monday and so on).
+ * @return {number} The week of the year in UTC time.
+ */
utcweek,
- now: Date.now
-};
\ No newline at end of file
+
+ /**
+ * Returns the date (day of month) of the specified *date* according to
+ * Coordinated Universal Time (UTC).
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The date (day of month) value in UTC time.
+ */
+ utcdate: (date) => t(date).getUTCDate(),
+
+ /**
+ * Returns the day of the year (1-366) of the specified *date* according
+ * to Coordinated Universal Time (UTC).
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The day of the year in UTC time.
+ */
+ utcdayofyear,
+
+ /**
+ * Returns the Sunday-based day of the week (0-6) of the specified *date*
+ * according to Coordinated Universal Time (UTC). A value of `0` indicates
+ * Sunday, `1` indicates Monday, and so on.
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The day of the week in UTC time.
+ */
+ utcdayofweek: (date) => t(date).getUTCDay(),
+
+ /**
+ * Returns the hour of the day for the specified *date* according to
+ * Coordinated Universal Time (UTC).
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The hours value in UTC time.
+ */
+ utchours: (date) => t(date).getUTCHours(),
+
+ /**
+ * Returns the minute of the hour for the specified *date* according to
+ * Coordinated Universal Time (UTC).
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The minutes value in UTC time.
+ */
+ utcminutes: (date) => t(date).getUTCMinutes(),
+
+ /**
+ * Returns the seconds of the minute for the specified *date* according to
+ * Coordinated Universal Time (UTC).
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The seconds value in UTC time.
+ */
+ utcseconds: (date) => t(date).getUTCSeconds(),
+
+ /**
+ * Returns the milliseconds of the second for the specified *date* according to
+ * Coordinated Universal Time (UTC).
+ * @param {Date | number} date The input Date or timestamp value.
+ * @return {number} The milliseconds value in UTC time.
+ */
+ utcmilliseconds: (date) => t(date).getUTCMilliseconds()
+};
diff --git a/src/op/functions/equal.js b/src/op/functions/equal.js
index 94422087..7aae8c21 100644
--- a/src/op/functions/equal.js
+++ b/src/op/functions/equal.js
@@ -1,6 +1,6 @@
-import isDate from '../../util/is-date';
-import isRegExp from '../../util/is-regexp';
-import isObject from '../../util/is-object';
+import isDate from '../../util/is-date.js';
+import isRegExp from '../../util/is-regexp.js';
+import isObject from '../../util/is-object.js';
/**
* Compare two values for equality, using join semantics in which null
@@ -62,4 +62,4 @@ function arrayEqual(a, b, test = equal) {
}
return true;
-}
\ No newline at end of file
+}
diff --git a/src/op/functions/index.js b/src/op/functions/index.js
index e59ad3c2..7eefe153 100644
--- a/src/op/functions/index.js
+++ b/src/op/functions/index.js
@@ -1,13 +1,13 @@
-import array from './array';
-import bin from './bin';
-import date from './date';
-import equal from './equal';
-import json from './json';
-import math from './math';
-import object from './object';
-import recode from './recode';
-import sequence from './sequence';
-import string from './string';
+import array from './array.js';
+import bin from './bin.js';
+import date from './date.js';
+import equal from './equal.js';
+import json from './json.js';
+import math from './math.js';
+import object from './object.js';
+import recode from './recode.js';
+import sequence from './sequence.js';
+import string from './string.js';
export default {
bin,
@@ -20,4 +20,4 @@ export default {
...math,
...object,
...string
-};
\ No newline at end of file
+};
diff --git a/src/op/functions/json.js b/src/op/functions/json.js
index 99c5ba81..d0e4e6ef 100644
--- a/src/op/functions/json.js
+++ b/src/op/functions/json.js
@@ -1,4 +1,16 @@
export default {
- parse_json: (str) => JSON.parse(str),
- to_json: (val) => JSON.stringify(val)
-};
\ No newline at end of file
+ /**
+ * Parses a string *value* in JSON format, constructing the JavaScript
+ * value or object described by the string.
+ * @param {string} value The input string value.
+ * @return {any} The parsed JSON.
+ */
+ parse_json: (value) => JSON.parse(value),
+
+ /**
+ * Converts a JavaScript object or value to a JSON string.
+ * @param {*} value The value to convert to a JSON string.
+ * @return {string} The JSON string.
+ */
+ to_json: (value) => JSON.stringify(value)
+};
diff --git a/src/op/functions/math.js b/src/op/functions/math.js
index 4faddf51..b7064fff 100644
--- a/src/op/functions/math.js
+++ b/src/op/functions/math.js
@@ -1,43 +1,300 @@
-import { random } from '../../util/random';
+import { random } from '../../util/random.js';
export default {
+ /**
+ * Return a random floating point number between 0 (inclusive) and 1
+ * (exclusive). By default uses *Math.random*. Use the *seed* method
+ * to instead use a seeded random number generator.
+ * @return {number} A pseudorandom number between 0 and 1.
+ */
random,
- is_nan: Number.isNaN,
+
+ /**
+ * Tests if the input *value* is not a number (`NaN`); equivalent
+ * to *Number.isNaN*.
+ * @param {*} value The value to test.
+ * @return {boolean} True if the value is not a number, false otherwise.
+ */
+ is_nan: Number.isNaN,
+
+ /**
+ * Tests if the input *value* is finite; equivalent to *Number.isFinite*.
+ * @param {*} value The value to test.
+ * @return {boolean} True if the value is finite, false otherwise.
+ */
is_finite: Number.isFinite,
- abs: Math.abs,
- cbrt: Math.cbrt,
- ceil: Math.ceil,
- clz32: Math.clz32,
- exp: Math.exp,
- expm1: Math.expm1,
- floor: Math.floor,
- fround: Math.fround,
+ /**
+ * Returns the absolute value of the input *value*; equivalent to *Math.abs*.
+ * @param {number} value The input number value.
+ * @return {number} The absolute value.
+ */
+ abs: Math.abs,
+
+ /**
+ * Returns the cube root value of the input *value*; equivalent to
+ * *Math.cbrt*.
+ * @param {number} value The input number value.
+ * @return {number} The cube root value.
+ */
+ cbrt: Math.cbrt,
+
+ /**
+ * Returns the ceiling of the input *value*, the nearest integer equal to
+ * or greater than the input; equivalent to *Math.ceil*.
+ * @param {number} value The input number value.
+ * @return {number} The ceiling value.
+ */
+ ceil: Math.ceil,
+
+ /**
+ * Returns the number of leading zero bits in the 32-bit binary
+ * representation of a number *value*; equivalent to *Math.clz32*.
+ * @param {number} value The input number value.
+ * @return {number} The leading zero bits value.
+ */
+ clz32: Math.clz32,
+
+ /**
+ * Returns *evalue*, where *e* is Euler's number, the base of the
+ * natural logarithm; equivalent to *Math.exp*.
+ * @param {number} value The input number value.
+ * @return {number} The base-e exponentiated value.
+ */
+ exp: Math.exp,
+
+ /**
+ * Returns *evalue - 1*, where *e* is Euler's number, the base of
+ * the natural logarithm; equivalent to *Math.expm1*.
+ * @param {number} value The input number value.
+ * @return {number} The base-e exponentiated value minus 1.
+ */
+ expm1: Math.expm1,
+
+ /**
+ * Returns the floor of the input *value*, the nearest integer equal to or
+ * less than the input; equivalent to *Math.floor*.
+ * @param {number} value The input number value.
+ * @return {number} The floor value.
+ */
+ floor: Math.floor,
+
+ /**
+ * Returns the nearest 32-bit single precision float representation of the
+ * input number *value*; equivalent to *Math.fround*. Useful for translating
+ * between 64-bit `Number` values and values from a `Float32Array`.
+ * @param {number} value The input number value.
+ * @return {number} The rounded value.
+ */
+ fround: Math.fround,
+
+ /**
+ * Returns the greatest (maximum) value among the input *values*; equivalent
+ * to *Math.max*. This is _not_ an aggregate function, see *op.max* to
+ * compute a maximum value across multiple rows.
+ * @param {...number} values The input number values.
+ * @return {number} The greatest (maximum) value among the inputs.
+ */
greatest: Math.max,
- least: Math.min,
- log: Math.log,
- log10: Math.log10,
- log1p: Math.log1p,
- log2: Math.log2,
- pow: Math.pow,
- round: Math.round,
- sign: Math.sign,
- sqrt: Math.sqrt,
- trunc: Math.trunc,
-
- degrees: (rad) => 180 * rad / Math.PI,
- radians: (deg) => Math.PI * deg / 180,
- acos: Math.acos,
- acosh: Math.acosh,
- asin: Math.asin,
- asinh: Math.asinh,
- atan: Math.atan,
- atan2: Math.atan2,
- atanh: Math.atanh,
- cos: Math.cos,
- cosh: Math.cosh,
- sin: Math.sin,
- sinh: Math.sinh,
- tan: Math.tan,
- tanh: Math.tanh
-};
\ No newline at end of file
+
+ /**
+ * Returns the least (minimum) value among the input *values*; equivalent
+ * to *Math.min*. This is _not_ an aggregate function, see *op.min* to
+ * compute a minimum value across multiple rows.
+ * @param {...number} values The input number values.
+ * @return {number} The least (minimum) value among the inputs.
+ */
+ least: Math.min,
+
+ /**
+ * Returns the natural logarithm (base *e*) of a number *value*; equivalent
+ * to *Math.log*.
+ * @param {number} value The input number value.
+ * @return {number} The base-e log value.
+ */
+ log: Math.log,
+
+ /**
+ * Returns the base 10 logarithm of a number *value*; equivalent
+ * to *Math.log10*.
+ * @param {number} value The input number value.
+ * @return {number} The base-10 log value.
+ */
+ log10: Math.log10,
+
+ /**
+ * Returns the natural logarithm (base *e*) of 1 + a number *value*;
+ * equivalent to *Math.log1p*.
+ * @param {number} value The input number value.
+ * @return {number} The base-e log of value + 1.
+ */
+ log1p: Math.log1p,
+
+ /**
+ * Returns the base 2 logarithm of a number *value*; equivalent
+ * to *Math.log2*.
+ * @param {number} value The input number value.
+ * @return {number} The base-2 log value.
+ */
+ log2: Math.log2,
+
+ /**
+ * Returns the *base* raised to the *exponent* power, that is,
+ * *base**exponent*; equivalent to *Math.pow*.
+ * @param {number} base The base number value.
+ * @param {number} exponent The exponent number value.
+ * @return {number} The exponentiated value.
+ */
+ pow: Math.pow,
+
+ /**
+ * Returns the value of a number rounded to the nearest integer;
+ * equivalent to *Math.round*.
+ * @param {number} value The input number value.
+ * @return {number} The rounded value.
+ */
+ round: Math.round,
+
+ /**
+ * Returns either a positive or negative +/- 1, indicating the sign of the
+ * input *value*; equivalent to *Math.sign*.
+ * @param {number} value The input number value.
+ * @return {number} The sign of the value.
+ */
+ sign: Math.sign,
+
+ /**
+ * Returns the square root of the input *value*; equivalent to *Math.sqrt*.
+ * @param {number} value The input number value.
+ * @return {number} The square root value.
+ */
+ sqrt: Math.sqrt,
+
+ /**
+ * Returns the integer part of a number by removing any fractional digits;
+ * equivalent to *Math.trunc*.
+ * @param {number} value The input number value.
+ * @return {number} The truncated value.
+ */
+ trunc: Math.trunc,
+
+ /**
+ * Converts the input *radians* value to degrees.
+ * @param {number} radians The input radians value.
+ * @return {number} The value in degrees
+ */
+ degrees: (radians) => 180 * radians / Math.PI,
+
+ /**
+ * Converts the input *degrees* value to radians.
+ * @param {number} degrees The input degrees value.
+ * @return {number} The value in radians.
+ */
+ radians: (degrees) => Math.PI * degrees / 180,
+
+ /**
+ * Returns the arc-cosine (in radians) of a number *value*;
+ * equivalent to *Math.acos*.
+ * @param {number} value The input number value.
+ * @return {number} The arc-cosine value.
+ */
+ acos: Math.acos,
+
+ /**
+ * Returns the hyperbolic arc-cosine of a number *value*;
+ * equivalent to *Math.acosh*.
+ * @param {number} value The input number value.
+ * @return {number} The hyperbolic arc-cosine value.
+ */
+ acosh: Math.acosh,
+
+ /**
+ * Returns the arc-sine (in radians) of a number *value*;
+ * equivalent to *Math.asin*.
+ * @param {number} value The input number value.
+ * @return {number} The arc-sine value.
+ */
+ asin: Math.asin,
+
+ /**
+ * Returns the hyperbolic arc-sine of a number *value*;
+ * equivalent to *Math.asinh*.
+ * @param {number} value The input number value.
+ * @return {number} The hyperbolic arc-sine value.
+ */
+ asinh: Math.asinh,
+
+ /**
+ * Returns the arc-tangent (in radians) of a number *value*;
+ * equivalent to *Math.atan*.
+ * @param {number} value The input number value.
+ * @return {number} The arc-tangent value.
+ */
+ atan: Math.atan,
+
+ /**
+ * Returns the angle in the plane (in radians) between the positive x-axis
+ * and the ray from (0, 0) to the point (*x*, *y*);
+ * equivalent to *Math.atan2*.
+ * @param {number} y The y coordinate of the point.
+ * @param {number} x The x coordinate of the point.
+ * @return {number} The arc-tangent angle.
+ */
+ atan2: Math.atan2,
+
+ /**
+ * Returns the hyperbolic arc-tangent of a number *value*;
+ * equivalent to *Math.atanh*.
+ * @param {number} value The input number value.
+ * @return {number} The hyperbolic arc-tangent value.
+ */
+ atanh: Math.atanh,
+
+ /**
+ * Returns the cosine (in radians) of a number *value*;
+ * equivalent to *Math.cos*.
+ * @param {number} value The input number value.
+ * @return {number} The cosine value.
+ */
+ cos: Math.cos,
+
+ /**
+ * Returns the hyperbolic cosine (in radians) of a number *value*;
+ * equivalent to *Math.cosh*.
+ * @param {number} value The input number value.
+ * @return {number} The hyperbolic cosine value.
+ */
+ cosh: Math.cosh,
+
+ /**
+ * Returns the sine (in radians) of a number *value*;
+ * equivalent to *Math.sin*.
+ * @param {number} value The input number value.
+ * @return {number} The sine value.
+ */
+ sin: Math.sin,
+
+ /**
+ * Returns the hyperbolic sine (in radians) of a number *value*;
+ * equivalent to *Math.sinh*.
+ * @param {number} value The input number value.
+ * @return {number} The hyperbolic sine value.
+ */
+ sinh: Math.sinh,
+
+ /**
+ * Returns the tangent (in radians) of a number *value*;
+ * equivalent to *Math.tan*.
+ * @param {number} value The input number value.
+ * @return {number} The tangent value.
+ */
+ tan: Math.tan,
+
+ /**
+ * Returns the hyperbolic tangent (in radians) of a number *value*;
+ * equivalent to *Math.tanh*.
+ * @param {number} value The input number value.
+ * @return {number} The hyperbolic tangent value.
+ */
+ tanh: Math.tanh
+};
diff --git a/src/op/functions/object.js b/src/op/functions/object.js
index 7c1d693b..a00f7d7b 100644
--- a/src/op/functions/object.js
+++ b/src/op/functions/object.js
@@ -1,24 +1,74 @@
-import NULL from '../../util/null';
-import has from '../../util/has';
-import isMap from '../../util/is-map';
-import isMapOrSet from '../../util/is-map-or-set';
+import NULL from '../../util/null.js';
+import has from '../../util/has.js';
+import isMap from '../../util/is-map.js';
+import isMapOrSet from '../../util/is-map-or-set.js';
function array(iter) {
return Array.from(iter);
}
export default {
- has: (obj, key) => isMapOrSet(obj) ? obj.has(key)
- : obj != null ? has(obj, key)
- : false,
- keys: (obj) => isMap(obj) ? array(obj.keys())
- : obj != null ? Object.keys(obj)
- : [],
- values: (obj) => isMapOrSet(obj) ? array(obj.values())
- : obj != null ? Object.values(obj)
- : [],
- entries: (obj) => isMapOrSet(obj) ? array(obj.entries())
- : obj != null ? Object.entries(obj)
- : [],
- object: (entries) => entries ? Object.fromEntries(entries) : NULL
-};
\ No newline at end of file
+ /**
+ * Returns a boolean indicating whether the *object* has the specified *key*
+ * as its own property (as opposed to inheriting it). If the *object* is a
+ * *Map* or *Set* instance, the *has* method will be invoked directly on the
+ * object, otherwise *Object.hasOwnProperty* is used.
+ * @template K, V
+ * @param {Map|Set|Record} object The object, Map, or Set to
+ * test for property membership.
+ * @param {K} key The property key to test for.
+ * @return {boolean} True if the object has the given key, false otherwise.
+ */
+ has: (object, key) => isMapOrSet(object) ? object.has(key)
+ : object != null ? has(object, `${key}`)
+ : false,
+
+ /**
+ * Returns an array of a given *object*'s own enumerable property names. If
+ * the *object* is a *Map* instance, the *keys* method will be invoked
+ * directly on the object, otherwise *Object.keys* is used.
+ * @template K, V
+ * @param {Map|Record} object The input object or Map value.
+ * @return {K[]} An array of property key name strings.
+ */
+ keys: (object) => isMap(object) ? array(object.keys())
+ : object != null ? Object.keys(object)
+ : [],
+
+ /**
+ * Returns an array of a given *object*'s own enumerable property values. If
+ * the *object* is a *Map* or *Set* instance, the *values* method will be
+ * invoked directly on the object, otherwise *Object.values* is used.
+ * @template K, V
+ * @param {Map|Set|Record} object The input
+ * object, Map, or Set value.
+ * @return {V[]} An array of property values.
+ */
+ values: (object) => isMapOrSet(object) ? array(object.values())
+ : object != null ? Object.values(object)
+ : [],
+
+ /**
+ * Returns an array of a given *object*'s own enumerable keyed property
+ * `[key, value]` pairs. If the *object* is a *Map* or *Set* instance, the
+ * *entries* method will be invoked directly on the object, otherwise
+ * *Object.entries* is used.
+ * @template K, V
+ * @param {Map|Set|Record} object The input
+ * object, Map, or Set value.
+ * @return {[K, V][]} An array of property values.
+ */
+ entries: (object) => isMapOrSet(object) ? array(object.entries())
+ : object != null ? Object.entries(object)
+ : [],
+
+ /**
+ * Returns a new object given iterable *entries* of `[key, value]` pairs.
+ * This method is Arquero's version of the *Object.fromEntries* method.
+ * @template K, V
+ * @param {Iterable<[K, V]>} entries An iterable collection of `[key, value]`
+ * pairs, such as an array of two-element arrays or a *Map*.
+ * @return {Record} An object of consolidated key-value pairs.
+ */
+ object: (entries) => entries ? Object.fromEntries(entries) : NULL
+};
diff --git a/src/op/functions/recode.js b/src/op/functions/recode.js
index 80bcb48d..34b9b5eb 100644
--- a/src/op/functions/recode.js
+++ b/src/op/functions/recode.js
@@ -1,24 +1,26 @@
-import has from '../../util/has';
+import has from '../../util/has.js';
/**
* Recodes an input value to an alternative value, based on a provided
* value map. If a fallback value is specified, it will be returned when
* a matching value is not found in the map; otherwise, the input value
* is returned unchanged.
- * @param {*} value The value to recode. The value must be safely
+ * @template T
+ * @param {T} value The value to recode. The value must be safely
* coercible to a string for lookup against the value map.
- * @param {object|Map} map An object or Map with input values for keys and
- * output recoded values as values. If a non-Map object, only the object's
- * own properties will be considered.
- * @param {*} [fallback] A default fallback value to use if the input
+ * @param {Map|Record} map An object or Map with input values
+ * for keys and output recoded values as values. If a non-Map object, only
+ * the object's own properties will be considered.
+ * @param {T} [fallback] A default fallback value to use if the input
* value is not found in the value map.
- * @return {*} The recoded value.
+ * @return {T} The recoded value.
*/
export default function(value, map, fallback) {
if (map instanceof Map) {
if (map.has(value)) return map.get(value);
- } else if (has(map, value)) {
- return map[value];
+ } else {
+ const key = `${value}`;
+ if (has(map, key)) return map[key];
}
return fallback !== undefined ? fallback : value;
-}
\ No newline at end of file
+}
diff --git a/src/op/functions/sequence.js b/src/op/functions/sequence.js
index 2b36f973..e9a5b8f9 100644
--- a/src/op/functions/sequence.js
+++ b/src/op/functions/sequence.js
@@ -27,4 +27,4 @@ export default function(start, stop, step) {
}
return seq;
-}
\ No newline at end of file
+}
diff --git a/src/op/functions/string.js b/src/op/functions/string.js
index f1faf379..581f795f 100644
--- a/src/op/functions/string.js
+++ b/src/op/functions/string.js
@@ -1,36 +1,222 @@
export default {
- parse_date: (str) => str == null ? str : new Date(str),
- parse_float: (str) => str == null ? str : Number.parseFloat(str),
- parse_int: (str, radix) => str == null ? str : Number.parseInt(str, radix),
- endswith: (str, search, length) => str == null ? false
- : String(str).endsWith(search, length),
- match: (str, regexp, index) => {
- const m = str == null ? str : String(str).match(regexp);
- return index == null || m == null ? m
- : typeof index === 'number' ? m[index]
- : m.groups ? m.groups[index]
- : null;
- },
- normalize: (str, form) => str == null ? str
- : String(str).normalize(form),
- padend: (str, len, fill) => str == null ? str
- : String(str).padEnd(len, fill),
- padstart: (str, len, fill) => str == null ? str
- : String(str).padStart(len, fill),
- upper: (str) => str == null ? str
- : String(str).toUpperCase(),
- lower: (str) => str == null ? str
- : String(str).toLowerCase(),
- repeat: (str, num) => str == null ? str
- : String(str).repeat(num),
- replace: (str, pattern, replacement) => str == null ? str
- : String(str).replace(pattern, String(replacement)),
- substring: (str, start, end) => str == null ? str
- : String(str).substring(start, end),
- split: (str, separator, limit) => str == null ? []
- : String(str).split(separator, limit),
- startswith: (str, search, length) => str == null ? false
- : String(str).startsWith(search, length),
- trim: (str) => str == null ? str
- : String(str).trim()
-};
\ No newline at end of file
+ /**
+ * Parses a string *value* and returns a Date instance. Beware: this method
+ * uses JavaScript's *Date.parse()* functionality, which is inconsistently
+ * implemented across browsers. That said,
+ * [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) formatted strings such
+ * as those produced by *op.format_date* and *op.format_utcdate* should be
+ * supported across platforms. Note that "bare" ISO date strings such as
+ * `"2001-01-01"` are interpreted by JavaScript as indicating midnight of
+ * that day in Coordinated Universal Time (UTC), *not* local time. To
+ * indicate the local timezone, an ISO string can include additional time
+ * components and no `Z` suffix: `"2001-01-01T00:00"`.
+ * @param {*} value The input value.
+ * @return {Date} The parsed date value.
+ */
+ parse_date: (value) => value == null ? value : new Date(value),
+
+ /**
+ * Parses a string *value* and returns a floating point number.
+ * @param {*} value The input value.
+ * @return {number} The parsed number value.
+ */
+ parse_float: (value) => value == null ? value : Number.parseFloat(value),
+
+ /**
+ * Parses a string *value* and returns an integer of the specified radix
+ * (the base in mathematical numeral systems).
+ * @param {*} value The input value.
+ * @param {number} [radix] An integer between 2 and 36 that represents the
+ * radix (the base in mathematical numeral systems) of the string. Be
+ * careful: this does not default to 10! If *radix* is `undefined`, `0`,
+ * or unspecified, JavaScript assumes the following: If the input string
+ * begins with `"0x"` or `"0X"` (a zero, followed by lowercase or
+ * uppercase X), the radix is assumed to be 16 and the rest of the string
+ * is parsed as a hexidecimal number. If the input string begins with `"0"`
+ * (a zero), the radix is assumed to be 8 (octal) or 10 (decimal). Exactly
+ * which radix is chosen is implementation-dependent. If the input string
+ * begins with any other value, the radix is 10 (decimal).
+ * @return {number} The parsed integer value.
+ */
+ parse_int: (value, radix) => value == null ? value
+ : Number.parseInt(value, radix),
+
+ /**
+ * Determines whether a string *value* ends with the characters of a
+ * specified *search* string, returning `true` or `false` as appropriate.
+ * @param {any} value The input string value.
+ * @param {string} search The search string to test for.
+ * @param {number} [length] If provided, used as the length of *value*
+ * (default `value.length`).
+ * @return {boolean} True if the value ends with the search string,
+ * false otherwise.
+ */
+ endswith: (value, search, length) => value == null ? false
+ : String(value).endsWith(search, length),
+
+ /**
+ * Retrieves the result of matching a string *value* against a regular
+ * expression *regexp*. If no *index* is specified, returns an array
+ * whose contents depend on the presence or absence of the regular
+ * expression global (`g`) flag, or `null` if no matches are found. If the
+ * `g` flag is used, all results matching the complete regular expression
+ * will be returned, but capturing groups will not. If the `g` flag is not
+ * used, only the first complete match and its related capturing groups are
+ * returned.
+ *
+ * If specified, the *index* looks up a value of the resulting match. If
+ * *index* is a number, the corresponding index of the result array is
+ * returned. If *index* is a string, the value of the corresponding
+ * named capture group is returned, or `null` if there is no such group.
+ * @param {*} value The input string value.
+ * @param {*} regexp The regular expression to match against.
+ * @param {number|string} index The index into the match result array
+ * or capture group.
+ * @return {string|string[]} The match result.
+ */
+ match: (value, regexp, index) => {
+ const m = value == null ? value : String(value).match(regexp);
+ return index == null || m == null ? m
+ : typeof index === 'number' ? m[index]
+ : m.groups ? m.groups[index]
+ : null;
+ },
+
+ /**
+ * Returns the Unicode normalization form of the string *value*.
+ * @param {*} value The input value to normalize.
+ * @param {string} form The Unicode normalization form, one of
+ * `'NFC'` (default, canonical decomposition, followed by canonical
+ * composition), `'NFD'` (canonical decomposition), `'NFKC'` (compatibility
+ * decomposition, followed by canonical composition),
+ * or `'NFKD'` (compatibility decomposition).
+ * @return {string} The normalized string value.
+ */
+ normalize: (value, form) => value == null ? value
+ : String(value).normalize(form),
+
+ /**
+ * Pad a string *value* with a given *fill* string (applied from the end of
+ * *value* and repeated, if needed) so that the resulting string reaches a
+ * given *length*.
+ * @param {*} value The input value to pad.
+ * @param {number} length The length of the resulting string once the
+ * *value* string has been padded. If the length is lower than
+ * `value.length`, the *value* string will be returned as-is.
+ * @param {string} [fill] The string to pad the *value* string with
+ * (default `''`). If *fill* is too long to stay within the target
+ * *length*, it will be truncated: for left-to-right languages the
+ * left-most part and for right-to-left languages the right-most will
+ * be applied.
+ * @return {string} The padded string.
+ */
+ padend: (value, length, fill) => value == null ? value
+ : String(value).padEnd(length, fill),
+
+ /**
+ * Pad a string *value* with a given *fill* string (applied from the start
+ * of *value* and repeated, if needed) so that the resulting string reaches
+ * a given *length*.
+ * @param {*} value The input value to pad.
+ * @param {number} length The length of the resulting string once the
+ * *value* string has been padded. If the length is lower than
+ * `value.length`, the *value* string will be returned as-is.
+ * @param {string} [fill] The string to pad the *value* string with
+ * (default `''`). If *fill* is too long to stay within the target
+ * *length*, it will be truncated: for left-to-right languages the
+ * left-most part and for right-to-left languages the right-most will
+ * be applied.
+ * @return {string} The padded string.
+ */
+ padstart: (value, length, fill) => value == null ? value
+ : String(value).padStart(length, fill),
+
+ /**
+ * Returns the string *value* converted to upper case.
+ * @param {*} value The input string value.
+ * @return {string} The upper case string.
+ */
+ upper: (value) => value == null ? value : String(value).toUpperCase(),
+
+ /**
+ * Returns the string *value* converted to lower case.
+ * @param {*} value The input string value.
+ * @return {string} The lower case string.
+ */
+ lower: (value) => value == null ? value : String(value).toLowerCase(),
+
+ /**
+ * Returns a new string which contains the specified *number* of copies of
+ * the *value* string concatenated together.
+ * @param {*} value The input string to repeat.
+ * @param {*} number An integer between `0` and `+Infinity`, indicating the
+ * number of times to repeat the string.
+ * @return {string} The repeated string.
+ */
+ repeat: (value, number) => value == null ? value
+ : String(value).repeat(number),
+
+ /**
+ * Returns a new string with some or all matches of a *pattern* replaced by
+ * a *replacement*. The *pattern* can be a string or a regular expression,
+ * and the *replacement* must be a string. If *pattern* is a string, only
+ * the first occurrence will be replaced; to make multiple replacements, use
+ * a regular expression *pattern* with a `g` (global) flag.
+ * @param {*} value The input string value.
+ * @param {*} pattern The pattern string or regular expression to replace.
+ * @param {*} replacement The replacement string to use.
+ * @return {string} The string with patterns replaced.
+ */
+ replace: (value, pattern, replacement) => value == null ? value
+ : String(value).replace(pattern, String(replacement)),
+
+ /**
+ * Divides a string *value* into an ordered list of substrings based on a
+ * *separator* pattern, puts these substrings into an array, and returns the
+ * array.
+ * @param {*} value The input string value.
+ * @param {*} separator A string or regular expression pattern describing
+ * where each split should occur.
+ * @param {number} [limit] An integer specifying a limit on the number of
+ * substrings to be included in the array.
+ * @return {string[]}
+ */
+ split: (value, separator, limit) => value == null ? []
+ : String(value).split(separator, limit),
+
+ /**
+ * Determines whether a string *value* starts with the characters of a
+ * specified *search* string, returning `true` or `false` as appropriate.
+ * @param {*} value The input string value.
+ * @param {string} search The search string to test for.
+ * @param {number} [position=0] The position in the *value* string at which
+ * to begin searching (default `0`).
+ * @return {boolean} True if the string starts with the search pattern,
+ * false otherwise.
+ */
+ startswith: (value, search, position) => value == null ? false
+ : String(value).startsWith(search, position),
+
+ /**
+ * Returns the part of the string *value* between the *start* and *end*
+ * indexes, or to the end of the string.
+ * @param {*} value The input string value.
+ * @param {number} [start=0] The index of the first character to include in
+ * the returned substring (default `0`).
+ * @param {number} [end] The index of the first character to exclude from
+ * the returned substring (default `value.length`).
+ * @return {string} The substring.
+ */
+ substring: (value, start, end) => value == null ? value
+ : String(value).substring(start, end),
+
+ /**
+ * Returns a new string with whitespace removed from both ends of the input
+ * *value* string. Whitespace in this context is all the whitespace
+ * characters (space, tab, no-break space, etc.) and all the line terminator
+ * characters (LF, CR, etc.).
+ * @param {*} value The input string value to trim.
+ * @return {string} The trimmed string.
+ */
+ trim: (value) => value == null ? value : String(value).trim()
+};
diff --git a/src/op/index.js b/src/op/index.js
index a5a0f83e..b4c032b6 100644
--- a/src/op/index.js
+++ b/src/op/index.js
@@ -1,13 +1,11 @@
-import aggregateFunctions from './aggregate-functions';
-import windowFunctions from './window-functions';
-import functions from './functions';
-import has from '../util/has';
+import aggregateFunctions from './aggregate-functions.js';
+import windowFunctions from './window-functions.js';
+import functions from './functions/index.js';
+import has from '../util/has.js';
-export {
- functions,
- aggregateFunctions,
- windowFunctions
-};
+export { default as aggregateFunctions } from './aggregate-functions.js';
+export { default as windowFunctions } from './window-functions.js';
+export { default as functions } from './functions/index.js';
/**
* Check if an aggregate function with the given name exists.
@@ -39,8 +37,8 @@ export function hasWindow(name) {
/**
* Get an aggregate function definition.
* @param {string} name The name of the aggregate function.
- * @return {AggregateDef} The aggregate function definition,
- * or undefined if not found.
+ * @return {import('./aggregate-functions.js').AggregateDef}
+ * The aggregate function definition, or undefined if not found.
*/
export function getAggregate(name) {
return hasAggregate(name) && aggregateFunctions[name];
@@ -49,8 +47,8 @@ export function getAggregate(name) {
/**
* Get a window function definition.
* @param {string} name The name of the window function.
- * @return {WindowDef} The window function definition,
- * or undefined if not found.
+ * @return {import('./window-functions.js').WindowDef}
+ * The window function definition, or undefined if not found.
*/
export function getWindow(name) {
return hasWindow(name) && windowFunctions[name];
@@ -63,4 +61,4 @@ export function getWindow(name) {
*/
export function getFunction(name) {
return hasFunction(name) && functions[name];
-}
\ No newline at end of file
+}
diff --git a/src/op/op-api.js b/src/op/op-api.js
index e5c7ca6b..534846b6 100644
--- a/src/op/op-api.js
+++ b/src/op/op-api.js
@@ -1,5 +1,33 @@
-import functions from './functions';
-import op from './op';
+import functions from './functions/index.js';
+import toArray from '../util/to-array.js';
+import toString from '../util/to-string.js';
+
+export class Op {
+ constructor(name, fields, params) {
+ this.name = name;
+ this.fields = fields;
+ this.params = params;
+ }
+ toString() {
+ const args = [
+ ...this.fields.map(f => `d[${toString(f)}]`),
+ ...this.params.map(toString)
+ ];
+ return `d => op.${this.name}(${args})`;
+ }
+ toObject() {
+ return { expr: this.toString(), func: true };
+ }
+}
+
+/**
+ * @param {string} name
+ * @param {any | any[]} [fields]
+ * @param {any | any[]} [params]
+ */
+export function op(name, fields = [], params = []) {
+ return new Op(name, toArray(fields), toArray(params));
+}
export const any = (field) => op('any', field);
export const count = () => op('count');
@@ -10,7 +38,7 @@ export const object_agg = (key, value) => op('object_agg', [key, value]);
export const entries_agg = (key, value) => op('entries_agg', [key, value]);
/**
- * @typedef {import('../table/transformable').Struct} Struct
+ * @typedef {import('../table/types.js').Struct} Struct
*/
/**
@@ -36,47 +64,53 @@ export default {
/**
* Aggregate function returning an arbitrary observed value.
- * @param {*} field The data field.
- * @return {*} An arbitrary observed value.
+ * @template T
+ * @param {T} field The data field.
+ * @return {T} An arbitrary observed value.
*/
any,
/**
* Aggregate function to collect an array of values.
- * @param {*} field The data field.
- * @return {Array} A list of values.
+ * @template T
+ * @param {T} field The data field.
+ * @return {Array} A list of values.
*/
array_agg,
/**
* Aggregate function to collect an array of distinct (unique) values.
- * @param {*} field The data field.
- * @return {Array} An array of unique values.
+ * @template T
+ * @param {T} field The data field.
+ * @return {Array} An array of unique values.
*/
array_agg_distinct,
/**
* Aggregate function to create an object given input key and value fields.
- * @param {*} key The object key field.
- * @param {*} value The object value field.
- * @return {Struct} An object of key-value pairs.
+ * @template K, V
+ * @param {K} key The object key field.
+ * @param {V} value The object value field.
+ * @return {Record} An object of key-value pairs.
*/
object_agg,
/**
* Aggregate function to create a Map given input key and value fields.
- * @param {*} key The object key field.
- * @param {*} value The object value field.
- * @return {Map} A Map of key-value pairs.
+ * @template K, V
+ * @param {K} key The object key field.
+ * @param {V} value The object value field.
+ * @return {Map} A Map of key-value pairs.
*/
map_agg,
/**
* Aggregate function to create an array in the style of Object.entries()
* given input key and value fields.
- * @param {*} key The object key field.
- * @param {*} value The object value field.
- * @return {[[any, any]]} An array of [key, value] arrays.
+ * @template K, V
+ * @param {K} key The object key field.
+ * @param {V} value The object value field.
+ * @return {[K, V][]} An array of [key, value] arrays.
*/
entries_agg,
@@ -86,6 +120,7 @@ export default {
* @param {*} field The data field.
* @return {number} The count of valid values.
*/
+ // @ts-ignore
valid: (field) => op('valid', field),
/**
@@ -94,6 +129,7 @@ export default {
* @param {*} field The data field.
* @return {number} The count of invalid values.
*/
+ // @ts-ignore
invalid: (field) => op('invalid', field),
/**
@@ -101,20 +137,24 @@ export default {
* @param {*} field The data field.
* @return {number} The count of distinct values.
*/
+ // @ts-ignore
distinct: (field) => op('distinct', field),
/**
* Aggregate function to determine the mode (most frequent) value.
- * @param {*} field The data field.
- * @return {number} The mode value.
+ * @template T
+ * @param {T} field The data field.
+ * @return {T} The mode value.
*/
+ // @ts-ignore
mode: (field) => op('mode', field),
/**
* Aggregate function to sum values.
- * @param {string} field The data field.
+ * @param {*} field The data field.
* @return {number} The sum of the values.
*/
+ // @ts-ignore
sum: (field) => op('sum', field),
/**
@@ -122,6 +162,7 @@ export default {
* @param {*} field The data field.
* @return {number} The product of the values.
*/
+ // @ts-ignore
product: (field) => op('product', field),
/**
@@ -129,6 +170,7 @@ export default {
* @param {*} field The data field.
* @return {number} The mean (average) of the values.
*/
+ // @ts-ignore
mean: (field) => op('mean', field),
/**
@@ -136,6 +178,7 @@ export default {
* @param {*} field The data field.
* @return {number} The average (mean) of the values.
*/
+ // @ts-ignore
average: (field) => op('average', field),
/**
@@ -143,6 +186,7 @@ export default {
* @param {*} field The data field.
* @return {number} The sample variance of the values.
*/
+ // @ts-ignore
variance: (field) => op('variance', field),
/**
@@ -150,6 +194,7 @@ export default {
* @param {*} field The data field.
* @return {number} The population variance of the values.
*/
+ // @ts-ignore
variancep: (field) => op('variancep', field),
/**
@@ -157,6 +202,7 @@ export default {
* @param {*} field The data field.
* @return {number} The sample standard deviation of the values.
*/
+ // @ts-ignore
stdev: (field) => op('stdev', field),
/**
@@ -164,20 +210,25 @@ export default {
* @param {*} field The data field.
* @return {number} The population standard deviation of the values.
*/
+ // @ts-ignore
stdevp: (field) => op('stdevp', field),
/**
* Aggregate function for the minimum value.
- * @param {*} field The data field.
- * @return {number} The minimum value.
+ * @template T
+ * @param {T} field The data field.
+ * @return {T} The minimum value.
*/
+ // @ts-ignore
min: (field) => op('min', field),
/**
* Aggregate function for the maximum value.
- * @param {*} field The data field.
- * @return {number} The maximum value.
+ * @template T
+ * @param {T} field The data field.
+ * @return {T} The maximum value.
*/
+ // @ts-ignore
max: (field) => op('max', field),
/**
@@ -187,6 +238,7 @@ export default {
* @param {number} p The probability threshold.
* @return {number} The quantile value.
*/
+ // @ts-ignore
quantile: (field, p) => op('quantile', field, p),
/**
@@ -195,6 +247,7 @@ export default {
* @param {*} field The data field.
* @return {number} The median value.
*/
+ // @ts-ignore
median: (field) => op('median', field),
/**
@@ -203,6 +256,7 @@ export default {
* @param {*} field2 The second data field.
* @return {number} The sample covariance of the values.
*/
+ // @ts-ignore
covariance: (field1, field2) => op('covariance', [field1, field2]),
/**
@@ -211,6 +265,7 @@ export default {
* @param {*} field2 The second data field.
* @return {number} The population covariance of the values.
*/
+ // @ts-ignore
covariancep: (field1, field2) => op('covariancep', [field1, field2]),
/**
@@ -221,6 +276,7 @@ export default {
* @param {*} field2 The second data field.
* @return {number} The correlation between the field values.
*/
+ // @ts-ignore
corr: (field1, field2) => op('corr', [field1, field2]),
/**
@@ -235,13 +291,18 @@ export default {
* If specified, the maxbins and minstep arguments are ignored.
* @return {[number, number, number]} The bin [min, max, and step] values.
*/
- bins: (field, maxbins, nice, minstep) =>
- op('bins', field, [maxbins, nice, minstep]),
+ // @ts-ignore
+ bins: (field, maxbins, nice, minstep, step) => op(
+ 'bins',
+ field,
+ [maxbins, nice, minstep, step]
+ ),
/**
* Window function to assign consecutive row numbers, starting from 1.
* @return {number} The row number value.
*/
+ // @ts-ignore
row_number: () => op('row_number'),
/**
@@ -251,6 +312,7 @@ export default {
* rank 1, the third value is assigned rank 3.
* @return {number} The rank value.
*/
+ // @ts-ignore
rank: () => op('rank'),
/**
@@ -259,6 +321,7 @@ export default {
* indices: if the first two values tie, both will be assigned rank 1.5.
* @return {number} The peer-averaged rank value.
*/
+ // @ts-ignore
avg_rank: () => op('avg_rank'),
/**
@@ -268,6 +331,7 @@ export default {
* values tie for rank 1, the third value is assigned rank 2.
* @return {number} The dense rank value.
*/
+ // @ts-ignore
dense_rank: () => op('dense_rank'),
/**
@@ -275,6 +339,7 @@ export default {
* The percent is calculated as (rank - 1) / (group_size - 1).
* @return {number} The percentage rank value.
*/
+ // @ts-ignore
percent_rank: () => op('percent_rank'),
/**
@@ -282,6 +347,7 @@ export default {
* to each value in a group.
* @return {number} The cumulative distribution value.
*/
+ // @ts-ignore
cume_dist: () => op('cume_dist'),
/**
@@ -291,68 +357,83 @@ export default {
* @param {number} num The number of buckets for ntile calculation.
* @return {number} The quantile value.
*/
+ // @ts-ignore
ntile: (num) => op('ntile', null, num),
/**
* Window function to assign a value that precedes the current value by
* a specified number of positions. If no such value exists, returns a
* default value instead.
- * @param {*} field The data field.
+ * @template T
+ * @param {T} field The data field.
* @param {number} [offset=1] The lag offset from the current value.
- * @param {*} [defaultValue=undefined] The default value.
- * @return {*} The lagging value.
+ * @param {T} [defaultValue=undefined] The default value.
+ * @return {T} The lagging value.
*/
+ // @ts-ignore
lag: (field, offset, defaultValue) => op('lag', field, [offset, defaultValue]),
/**
* Window function to assign a value that follows the current value by
* a specified number of positions. If no such value exists, returns a
* default value instead.
- * @param {*} field The data field.
+ * @template T
+ * @param {T} field The data field.
* @param {number} [offset=1] The lead offset from the current value.
- * @param {*} [defaultValue=undefined] The default value.
- * @return {*} The leading value.
+ * @param {T} [defaultValue=undefined] The default value.
+ * @return {T} The leading value.
*/
+ // @ts-ignore
lead: (field, offset, defaultValue) => op('lead', field, [offset, defaultValue]),
/**
* Window function to assign the first value in a sliding window frame.
- * @param {*} field The data field.
- * @return {*} The first value in the current frame.
+ * @template T
+ * @param {T} field The data field.
+ * @return {T} The first value in the current frame.
*/
+ // @ts-ignore
first_value: (field) => op('first_value', field),
/**
* Window function to assign the last value in a sliding window frame.
- * @param {*} field The data field.
- * @return {*} The last value in the current frame.
+ * @template T
+ * @param {T} field The data field.
+ * @return {T} The last value in the current frame.
*/
+ // @ts-ignore
last_value: (field) => op('last_value', field),
/**
* Window function to assign the nth value in a sliding window frame
* (counting from 1), or undefined if no such value exists.
- * @param {*} field The data field.
+ * @template T
+ * @param {T} field The data field.
* @param {number} nth The nth position, starting from 1.
- * @return {*} The nth value in the current frame.
+ * @return {T} The nth value in the current frame.
*/
+ // @ts-ignore
nth_value: (field, nth) => op('nth_value', field, nth),
/**
* Window function to fill in missing values with preceding values.
- * @param {*} field The data field.
- * @param {*} [defaultValue=undefined] The default value.
- * @return {*} The current value if valid, otherwise the first preceding
+ * @template T
+ * @param {T} field The data field.
+ * @param {T} [defaultValue=undefined] The default value.
+ * @return {T} The current value if valid, otherwise the first preceding
* valid value. If no such value exists, returns the default value.
*/
+ // @ts-ignore
fill_down: (field, defaultValue) => op('fill_down', field, defaultValue),
/**
* Window function to fill in missing values with subsequent values.
- * @param {*} field The data field.
- * @param {*} [defaultValue=undefined] The default value.
- * @return {*} The current value if valid, otherwise the first subsequent
+ * @template T
+ * @param {T} field The data field.
+ * @param {T} [defaultValue=undefined] The default value.
+ * @return {T} The current value if valid, otherwise the first subsequent
* valid value. If no such value exists, returns the default value.
*/
+ // @ts-ignore
fill_up: (field, defaultValue) => op('fill_up', field, defaultValue)
-};
\ No newline at end of file
+};
diff --git a/src/op/op.js b/src/op/op.js
deleted file mode 100644
index d31986e8..00000000
--- a/src/op/op.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import toArray from '../util/to-array';
-import toString from '../util/to-string';
-
-export default function(name, fields = [], params = []) {
- return new Op(name, toArray(fields), toArray(params));
-}
-
-export class Op {
- constructor(name, fields, params) {
- this.name = name;
- this.fields = fields;
- this.params = params;
- }
- toString() {
- const args = [
- ...this.fields.map(f => `d[${toString(f)}]`),
- ...this.params.map(toString)
- ];
- return `d => op.${this.name}(${args})`;
- }
- toObject() {
- return { expr: this.toString(), func: true };
- }
-}
\ No newline at end of file
diff --git a/src/op/register.js b/src/op/register.js
new file mode 100644
index 00000000..560ed116
--- /dev/null
+++ b/src/op/register.js
@@ -0,0 +1,112 @@
+import aggregateFunctions from './aggregate-functions.js';
+import windowFunctions from './window-functions.js';
+import functions from './functions/index.js';
+import ops, { op } from './op-api.js';
+import { ROW_OBJECT } from '../expression/row-object.js';
+import error from '../util/error.js';
+import has from '../util/has.js';
+import toString from '../util/to-string.js';
+
+const onIllegal = (name, type) =>
+ error(`Illegal ${type} name: ${toString(name)}`);
+
+const onDefined = (name, type) =>
+ error(`The ${type} ${toString(name)} is already defined. Use override option?`);
+
+const onReserve = (name, type) =>
+ error(`The ${type} name ${toString(name)} is reserved and can not be overridden.`);
+
+function check(name, options, obj = ops, type = 'function') {
+ if (!name) onIllegal(name, type);
+ if (!options.override && has(obj, name)) onDefined(name, type);
+}
+
+function verifyFunction(name, def, object, options) {
+ return object[name] === def || check(name, options);
+}
+
+/**
+ * Register an aggregate or window operation.
+ * @param {string} name The name of the operation
+ * @param {AggregateDef|WindowDef} def The operation definition.
+ * @param {object} object The registry object to add the definition to.
+ * @param {RegisterOptions} [options] Registration options.
+ */
+function addOp(name, def, object, options = {}) {
+ if (verifyFunction(name, def, object, options)) return;
+ const [nf = 0, np = 0] = def.param; // num fields, num params
+ object[name] = def;
+ ops[name] = (...params) => op(
+ name,
+ params.slice(0, nf),
+ params.slice(nf, nf + np)
+ );
+}
+
+/**
+ * Register a custom aggregate function.
+ * @param {string} name The name to use for the aggregate function.
+ * @param {AggregateDef} def The aggregate operator definition.
+ * @param {RegisterOptions} [options] Function registration options.
+ * @throws If a function with the same name is already registered and
+ * the override option is not specified.
+ */
+export function addAggregateFunction(name, def, options) {
+ addOp(name, def, aggregateFunctions, options);
+}
+
+/**
+ * Register a custom window function.
+ * @param {string} name The name to use for the window function.
+ * @param {WindowDef} def The window operator definition.
+ * @param {RegisterOptions} [options] Function registration options.
+ * @throws If a function with the same name is already registered and
+ * the override option is not specified.
+ */
+export function addWindowFunction(name, def, options) {
+ addOp(name, def, windowFunctions, options);
+}
+
+/**
+ * Register a function for use within table expressions.
+ * If only a single argument is provided, it will be assumed to be a
+ * function and the system will try to extract its name.
+ * @param {string} name The name to use for the function.
+ * @param {Function} fn A standard JavaScript function.
+ * @param {RegisterOptions} [options] Function registration options.
+ * @throws If a function with the same name is already registered and
+ * the override option is not specified, or if no name is provided
+ * and the input function is anonymous.
+ */
+export function addFunction(name, fn, options = {}) {
+ if (arguments.length === 1) {
+ // @ts-ignore
+ fn = name;
+ name = fn.name;
+ if (name === '' || name === 'anonymous') {
+ error('Anonymous function provided, please include a name argument.');
+ } else if (name === ROW_OBJECT) {
+ onReserve(ROW_OBJECT, 'function');
+ }
+ }
+ if (verifyFunction(name, fn, functions, options)) return;
+ functions[name] = fn;
+ ops[name] = fn;
+}
+
+/**
+ * Aggregate function definition.
+ * @typedef {import('./aggregate-functions.js').AggregateDef} AggregateDef
+ */
+
+/**
+ * Window function definition.
+ * @typedef {import('./window-functions.js').WindowDef} WindowDef
+ */
+
+/**
+ * Options for registering new functions.
+ * @typedef {object} RegisterOptions
+ * @property {boolean} [override=false] Flag indicating if the added
+ * function can override an existing function with the same name.
+ */
diff --git a/src/op/window-functions.js b/src/op/window-functions.js
index b861538f..60710ba4 100644
--- a/src/op/window-functions.js
+++ b/src/op/window-functions.js
@@ -1,7 +1,7 @@
-import error from '../util/error';
-import isValid from '../util/is-valid';
-import noop from '../util/no-op';
-import NULL from '../util/null';
+import error from '../util/error.js';
+import isValid from '../util/is-valid.js';
+import noop from '../util/no-op.js';
+import NULL from '../util/null.js';
/**
* Initialize a window operator.
@@ -11,7 +11,7 @@ import NULL from '../util/null';
/**
* A storage object for the state of the window.
- * @typedef {import('../engine/window/window-state').default} WindowState
+ * @typedef {import('../verbs/window/window-state.js').default} WindowState
*/
/**
@@ -23,12 +23,12 @@ import NULL from '../util/null';
/**
* Initialize an aggregate operator.
- * @typedef {import('./aggregate-functions').AggregateInit} AggregateInit
+ * @typedef {import('./aggregate-functions.js').AggregateInit} AggregateInit
*/
/**
* Retrive an output value from an aggregate operator.
- * @typedef {import('./aggregate-functions').AggregateValue} AggregateValue
+ * @typedef {import('./aggregate-functions.js').AggregateValue} AggregateValue
*/
/**
@@ -47,7 +47,7 @@ import NULL from '../util/null';
/**
* Create a new aggregate operator instance.
- * @typedef {import('./aggregate-functions').AggregateCreate} AggregateCreate
+ * @typedef {import('./aggregate-functions.js').AggregateCreate} AggregateCreate
*/
/**
diff --git a/src/query/constants.js b/src/query/constants.js
deleted file mode 100644
index fd7591b4..00000000
--- a/src/query/constants.js
+++ /dev/null
@@ -1,17 +0,0 @@
-export const Expr = 'Expr';
-export const ExprList = 'ExprList';
-export const ExprNumber = 'ExprNumber';
-export const ExprObject = 'ExprObject';
-export const JoinKeys = 'JoinKeys';
-export const JoinValues = 'JoinValues';
-export const Options = 'Options';
-export const OrderbyKeys = 'OrderKeys';
-export const SelectionList = 'SelectionList';
-export const TableRef = 'TableRef';
-export const TableRefList = 'TableRefList';
-
-export const Descending = 'Descending';
-export const Query = 'Query';
-export const Selection = 'Selection';
-export const Verb = 'Verb';
-export const Window = 'Window';
\ No newline at end of file
diff --git a/src/query/query.js b/src/query/query.js
deleted file mode 100644
index c1b08a96..00000000
--- a/src/query/query.js
+++ /dev/null
@@ -1,190 +0,0 @@
-import Transformable from '../table/transformable';
-import { Query as QueryType } from './constants';
-import { Verb, Verbs } from './verb';
-
-/**
- * Create a new query instance. The query interface provides
- * a table-like verb API to construct a query that can be
- * serialized or evaluated against Arquero tables.
- * @param {string} [tableName] The name of the table to query. If
- * provided, will be used as the default input table to pull from
- * a provided catalog to run the query against.
- * @return {Query} A new builder instance.
- */
-export function query(tableName) {
- return new Query(null, null, tableName);
-}
-
-/**
- * Create a new query instance from a serialized object.
- * @param {object} object A serialized query representation, such as
- * those generated by query(...).toObject().
- * @returns {Query} The instantiated query instance.
- */
-export function queryFrom(object) {
- return Query.from(object);
-}
-
-/**
- * Model a query as a collection of serializble verbs.
- * Provides a table-like interface for constructing queries.
- */
-export default class Query extends Transformable {
-
- /**
- * Construct a new query instance.
- * @param {Verb[]} verbs An array of verb instances.
- * @param {object} [params] Optional query parameters, corresponding
- * to parameter references in table expressions.
- * @param {string} [table] Optional name of the table to query.
- */
- constructor(verbs, params, table) {
- super(params);
- this._verbs = verbs || [];
- this._table = table;
- }
-
- /**
- * Create a new query instance from the given serialized object.
- * @param {QueryObject} object A serialized query representation, such as
- * those generated by Query.toObject.
- * @returns {Query} The instantiated query.
- */
- static from({ verbs, table, params }) {
- return new Query(verbs.map(Verb.from), params, table);
- }
-
- /**
- * Provide an informative object string tag.
- */
- get [Symbol.toStringTag]() {
- if (!this._verbs) return 'Object'; // bail if called on prototype
- const ns = this._verbs.length;
- return `Query: ${ns} verbs` + (this._table ? ` on '${this._table}'` : '');
- }
-
- /**
- * Return the number of verbs in this query.
- */
- get length() {
- return this._verbs.length;
- }
-
- /**
- * Return the name of the table this query applies to.
- * @return {string} The name of the source table, or undefined.
- */
- get tableName() {
- return this._table;
- }
-
- /**
- * Get or set table expression parameter values.
- * If called with no arguments, returns the current parameter values
- * as an object. Otherwise, adds the provided parameters to this
- * query's parameter set and returns the table. Any prior parameters
- * with names matching the input parameters are overridden.
- * @param {object} values The parameter values.
- * @return {Query|object} The current parameter values (if called
- * with no arguments) or this query.
- */
- params(values) {
- if (arguments.length) {
- this._params = { ...this._params, ...values };
- return this;
- } else {
- return this._params;
- }
- }
-
- /**
- * Evaluate this query against a given table and catalog.
- * @param {Table} table The Arquero table to process.
- * @param {Function} catalog A table lookup function that accepts a table
- * name string as input and returns a corresponding Arquero table.
- * @returns {Table} The resulting Arquero table.
- */
- evaluate(table, catalog) {
- table = table || catalog(this._table);
- for (const verb of this._verbs) {
- table = verb.evaluate(table.params(this._params), catalog);
- }
- return table;
- }
-
- /**
- * Serialize this query as a JSON-compatible object. The resulting
- * object can be passed to Query.from to re-instantiate this query.
- * @returns {object} A JSON-compatible object representing this query.
- */
- toObject() {
- return serialize(this, 'toObject');
- }
-
- /**
- * Serialize this query as a JSON-compatible object. The resulting
- * object can be passed to Query.from to re-instantiate this query.
- * This method simply returns the result of toObject, but is provided
- * as a separate method to allow later customization of JSON export.
- * @returns {object} A JSON-compatible object representing this query.
- */
- toJSON() {
- return this.toObject();
- }
-
- /**
- * Serialize this query to a JSON-compatible abstract syntax tree.
- * All table expressions will be parsed and represented as AST instances
- * using a modified form of the Mozilla JavaScript AST format.
- * This method can be used to output parsed and serialized representations
- * to translate Arquero queries to alternative data processing platforms.
- * @returns {object} A JSON-compatible abstract syntax tree object.
- */
- toAST() {
- return serialize(this, 'toAST', { type: QueryType });
- }
-}
-
-/**
- * Abstract class representing a data table.
- * @typedef {import('../table/table').default} Table
- */
-
-/**
- * Serialized object representation of a query.
- * @typedef {object} QueryObject
- * @property {object[]} verbs An array of verb definitions.
- * @property {object} [params] An object of parameter values.
- * @property {string} [table] The name of the table to query.
- */
-
-function serialize(query, method, props) {
- return {
- ...props,
- verbs: query._verbs.map(verb => verb[method]()),
- ...(query._params ? { params: query._params } : null),
- ...(query._table ? { table: query._table } : null)
- };
-}
-
-function append(qb, verb) {
- return new Query(
- qb._verbs.concat(verb),
- qb._params,
- qb._table
- );
-}
-
-export function addQueryVerb(name, verb) {
- Query.prototype[name] = function(...args) {
- return append(this, verb(...args));
- };
-}
-
-// Internal verb handlers
-for (const name in Verbs) {
- const verb = Verbs[name];
- Query.prototype['__' + name] = function(qb, ...args) {
- return append(qb, verb(...args));
- };
-}
\ No newline at end of file
diff --git a/src/query/to-ast.js b/src/query/to-ast.js
deleted file mode 100644
index 6b143e93..00000000
--- a/src/query/to-ast.js
+++ /dev/null
@@ -1,160 +0,0 @@
-import error from '../util/error';
-import isArray from '../util/is-array';
-import isFunction from '../util/is-function';
-import isNumber from '../util/is-number';
-import isObject from '../util/is-object';
-import isString from '../util/is-string';
-import toArray from '../util/to-array';
-import parse from '../expression/parse';
-import { isSelection, toObject } from './util';
-
-import { Column } from '../expression/ast/constants';
-import {
- Descending,
- Expr,
- ExprList,
- ExprNumber,
- ExprObject,
- JoinKeys,
- JoinValues,
- Options,
- OrderbyKeys,
- Selection,
- SelectionList,
- TableRef,
- TableRefList,
- Window
-} from './constants';
-
-const Methods = {
- [Expr]: astExpr,
- [ExprList]: astExprList,
- [ExprNumber]: astExprNumber,
- [ExprObject]: astExprObject,
- [JoinKeys]: astJoinKeys,
- [JoinValues]: astJoinValues,
- [OrderbyKeys]: astExprList,
- [SelectionList]: astSelectionList
-};
-
-export default function(value, type, propTypes) {
- return type === TableRef ? astTableRef(value)
- : type === TableRefList ? value.map(astTableRef)
- : ast(toObject(value), type, propTypes);
-}
-
-function ast(value, type, propTypes) {
- return type === Options
- ? (value ? astOptions(value, propTypes) : value)
- : Methods[type](value);
-}
-
-function astOptions(value, types = {}) {
- const output = {};
- for (const key in value) {
- const prop = value[key];
- output[key] = types[key] ? ast(prop, types[key]) : prop;
- }
- return output;
-}
-
-function astParse(expr, opt) {
- return parse({ expr }, { ...opt, ast: true }).exprs[0];
-}
-
-function astColumn(name) {
- return { type: Column, name };
-}
-
-function astColumnIndex(index) {
- return { type: Column, index };
-}
-
-function astExprObject(obj, opt) {
- if (isString(obj)) {
- return astParse(obj, opt);
- }
-
- if (obj.expr) {
- let ast;
- if (obj.field === true) {
- ast = astColumn(obj.expr);
- } else if (obj.func === true) {
- ast = astExprObject(obj.expr, opt);
- }
- if (ast) {
- if (obj.desc) {
- ast = { type: Descending, expr: ast };
- }
- if (obj.window) {
- ast = { type: Window, expr: ast, ...obj.window };
- }
- return ast;
- }
- }
-
- return Object.keys(obj)
- .map(key => ({
- ...astExprObject(obj[key], opt),
- as: key
- }));
-}
-
-function astSelection(sel) {
- const type = Selection;
- return sel.all ? { type, operator: 'all' }
- : sel.not ? { type, operator: 'not', arguments: astExprList(sel.not) }
- : sel.range ? { type, operator: 'range', arguments: astExprList(sel.range) }
- : sel.matches ? { type, operator: 'matches', arguments: sel.matches }
- : error('Invalid input');
-}
-
-function astSelectionList(arr) {
- return toArray(arr).map(astSelectionItem).flat();
-}
-
-function astSelectionItem(val) {
- return isSelection(val) ? astSelection(val)
- : isNumber(val) ? astColumnIndex(val)
- : isString(val) ? astColumn(val)
- : isObject(val) ? Object.keys(val)
- .map(name => ({ type: Column, name, as: val[name] }))
- : error('Invalid input');
-}
-
-function astExpr(val) {
- return isSelection(val) ? astSelection(val)
- : isNumber(val) ? astColumnIndex(val)
- : isString(val) ? astColumn(val)
- : isObject(val) ? astExprObject(val)
- : error('Invalid input');
-}
-
-function astExprList(arr) {
- return toArray(arr).map(astExpr).flat();
-}
-
-function astExprNumber(val) {
- return isNumber(val) ? val : astExprObject(val);
-}
-
-function astJoinKeys(val) {
- return isArray(val)
- ? val.map(astExprList)
- : astExprObject(val, { join: true });
-}
-
-function astJoinValues(val) {
- return isArray(val)
- ? val.map((v, i) => i < 2
- ? astExprList(v)
- : astExprObject(v, { join: true })
- )
- : astExprObject(val, { join: true });
-}
-
-function astTableRef(value) {
- return value && isFunction(value.toAST)
- ? value.toAST()
- : value;
-}
\ No newline at end of file
diff --git a/src/query/util.js b/src/query/util.js
deleted file mode 100644
index 878c4967..00000000
--- a/src/query/util.js
+++ /dev/null
@@ -1,130 +0,0 @@
-import desc from '../helpers/desc';
-import field from '../helpers/field';
-import rolling from '../helpers/rolling';
-import { all, matches, not, range } from '../helpers/selection';
-import Query from './query';
-import error from '../util/error';
-import isArray from '../util/is-array';
-import isFunction from '../util/is-function';
-import isNumber from '../util/is-number';
-import isObject from '../util/is-object';
-import isString from '../util/is-string';
-import map from '../util/map-object';
-import toArray from '../util/to-array';
-
-function func(expr) {
- const f = d => d;
- f.toString = () => expr;
- return f;
-}
-
-export function getTable(catalog, ref) {
- ref = ref && isFunction(ref.query) ? ref.query() : ref;
- return ref && isFunction(ref.evaluate)
- ? ref.evaluate(null, catalog)
- : catalog(ref);
-}
-
-export function isSelection(value) {
- return isObject(value) && (
- isArray(value.all) ||
- isArray(value.matches) ||
- isArray(value.not) ||
- isArray(value.range)
- );
-}
-
-export function toObject(value) {
- return value && isFunction(value.toObject) ? value.toObject()
- : isFunction(value) ? { expr: String(value), func: true }
- : isArray(value) ? value.map(toObject)
- : isObject(value) ? map(value, _ => toObject(_))
- : value;
-}
-
-export function fromObject(value) {
- return isArray(value) ? value.map(fromObject)
- : !isObject(value) ? value
- : isArray(value.verbs) ? Query.from(value)
- : isArray(value.all) ? all()
- : isArray(value.range) ? range(...value.range)
- : isArray(value.match) ? matches(RegExp(...value.match))
- : isArray(value.not) ? not(value.not.map(toObject))
- : fromExprObject(value);
-}
-
-function fromExprObject(value) {
- let output = value;
- let expr = value.expr;
-
- if (expr != null) {
- if (value.field === true) {
- output = expr = field(expr);
- } else if (value.func === true) {
- output = expr = func(expr);
- }
-
- if (isObject(value.window)) {
- const { frame, peers } = value.window;
- output = expr = rolling(expr, frame, peers);
- }
-
- if (value.desc === true) {
- output = desc(expr);
- }
- }
-
- return value === output
- ? map(value, _ => fromObject(_))
- : output;
-}
-
-export function joinKeys(keys) {
- return isArray(keys) ? keys.map(parseJoinKeys)
- : keys;
-}
-
-function parseJoinKeys(keys) {
- const list = [];
-
- toArray(keys).forEach(param => {
- isNumber(param) ? list.push(param)
- : isString(param) ? list.push(field(param, null))
- : isObject(param) && param.expr ? list.push(param)
- : isFunction(param) ? list.push(param)
- : error(`Invalid key value: ${param+''}`);
- });
-
- return list;
-}
-
-export function joinValues(values) {
- return isArray(values)
- ? values.map(parseJoinValues)
- : values;
-}
-
-function parseJoinValues(values, index) {
- return index < 2 ? toArray(values) : values;
-}
-
-export function orderbyKeys(keys) {
- const list = [];
-
- keys.forEach(param => {
- const expr = param.expr != null ? param.expr : param;
- if (isObject(expr) && !isFunction(expr)) {
- for (const key in expr) {
- list.push(expr[key]);
- }
- } else {
- param = isNumber(expr) ? expr
- : isString(expr) ? field(param)
- : isFunction(expr) ? param
- : error(`Invalid orderby field: ${param+''}`);
- list.push(param);
- }
- });
-
- return list;
-}
\ No newline at end of file
diff --git a/src/query/verb.js b/src/query/verb.js
deleted file mode 100644
index e2321401..00000000
--- a/src/query/verb.js
+++ /dev/null
@@ -1,248 +0,0 @@
-import { Verb as VerbType } from './constants';
-
-import {
- fromObject,
- getTable,
- joinKeys,
- joinValues,
- orderbyKeys,
- toObject
-} from './util';
-
-import {
- Expr,
- ExprList,
- ExprNumber,
- ExprObject,
- JoinKeys,
- JoinValues,
- Options,
- OrderbyKeys,
- SelectionList,
- TableRef,
- TableRefList
-} from './constants';
-
-import toAST from './to-ast';
-
-/**
- * Model an Arquero verb as a serializable object.
- */
-export class Verb {
-
- /**
- * Construct a new verb instance.
- * @param {string} verb The verb name.
- * @param {object[]} schema Schema describing verb parameters.
- * @param {any[]} params Array of parameter values.
- */
- constructor(verb, schema = [], params = []) {
- this.verb = verb;
- this.schema = schema;
- schema.forEach((s, index) => {
- const type = s.type;
- const param = params[index];
- const value = type === JoinKeys ? joinKeys(param)
- : type === JoinValues ? joinValues(param)
- : type === OrderbyKeys ? orderbyKeys(param)
- : param;
- this[s.name] = value !== undefined ? value : s.default;
- });
- }
-
- /**
- * Create new verb instance from the given serialized object.
- * @param {object} object A serialized verb representation, such as
- * those generated by Verb.toObject.
- * @returns {Verb} The instantiated verb.
- */
- static from(object) {
- const verb = Verbs[object.verb];
- const params = (verb.schema || [])
- .map(({ name }) => fromObject(object[name]));
- return verb(...params);
- }
-
- /**
- * Evaluate this verb against a given table and catalog.
- * @param {Table} table The Arquero table to process.
- * @param {Function} catalog A table lookup function that accepts a table
- * name string as input and returns a corresponding Arquero table.
- * @returns {Table} The resulting Arquero table.
- */
- evaluate(table, catalog) {
- const params = this.schema.map(({ name, type }) => {
- const value = this[name];
- return type === TableRef ? getTable(catalog, value)
- : type === TableRefList ? value.map(t => getTable(catalog, t))
- : value;
- });
- return table[this.verb](...params);
- }
-
- /**
- * Serialize this verb as a JSON-compatible object. The resulting
- * object can be passed to Verb.from to re-instantiate this verb.
- * @returns {object} A JSON-compatible object representing this verb.
- */
- toObject() {
- const obj = { verb: this.verb };
- this.schema.forEach(({ name }) => {
- obj[name] = toObject(this[name]);
- });
- return obj;
- }
-
- /**
- * Serialize this verb to a JSON-compatible abstract syntax tree.
- * All table expressions will be parsed and represented as AST instances
- * using a modified form of the Mozilla JavaScript AST format.
- * This method can be used to output parsed and serialized representations
- * to translate Arquero verbs to alternative data processing platforms.
- * @returns {object} A JSON-compatible abstract syntax tree object.
- */
- toAST() {
- const obj = { type: VerbType, verb: this.verb };
- this.schema.forEach(({ name, type, props }) => {
- obj[name] = toAST(this[name], type, props);
- });
- return obj;
- }
-}
-
-/**
- * Verb parameter type.
- * @typedef {Expr|ExprList|ExprNumber|ExprObject|JoinKeys|JoinValues|Options|OrderbyKeys|SelectionList|TableRef|TableRefList} ParamType
- */
-
-/**
- * Verb parameter schema.
- * @typedef {object} ParamDef
- * @property {string} name The name of the parameter.
- * @property {ParamType} type The type of the parameter.
- * @property {{ [key: string]: ParamType }} [props] Types for non-literal properties.
- */
-
-/**
- * Create a new constructors.
- * @param {string} name The name of the verb.
- * @param {ParamDef[]} schema The verb parameter schema.
- * @return {Function} A verb constructor function.
- */
-export function createVerb(name, schema) {
- return Object.assign(
- (...params) => new Verb(name, schema, params),
- { schema }
- );
-}
-
-/**
- * A lookup table of verb classes.
- */
-export const Verbs = {
- count: createVerb('count', [
- { name: 'options', type: Options }
- ]),
- derive: createVerb('derive', [
- { name: 'values', type: ExprObject },
- { name: 'options', type: Options,
- props: { before: SelectionList, after: SelectionList }
- }
- ]),
- filter: createVerb('filter', [
- { name: 'criteria', type: ExprObject }
- ]),
- groupby: createVerb('groupby', [
- { name: 'keys', type: ExprList }
- ]),
- orderby: createVerb('orderby', [
- { name: 'keys', type: OrderbyKeys }
- ]),
- relocate: createVerb('relocate', [
- { name: 'columns', type: SelectionList },
- { name: 'options', type: Options,
- props: { before: SelectionList, after: SelectionList }
- }
- ]),
- rename: createVerb('rename', [
- { name: 'columns', type: SelectionList }
- ]),
- rollup: createVerb('rollup', [
- { name: 'values', type: ExprObject }
- ]),
- sample: createVerb('sample', [
- { name: 'size', type: ExprNumber },
- { name: 'options', type: Options, props: { weight: Expr } }
- ]),
- select: createVerb('select', [
- { name: 'columns', type: SelectionList }
- ]),
- ungroup: createVerb('ungroup'),
- unorder: createVerb('unorder'),
- reify: createVerb('reify'),
- dedupe: createVerb('dedupe', [
- { name: 'keys', type: ExprList, default: [] }
- ]),
- impute: createVerb('impute', [
- { name: 'values', type: ExprObject },
- { name: 'options', type: Options, props: { expand: ExprList } }
- ]),
- fold: createVerb('fold', [
- { name: 'values', type: ExprList },
- { name: 'options', type: Options }
- ]),
- pivot: createVerb('pivot', [
- { name: 'keys', type: ExprList },
- { name: 'values', type: ExprList },
- { name: 'options', type: Options }
- ]),
- spread: createVerb('spread', [
- { name: 'values', type: ExprList },
- { name: 'options', type: Options }
- ]),
- unroll: createVerb('unroll', [
- { name: 'values', type: ExprList },
- { name: 'options', type: Options, props: { drop: ExprList } }
- ]),
- lookup: createVerb('lookup', [
- { name: 'table', type: TableRef },
- { name: 'on', type: JoinKeys },
- { name: 'values', type: ExprList }
- ]),
- join: createVerb('join', [
- { name: 'table', type: TableRef },
- { name: 'on', type: JoinKeys },
- { name: 'values', type: JoinValues },
- { name: 'options', type: Options }
- ]),
- cross: createVerb('cross', [
- { name: 'table', type: TableRef },
- { name: 'values', type: JoinValues },
- { name: 'options', type: Options }
- ]),
- semijoin: createVerb('semijoin', [
- { name: 'table', type: TableRef },
- { name: 'on', type: JoinKeys }
- ]),
- antijoin: createVerb('antijoin', [
- { name: 'table', type: TableRef },
- { name: 'on', type: JoinKeys }
- ]),
- concat: createVerb('concat', [
- { name: 'tables', type: TableRefList }
- ]),
- union: createVerb('union', [
- { name: 'tables', type: TableRefList }
- ]),
- intersect: createVerb('intersect', [
- { name: 'tables', type: TableRefList }
- ]),
- except: createVerb('except', [
- { name: 'tables', type: TableRefList }
- ])
-};
-
-/**
- * Abstract class representing a data table.
- * @typedef {import('../table/table').default} Table
- */
diff --git a/src/register.js b/src/register.js
deleted file mode 100644
index 1c6f725c..00000000
--- a/src/register.js
+++ /dev/null
@@ -1,260 +0,0 @@
-import ColumnTable from './table/column-table';
-import aggregateFunctions from './op/aggregate-functions';
-import windowFunctions from './op/window-functions';
-import functions from './op/functions';
-import op from './op/op';
-import ops from './op/op-api';
-import Query, { addQueryVerb } from './query/query';
-import { Verbs, createVerb } from './query/verb';
-import { ROW_OBJECT } from './expression/row-object';
-import error from './util/error';
-import has from './util/has';
-import toString from './util/to-string';
-
-const onIllegal = (name, type) =>
- error(`Illegal ${type} name: ${toString(name)}`);
-
-const onDefined = (name, type) =>
- error(`The ${type} ${toString(name)} is already defined. Use override option?`);
-
-const onReserve = (name, type) =>
- error(`The ${type} name ${toString(name)} is reserved and can not be overridden.`);
-
-function check(name, options, obj = ops, type = 'function') {
- if (!name) onIllegal(name, type);
- if (!options.override && has(obj, name)) onDefined(name, type);
-}
-
-// -- Op Functions --------------------------------------------------
-
-function verifyFunction(name, def, object, options) {
- return object[name] === def || check(name, options);
-}
-
-/**
- * Register an aggregate or window operation.
- * @param {string} name The name of the operation
- * @param {AggregateDef|WindowDef} def The operation definition.
- * @param {object} object The registry object to add the definition to.
- * @param {RegisterOptions} [options] Registration options.
- */
-function addOp(name, def, object, options = {}) {
- if (verifyFunction(name, def, object, options)) return;
- const [nf = 0, np = 0] = def.param;
- object[name] = def;
- ops[name] = (...params) => op(
- name,
- params.slice(0, nf),
- params.slice(nf, nf + np)
- );
-}
-
-/**
- * Register a custom aggregate function.
- * @param {string} name The name to use for the aggregate function.
- * @param {AggregateDef} def The aggregate operator definition.
- * @param {RegisterOptions} [options] Function registration options.
- * @throws If a function with the same name is already registered and
- * the override option is not specified.
- */
-export function addAggregateFunction(name, def, options) {
- addOp(name, def, aggregateFunctions, options);
-}
-
-/**
- * Register a custom window function.
- * @param {string} name The name to use for the window function.
- * @param {WindowDef} def The window operator definition.
- * @param {RegisterOptions} [options] Function registration options.
- * @throws If a function with the same name is already registered and
- * the override option is not specified.
- */
-export function addWindowFunction(name, def, options) {
- addOp(name, def, windowFunctions, options);
-}
-
-/**
- * Register a function for use within table expressions.
- * If only a single argument is provided, it will be assumed to be a
- * function and the system will try to extract its name.
- * @param {string} name The name to use for the function.
- * @param {Function} fn A standard JavaScript function.
- * @param {RegisterOptions} [options] Function registration options.
- * @throws If a function with the same name is already registered and
- * the override option is not specified, or if no name is provided
- * and the input function is anonymous.
- */
-export function addFunction(name, fn, options = {}) {
- if (arguments.length === 1) {
- fn = name;
- name = fn.name;
- if (name === '' || name === 'anonymous') {
- error('Anonymous function provided, please include a name argument.');
- } else if (name === ROW_OBJECT) {
- onReserve(ROW_OBJECT, 'function');
- }
- }
- if (verifyFunction(name, fn, functions, options)) return;
- functions[name] = fn;
- ops[name] = fn;
-}
-
-// -- Table Methods and Verbs ---------------------------------------
-
-const proto = ColumnTable.prototype;
-
-/**
- * Reserved table/query methods that must not be overwritten.
- */
-let RESERVED;
-
-function addReserved(obj) {
- for (; obj; obj = Object.getPrototypeOf(obj)) {
- Object.getOwnPropertyNames(obj).forEach(name => RESERVED[name] = 1);
- }
-}
-
-function verifyTableMethod(name, fn, options) {
- const type = 'method';
-
- // exit early if duplicate re-assignment
- if (proto[name] && proto[name].fn === fn) return true;
-
- // initialize reserved properties to avoid overriding internals
- if (!RESERVED) {
- RESERVED = {};
- addReserved(proto);
- addReserved(Query.prototype);
- }
-
- // perform name checks
- if (RESERVED[name]) onReserve(name, type);
- if ((name + '')[0] === '_') onIllegal(name, type);
- check(name, options, proto, type);
-}
-
-/**
- * Register a new table method. A new method will be added to the column
- * table prototype. When invoked from a table, the registered method will
- * be invoked with the table as the first argument, followed by all the
- * provided arguments.
- * @param {string} name The name of the table method.
- * @param {Function} method The table method.
- * @param {RegisterOptions} options
- */
-export function addTableMethod(name, method, options = {}) {
- if (verifyTableMethod(name, method, options)) return;
- proto[name] = function(...args) { return method(this, ...args); };
- proto[name].fn = method;
-}
-
-/**
- * Register a new transformation verb.
- * @param {string} name The name of the verb.
- * @param {Function} method The verb implementation.
- * @param {ParamDef[]} params The verb parameter schema.
- * @param {RegisterOptions} options Function registration options.
- */
-export function addVerb(name, method, params, options = {}) {
- // register table method first
- // if that doesn't throw, add serializable verb entry
- addTableMethod(name, method, options);
- addQueryVerb(name, Verbs[name] = createVerb(name, params));
-}
-
-// -- Package Bundles -----------------------------------------------
-
-const PACKAGE = 'arquero_package';
-
-/**
- * Add an extension package of functions, table methods, and/or verbs.
- * @param {Package|PackageBundle} bundle The package of extensions.
- * @throws If package validation fails.
- */
-export function addPackage(bundle, options = {}) {
- const pkg = bundle && bundle[PACKAGE] || bundle;
- const parts = {
- functions: [
- (name, def, opt) => verifyFunction(name, def, functions, opt),
- addFunction
- ],
- aggregateFunctions: [
- (name, def, opt) => verifyFunction(name, def, aggregateFunctions, opt),
- addAggregateFunction
- ],
- windowFunctions: [
- (name, def, opt) => verifyFunction(name, def, windowFunctions, opt),
- addWindowFunction
- ],
- tableMethods: [
- verifyTableMethod,
- addTableMethod
- ],
- verbs: [
- (name, obj, opt) => verifyTableMethod(name, obj.method, opt),
- (name, obj, opt) => addVerb(name, obj.method, obj.params, opt)
- ]
- };
-
- function scan(index) {
- for (const key in parts) {
- const part = parts[key];
- const p = pkg[key];
- for (const name in p) part[index](name, p[name], options);
- }
- }
- scan(0); // first validate package, throw if validation fails
- scan(1); // then add package content
-}
-
-/**
- * Aggregate function definition.
- * @typedef {import('./op/aggregate-functions').AggregateDef} AggregateDef
- */
-
-/**
- * Window function definition.
- * @typedef {import('./op/window-functions').WindowDef} WindowDef
- */
-
-/**
- * Verb parameter definition.
- * @typedef {import('./query/verb').ParamDef} ParamDef
- */
-
-/**
- * Verb definition.
- * @typedef {object} VerbDef
- * @property {Function} method A function implementing the verb.
- * @property {ParamDef[]} params The verb parameter schema.
- */
-
-/**
- * Verb parameter definition.
- * @typedef {object} ParamDef
- * @property {string} name The verb parameter name.
- * @property {ParamType} type The verb parameter type.
- */
-
-/**
- * A package of op function and table method definitions.
- * @typedef {object} Package
- * @property {{[name: string]: Function}} [functions] Standard function entries.
- * @property {{[name: string]: AggregateDef}} [aggregateFunctions] Aggregate function entries.
- * @property {{[name: string]: WindowDef}} [windowFunctions] Window function entries.
- * @property {{[name: string]: Function}} [tableMethods] Table method entries.
- * @property {{[name: string]: VerbDef}} [verbs] Verb entries.
- */
-
-/**
- * An object containing an extension package.
- * @typedef {object} PackageBundle
- * @property {Package} arquero.package The package bundle.
- */
-
-/**
- * Options for registering new functions.
- * @typedef {object} RegisterOptions
- * @property {boolean} [override=false] Flag indicating if the added
- * function can override an existing function with the same name.
- */
\ No newline at end of file
diff --git a/src/table/bit-set.js b/src/table/BitSet.js
similarity index 99%
rename from src/table/bit-set.js
rename to src/table/BitSet.js
index 71d6823e..7cf546f7 100644
--- a/src/table/bit-set.js
+++ b/src/table/BitSet.js
@@ -4,7 +4,7 @@ const ALL = 0xFFFFFFFF;
/**
* Represent an indexable set of bits.
*/
-export default class BitSet {
+export class BitSet {
/**
* Instantiate a new BitSet instance.
* @param {number} size The number of bits.
@@ -162,4 +162,4 @@ export default class BitSet {
}
return this;
}
-}
\ No newline at end of file
+}
diff --git a/src/table/ColumnSet.js b/src/table/ColumnSet.js
new file mode 100644
index 00000000..1b1d49c2
--- /dev/null
+++ b/src/table/ColumnSet.js
@@ -0,0 +1,83 @@
+import has from '../util/has.js';
+
+/**
+ * Return a new column set instance.
+ * @param {import('./Table.js').Table} [table] A base table whose columns
+ * should populate the returned set. If unspecified, create an empty set.
+ * @return {ColumnSet} The column set.
+ */
+export function columnSet(table) {
+ return table
+ ? new ColumnSet({ ...table.data() }, table.columnNames())
+ : new ColumnSet();
+}
+
+/** An editable collection of named columns. */
+export class ColumnSet {
+ /**
+ * Create a new column set instance.
+ * @param {import('./types.js').ColumnData} [data] Initial column data.
+ * @param {string[]} [names] Initial column names.
+ */
+ constructor(data, names) {
+ this.data = data || {};
+ this.names = names || [];
+ }
+
+ /**
+ * Add a new column to this set and return the column values.
+ * @template {import('./types.js').ColumnType} T
+ * @param {string} name The column name.
+ * @param {T} values The column values.
+ * @return {T} The provided column values.
+ */
+ add(name, values) {
+ if (!this.has(name)) this.names.push(name + '');
+ return this.data[name] = values;
+ }
+
+ /**
+ * Test if this column set has a columns with the given name.
+ * @param {string} name A column name
+ * @return {boolean} True if this set contains a column with the given name,
+ * false otherwise.
+ */
+ has(name) {
+ return has(this.data, name);
+ }
+
+ /**
+ * Add a groupby specification to this column set.
+ * @param {import('./types.js').GroupBySpec} groups A groupby specification.
+ * @return {this} This column set.
+ */
+ groupby(groups) {
+ this.groups = groups;
+ return this;
+ }
+
+ /**
+ * Create a new table with the contents of this column set, using the same
+ * type as a given prototype table. The new table does not inherit the
+ * filter, groupby, or orderby state of the prototype.
+ * @template {import('./Table.js').Table} T
+ * @param {T} proto A prototype table
+ * @return {T} The new table.
+ */
+ new(proto) {
+ const { data, names, groups = null } = this;
+ return proto.create({ data, names, groups, filter: null, order: null });
+ }
+
+ /**
+ * Create a derived table with the contents of this column set, using the same
+ * type as a given prototype table. The new table will inherit the filter,
+ * groupby, and orderby state of the prototype.
+ * @template {import('./Table.js').Table} T
+ * @param {T} proto A prototype table
+ * @return {T} The new table.
+ */
+ derive(proto) {
+ return proto.create(this);
+ }
+}
diff --git a/src/table/ColumnTable.js b/src/table/ColumnTable.js
new file mode 100644
index 00000000..43320650
--- /dev/null
+++ b/src/table/ColumnTable.js
@@ -0,0 +1,848 @@
+import { Table } from './Table.js';
+import {
+ antijoin,
+ assign,
+ concat,
+ cross,
+ dedupe,
+ derive,
+ except,
+ filter,
+ fold,
+ groupby,
+ impute,
+ intersect,
+ join,
+ lookup,
+ orderby,
+ pivot,
+ reduce,
+ relocate,
+ rename,
+ rollup,
+ sample,
+ select,
+ semijoin,
+ slice,
+ spread,
+ ungroup,
+ union,
+ unorder,
+ unroll
+} from '../verbs/index.js';
+import { count } from '../op/op-api.js';
+import toArrow from '../arrow/to-arrow.js';
+import toArrowIPC from '../arrow/to-arrow-ipc.js';
+import toCSV from '../format/to-csv.js';
+import toHTML from '../format/to-html.js';
+import toJSON from '../format/to-json.js';
+import toMarkdown from '../format/to-markdown.js';
+import toArray from '../util/to-array.js';
+
+/**
+ * A data table with transformation verbs.
+ */
+export class ColumnTable extends Table {
+ /**
+ * Create a new table with additional columns drawn from one or more input
+ * tables. All tables must have the same numer of rows and are reified
+ * prior to assignment. In the case of repeated column names, input table
+ * columns overwrite existing columns.
+ * @param {...(Table|import('./types.js').ColumnData)} tables
+ * The tables to merge with this table.
+ * @return {this} A new table with merged columns.
+ * @example table.assign(table1, table2)
+ */
+ assign(...tables) {
+ return assign(this, ...tables);
+ }
+
+ /**
+ * Count the number of values in a group. This method is a shorthand
+ * for *rollup* with a count aggregate function.
+ * @param {import('./types.js').CountOptions} [options]
+ * Options for the count.
+ * @return {this} A new table with groupby and count columns.
+ * @example table.groupby('colA').count()
+ * @example table.groupby('colA').count({ as: 'num' })
+ */
+ count(options = {}) {
+ const { as = 'count' } = options;
+ return rollup(this, { [as]: count() });
+ }
+
+ /**
+ * Derive new column values based on the provided expressions. By default,
+ * new columns are added after (higher indices than) existing columns. Use
+ * the before or after options to place new columns elsewhere.
+ * @param {import('./types.js').ExprObject} values
+ * Object of name-value pairs defining the columns to derive. The input
+ * object should have output column names for keys and table expressions
+ * for values.
+ * @param {import('./types.js').DeriveOptions} [options]
+ * Options for dropping or relocating derived columns. Use either a before
+ * or after property to indicate where to place derived columns. Specifying
+ * both before and after is an error. Unlike the *relocate* verb, this
+ * option affects only new columns; updated columns with existing names
+ * are excluded from relocation.
+ * @return {this} A new table with derived columns added.
+ * @example table.derive({ sumXY: d => d.x + d.y })
+ * @example table.derive({ z: d => d.x * d.y }, { before: 'x' })
+ */
+ derive(values, options) {
+ return derive(this, values, options);
+ }
+
+ /**
+ * Filter a table to a subset of rows based on the input criteria.
+ * The resulting table provides a filtered view over the original data; no
+ * data copy is made. To create a table that copies only filtered data to
+ * new data structures, call *reify* on the output table.
+ * @param {import('./types.js').TableExpr} criteria
+ * Filter criteria as a table expression. Both aggregate and window
+ * functions are permitted, taking into account *groupby* or *orderby*
+ * settings.
+ * @return {this} A new table with filtered rows.
+ * @example table.filter(d => abs(d.value) < 5)
+ */
+ filter(criteria) {
+ return filter(this, criteria);
+ }
+
+ /**
+ * Extract rows with indices from start to end (end not included), where
+ * start and end represent per-group ordered row numbers in the table.
+ * @param {number} [start] Zero-based index at which to start extraction.
+ * A negative index indicates an offset from the end of the group.
+ * If start is undefined, slice starts from the index 0.
+ * @param {number} [end] Zero-based index before which to end extraction.
+ * A negative index indicates an offset from the end of the group.
+ * If end is omitted, slice extracts through the end of the group.
+ * @return {this} A new table with sliced rows.
+ * @example table.slice(1, -1)
+ */
+ slice(start, end) {
+ return slice(this, start, end);
+ }
+
+ /**
+ * Group table rows based on a set of column values.
+ * Subsequent operations that are sensitive to grouping (such as
+ * aggregate functions) will operate over the grouped rows.
+ * To undo grouping, use *ungroup*.
+ * @param {...import('./types.js').ExprList} keys
+ * Key column values to group by. The keys may be specified using column
+ * name strings, column index numbers, value objects with output column
+ * names for keys and table expressions for values, or selection helper
+ * functions.
+ * @return {this} A new table with grouped rows.
+ * @example table.groupby('colA', 'colB')
+ * @example table.groupby({ key: d => d.colA + d.colB })
+ */
+ groupby(...keys) {
+ return groupby(this, ...keys);
+ }
+
+ /**
+ * Order table rows based on a set of column values. Subsequent operations
+ * sensitive to ordering (such as window functions) will operate over sorted
+ * values. The resulting table provides an view over the original data,
+ * without any copying. To create a table with sorted data copied to new
+ * data strucures, call *reify* on the result of this method. To undo
+ * ordering, use *unorder*.
+ * @param {...import('./types.js').OrderKeys} keys
+ * Key values to sort by, in precedence order.
+ * By default, sorting is done in ascending order.
+ * To sort in descending order, wrap values using *desc*.
+ * If a string, order by the column with that name.
+ * If a number, order by the column with that index.
+ * If a function, must be a valid table expression; aggregate functions
+ * are permitted, but window functions are not.
+ * If an object, object values must be valid values parameters
+ * with output column names for keys and table expressions
+ * for values (the output names will be ignored).
+ * If an array, array values must be valid key parameters.
+ * @return {this} A new ordered table.
+ * @example table.orderby('a', desc('b'))
+ * @example table.orderby({ a: 'a', b: desc('b') )})
+ * @example table.orderby(desc(d => d.a))
+ */
+ orderby(...keys) {
+ return orderby(this, ...keys);
+ }
+
+ /**
+ * Relocate a subset of columns to change their positions, also
+ * potentially renaming them.
+ * @param {import('./types.js').Select} columns
+ * An ordered selection of columns to relocate.
+ * The input may consist of column name strings, column integer indices,
+ * rename objects with current column names as keys and new column names
+ * as values, or functions that take a table as input and returns a valid
+ * selection parameter (typically the output of selection helper functions
+ * such as *all*, *not*, or *range*).
+ * @param {import('./types.js').RelocateOptions} options
+ * Options for relocating. Must include either the before or after property
+ * to indicate where to place the relocated columns. Specifying both before
+ * and after is an error.
+ * @return {this} A new table with relocated columns.
+ * @example table.relocate(['colY', 'colZ'], { after: 'colX' })
+ * @example table.relocate(not('colB', 'colC'), { before: 'colA' })
+ * @example table.relocate({ colA: 'newA', colB: 'newB' }, { after: 'colC' })
+ */
+ relocate(columns, options) {
+ return relocate(this, toArray(columns), options);
+ }
+
+ /**
+ * Rename one or more columns, preserving column order.
+ * @param {...import('./types.js').Select} columns
+ * One or more rename objects with current column names as keys and new
+ * column names as values.
+ * @return {this} A new table with renamed columns.
+ * @example table.rename({ oldName: 'newName' })
+ * @example table.rename({ a: 'a2', b: 'b2' })
+ */
+ rename(...columns) {
+ return rename(this, ...columns);
+ }
+
+ /**
+ * Reduce a table, processing all rows to produce a new table.
+ * To produce standard aggregate summaries, use the rollup verb.
+ * This method allows the use of custom reducer implementations,
+ * for example to produce multiple rows for an aggregate.
+ * @param {import('../verbs/reduce/reducer.js').default} reducer
+ * The reducer to apply.
+ * @return {this} A new table of reducer outputs.
+ */
+ reduce(reducer) {
+ return reduce(this, reducer);
+ }
+
+ /**
+ * Rollup a table to produce an aggregate summary.
+ * Often used in conjunction with *groupby*.
+ * To produce counts only, *count* is a shortcut.
+ * @param {import('./types.js').ExprObject} [values]
+ * Object of name-value pairs defining aggregate output columns. The input
+ * object should have output column names for keys and table expressions
+ * for values. The expressions must be valid aggregate expressions: window
+ * functions are not allowed and column references must be arguments to
+ * aggregate functions.
+ * @return {this} A new table of aggregate summary values.
+ * @example table.groupby('colA').rollup({ mean: d => mean(d.colB) })
+ * @example table.groupby('colA').rollup({ mean: op.median('colB') })
+ */
+ rollup(values) {
+ return rollup(this, values);
+ }
+
+ /**
+ * Generate a table from a random sample of rows.
+ * If the table is grouped, performs a stratified sample by
+ * sampling from each group separately.
+ * @param {number | import('./types.js').TableExpr} size
+ * The number of samples to draw per group.
+ * If number-valued, the same sample size is used for each group.
+ * If function-valued, the input should be an aggregate table
+ * expression compatible with *rollup*.
+ * @param {import('./types.js').SampleOptions} [options]
+ * Options for sampling.
+ * @return {this} A new table with sampled rows.
+ * @example table.sample(50)
+ * @example table.sample(100, { replace: true })
+ * @example table.groupby('colA').sample(() => op.floor(0.5 * op.count()))
+ */
+ sample(size, options) {
+ return sample(this, size, options);
+ }
+
+ /**
+ * Select a subset of columns into a new table, potentially renaming them.
+ * @param {...import('./types.js').Select} columns
+ * An ordered selection of columns.
+ * The input may consist of column name strings, column integer indices,
+ * rename objects with current column names as keys and new column names
+ * as values, or functions that take a table as input and returns a valid
+ * selection parameter (typically the output of selection helper functions
+ * such as *all*, *not*, or *range*.).
+ * @return {this} A new table of selected columns.
+ * @example table.select('colA', 'colB')
+ * @example table.select(not('colB', 'colC'))
+ * @example table.select({ colA: 'newA', colB: 'newB' })
+ */
+ select(...columns) {
+ return select(this, ...columns);
+ }
+
+ /**
+ * Ungroup a table, removing any grouping criteria.
+ * Undoes the effects of *groupby*.
+ * @return {this} A new ungrouped table, or this table if not grouped.
+ * @example table.ungroup()
+ */
+ ungroup() {
+ return ungroup(this);
+ }
+
+ /**
+ * Unorder a table, removing any sorting criteria.
+ * Undoes the effects of *orderby*.
+ * @return {this} A new unordered table, or this table if not ordered.
+ * @example table.unorder()
+ */
+ unorder() {
+ return unorder(this);
+ }
+
+ // -- Cleaning Verbs ------------------------------------------------------
+
+ /**
+ * De-duplicate table rows by removing repeated row values.
+ * @param {...import('./types.js').ExprList} keys
+ * Key columns to check for duplicates.
+ * Two rows are considered duplicates if they have matching values for
+ * all keys. If keys are unspecified, all columns are used.
+ * The keys may be specified using column name strings, column index
+ * numbers, value objects with output column names for keys and table
+ * expressions for values, or selection helper functions.
+ * @return {this} A new de-duplicated table.
+ * @example table.dedupe()
+ * @example table.dedupe('a', 'b')
+ * @example table.dedupe({ abs: d => op.abs(d.a) })
+ */
+ dedupe(...keys) {
+ return dedupe(this, ...keys);
+ }
+
+ /**
+ * Impute missing values or rows. Accepts a set of column-expression pairs
+ * and evaluates the expressions to replace any missing (null, undefined,
+ * or NaN) values in the original column.
+ * If the expand option is specified, imputes new rows for missing
+ * combinations of values. All combinations of key values (a full cross
+ * product) are considered for each level of grouping (specified by
+ * *groupby*). New rows will be added for any combination
+ * of key and groupby values not already contained in the table. For all
+ * non-key and non-group columns the new rows are populated with imputation
+ * values (first argument) if specified, otherwise undefined.
+ * If the expand option is specified, any filter or orderby settings are
+ * removed from the output table, but groupby settings persist.
+ * @param {import('./types.js').ExprObject} values
+ * Object of name-value pairs for the column values to impute. The input
+ * object should have existing column names for keys and table expressions
+ * for values. The expressions will be evaluated to determine replacements
+ * for any missing values.
+ * @param {import('./types.js').ImputeOptions} [options] Imputation options.
+ * The expand property specifies a set of column values to consider for
+ * imputing missing rows. All combinations of expanded values are
+ * considered, and new rows are added for each combination that does not
+ * appear in the input table.
+ * @return {this} A new table with imputed values and/or rows.
+ * @example table.impute({ v: () => 0 })
+ * @example table.impute({ v: d => op.mean(d.v) })
+ * @example table.impute({ v: () => 0 }, { expand: ['x', 'y'] })
+ */
+ impute(values, options) {
+ return impute(this, values, options);
+ }
+
+ // -- Reshaping Verbs -----------------------------------------------------
+
+ /**
+ * Fold one or more columns into two key-value pair columns.
+ * The fold transform is an inverse of the *pivot* transform.
+ * The resulting table has two new columns, one containing the column
+ * names (named "key") and the other the column values (named "value").
+ * The number of output rows equals the original row count multiplied
+ * by the number of folded columns.
+ * @param {import('./types.js').ExprList} values The columns to fold.
+ * The columns may be specified using column name strings, column index
+ * numbers, value objects with output column names for keys and table
+ * expressions for values, or selection helper functions.
+ * @param {import('./types.js').FoldOptions} [options] Options for folding.
+ * @return {this} A new folded table.
+ * @example table.fold('colA')
+ * @example table.fold(['colA', 'colB'])
+ * @example table.fold(range(5, 8))
+ */
+ fold(values, options) {
+ return fold(this, values, options);
+ }
+
+ /**
+ * Pivot columns into a cross-tabulation.
+ * The pivot transform is an inverse of the *fold* transform.
+ * The resulting table has new columns for each unique combination
+ * of the provided *keys*, populated with the provided *values*.
+ * The provided *values* must be aggregates, as a single set of keys may
+ * include more than one row. If string-valued, the *any* aggregate is used.
+ * If only one *values* column is defined, the new pivoted columns will
+ * be named using key values directly. Otherwise, input value column names
+ * will be included as a component of the output column names.
+ * @param {import('./types.js').ExprList} keys
+ * Key values to map to new column names. The keys may be specified using
+ * column name strings, column index numbers, value objects with output
+ * column names for keys and table expressions for values, or selection
+ * helper functions.
+ * @param {import('./types.js').ExprList} values Output values for pivoted
+ * columns. Column references will be wrapped in an *any* aggregate. If
+ * object-valued, the input object should have output value names for keys
+ * and aggregate table expressions for values.
+ * @param {import('./types.js').PivotOptions} [options]
+ * Options for pivoting.
+ * @return {this} A new pivoted table.
+ * @example table.pivot('key', 'value')
+ * @example table.pivot(['keyA', 'keyB'], ['valueA', 'valueB'])
+ * @example table.pivot({ key: d => d.key }, { value: d => op.sum(d.value) })
+ */
+ pivot(keys, values, options) {
+ return pivot(this, keys, values, options);
+ }
+
+ /**
+ * Spread array elements into a set of new columns.
+ * Output columns are named based on the value key and array index.
+ * @param {import('./types.js').ExprList} values
+ * The column values to spread. The values may be specified using column
+ * name strings, column index numbers, value objects with output column
+ * names for keys and table expressions for values, or selection helper
+ * functions.
+ * @param {import('./types.js').SpreadOptions } [options]
+ * Options for spreading.
+ * @return {this} A new table with the spread columns added.
+ * @example table.spread({ a: d => op.split(d.text, '') })
+ * @example table.spread('arrayCol', { limit: 100 })
+ */
+ spread(values, options) {
+ return spread(this, values, options);
+ }
+
+ /**
+ * Unroll one or more array-valued columns into new rows.
+ * If more than one array value is used, the number of new rows
+ * is the smaller of the limit and the largest length.
+ * Values for all other columns are copied over.
+ * @param {import('./types.js').ExprList} values
+ * The column values to unroll. The values may be specified using column
+ * name strings, column index numbers, value objects with output column
+ * names for keys and table expressions for values, or selection helper
+ * functions.
+ * @param {import('./types.js').UnrollOptions} [options]
+ * Options for unrolling.
+ * @return {this} A new unrolled table.
+ * @example table.unroll('colA', { limit: 1000 })
+ */
+ unroll(values, options) {
+ return unroll(this, values, options);
+ }
+
+ // -- Joins ---------------------------------------------------------------
+
+ /**
+ * Lookup values from a secondary table and add them as new columns.
+ * A lookup occurs upon matching key values for rows in both tables.
+ * If the secondary table has multiple rows with the same key, only
+ * the last observed instance will be considered in the lookup.
+ * Lookup is similar to *join_left*, but with a simpler
+ * syntax and the added constraint of allowing at most one match only.
+ * @param {import('./types.js').TableRef} other
+ * The secondary table to look up values from.
+ * @param {import('./types.js').JoinKeys} [on]
+ * Lookup keys (column name strings or table expressions) for this table
+ * and the secondary table, respectively.
+ * @param {...import('./types.js').ExprList} values
+ * The column values to add from the secondary table. Can be column name
+ * strings or objects with column names as keys and table expressions as
+ * values.
+ * @return {this} A new table with lookup values added.
+ * @example table.lookup(other, ['key1', 'key2'], 'value1', 'value2')
+ */
+ lookup(other, on, ...values) {
+ return lookup(this, other, on, ...values);
+ }
+
+ /**
+ * Join two tables, extending the columns of one table with
+ * values from the other table. The current table is considered
+ * the "left" table in the join, and the new table input is
+ * considered the "right" table in the join. By default an inner
+ * join is performed, removing all rows that do not match the
+ * join criteria. To perform left, right, or full outer joins, use
+ * the *join_left*, *join_right*, or *join_full* methods, or provide
+ * an options argument.
+ * @param {import('./types.js').TableRef} other
+ * The other (right) table to join with.
+ * @param {import('./types.js').JoinPredicate} [on]
+ * The join criteria for matching table rows. If unspecified, the values of
+ * all columns with matching names are compared.
+ * If array-valued, a two-element array should be provided, containing
+ * the columns to compare for the left and right tables, respectively.
+ * If a one-element array or a string value is provided, the same
+ * column names will be drawn from both tables.
+ * If function-valued, should be a two-table table expression that
+ * returns a boolean value. When providing a custom predicate, note that
+ * join key values can be arrays or objects, and that normal join
+ * semantics do not consider null or undefined values to be equal (that is,
+ * null !== null). Use the op.equal function to handle these cases.
+ * @param {import('./types.js').JoinValues} [values]
+ * The columns to include in the join output.
+ * If unspecified, all columns from both tables are included; paired
+ * join keys sharing the same column name are included only once.
+ * If array-valued, a two element array should be provided, containing
+ * the columns to include for the left and right tables, respectively.
+ * Array input may consist of column name strings, objects with output
+ * names as keys and single-table table expressions as values, or the
+ * selection helper functions *all*, *not*, or *range*.
+ * If object-valued, specifies the key-value pairs for each output,
+ * defined using two-table table expressions.
+ * @param {import('./types.js').JoinOptions} [options]
+ * Options for the join.
+ * @return {this} A new joined table.
+ * @example table.join(other, ['keyL', 'keyR'])
+ * @example table.join(other, (a, b) => op.equal(a.keyL, b.keyR))
+ */
+ join(other, on, values, options) {
+ return join(this, other, on, values, options);
+ }
+
+ /**
+ * Perform a left outer join on two tables. Rows in the left table
+ * that do not match a row in the right table will be preserved.
+ * This is a convenience method with fixed options for *join*.
+ * @param {import('./types.js').TableRef} other
+ * The other (right) table to join with.
+ * @param {import('./types.js').JoinPredicate} [on]
+ * The join criteria for matching table rows.
+ * If unspecified, the values of all columns with matching names
+ * are compared.
+ * If array-valued, a two-element array should be provided, containing
+ * the columns to compare for the left and right tables, respectively.
+ * If a one-element array or a string value is provided, the same
+ * column names will be drawn from both tables.
+ * If function-valued, should be a two-table table expression that
+ * returns a boolean value. When providing a custom predicate, note that
+ * join key values can be arrays or objects, and that normal join
+ * semantics do not consider null or undefined values to be equal (that is,
+ * null !== null). Use the op.equal function to handle these cases.
+ * @param {import('./types.js').JoinValues} [values]
+ * he columns to include in the join output.
+ * If unspecified, all columns from both tables are included; paired
+ * join keys sharing the same column name are included only once.
+ * If array-valued, a two element array should be provided, containing
+ * the columns to include for the left and right tables, respectively.
+ * Array input may consist of column name strings, objects with output
+ * names as keys and single-table table expressions as values, or the
+ * selection helper functions *all*, *not*, or *range*.
+ * If object-valued, specifies the key-value pairs for each output,
+ * defined using two-table table expressions.
+ * @param {import('./types.js').JoinOptions} [options]
+ * Options for the join. With this method, any options will be
+ * overridden with `{left: true, right: false}`.
+ * @return {this} A new joined table.
+ * @example table.join_left(other, ['keyL', 'keyR'])
+ * @example table.join_left(other, (a, b) => op.equal(a.keyL, b.keyR))
+ */
+ join_left(other, on, values, options) {
+ const opt = { ...options, left: true, right: false };
+ return join(this, other, on, values, opt);
+ }
+
+ /**
+ * Perform a right outer join on two tables. Rows in the right table
+ * that do not match a row in the left table will be preserved.
+ * This is a convenience method with fixed options for *join*.
+ * @param {import('./types.js').TableRef} other
+ * The other (right) table to join with.
+ * @param {import('./types.js').JoinPredicate} [on]
+ * The join criteria for matching table rows.
+ * If unspecified, the values of all columns with matching names
+ * are compared.
+ * If array-valued, a two-element array should be provided, containing
+ * the columns to compare for the left and right tables, respectively.
+ * If a one-element array or a string value is provided, the same
+ * column names will be drawn from both tables.
+ * If function-valued, should be a two-table table expression that
+ * returns a boolean value. When providing a custom predicate, note that
+ * join key values can be arrays or objects, and that normal join
+ * semantics do not consider null or undefined values to be equal (that is,
+ * null !== null). Use the op.equal function to handle these cases.
+ * @param {import('./types.js').JoinValues} [values]
+ * The columns to include in the join output.
+ * If unspecified, all columns from both tables are included; paired
+ * join keys sharing the same column name are included only once.
+ * If array-valued, a two element array should be provided, containing
+ * the columns to include for the left and right tables, respectively.
+ * Array input may consist of column name strings, objects with output
+ * names as keys and single-table table expressions as values, or the
+ * selection helper functions *all*, *not*, or *range*.
+ * If object-valued, specifies the key-value pairs for each output,
+ * defined using two-table table expressions.
+ * @param {import('./types.js').JoinOptions} [options]
+ * Options for the join. With this method, any options will be overridden
+ * with `{left: false, right: true}`.
+ * @return {this} A new joined table.
+ * @example table.join_right(other, ['keyL', 'keyR'])
+ * @example table.join_right(other, (a, b) => op.equal(a.keyL, b.keyR))
+ */
+ join_right(other, on, values, options) {
+ const opt = { ...options, left: false, right: true };
+ return join(this, other, on, values, opt);
+ }
+
+ /**
+ * Perform a full outer join on two tables. Rows in either the left or
+ * right table that do not match a row in the other will be preserved.
+ * This is a convenience method with fixed options for *join*.
+ * @param {import('./types.js').TableRef} other
+ * The other (right) table to join with.
+ * @param {import('./types.js').JoinPredicate} [on]
+ * The join criteria for matching table rows.
+ * If unspecified, the values of all columns with matching names
+ * are compared.
+ * If array-valued, a two-element array should be provided, containing
+ * the columns to compare for the left and right tables, respectively.
+ * If a one-element array or a string value is provided, the same
+ * column names will be drawn from both tables.
+ * If function-valued, should be a two-table table expression that
+ * returns a boolean value. When providing a custom predicate, note that
+ * join key values can be arrays or objects, and that normal join
+ * semantics do not consider null or undefined values to be equal (that is,
+ * null !== null). Use the op.equal function to handle these cases.
+ * @param {import('./types.js').JoinValues} [values]
+ * The columns to include in the join output.
+ * If unspecified, all columns from both tables are included; paired
+ * join keys sharing the same column name are included only once.
+ * If array-valued, a two element array should be provided, containing
+ * the columns to include for the left and right tables, respectively.
+ * Array input may consist of column name strings, objects with output
+ * names as keys and single-table table expressions as values, or the
+ * selection helper functions *all*, *not*, or *range*.
+ * If object-valued, specifies the key-value pairs for each output,
+ * defined using two-table table expressions.
+ * @param {import('./types.js').JoinOptions} [options]
+ * Options for the join. With this method, any options will be overridden
+ * with `{left: true, right: true}`.
+ * @return {this} A new joined table.
+ * @example table.join_full(other, ['keyL', 'keyR'])
+ * @example table.join_full(other, (a, b) => op.equal(a.keyL, b.keyR))
+ */
+ join_full(other, on, values, options) {
+ const opt = { ...options, left: true, right: true };
+ return join(this, other, on, values, opt);
+ }
+
+ /**
+ * Produce the Cartesian cross product of two tables. The output table
+ * has one row for every pair of input table rows. Beware that outputs
+ * may be quite large, as the number of output rows is the product of
+ * the input row counts.
+ * This is a convenience method for *join* in which the
+ * join criteria is always true.
+ * @param {import('./types.js').TableRef} other
+ * The other (right) table to join with.
+ * @param {import('./types.js').JoinValues} [values]
+ * The columns to include in the output.
+ * If unspecified, all columns from both tables are included.
+ * If array-valued, a two element array should be provided, containing
+ * the columns to include for the left and right tables, respectively.
+ * Array input may consist of column name strings, objects with output
+ * names as keys and single-table table expressions as values, or the
+ * selection helper functions *all*, *not*, or *range*.
+ * If object-valued, specifies the key-value pairs for each output,
+ * defined using two-table table expressions.
+ * @param {import('./types.js').JoinOptions} [options]
+ * Options for the join.
+ * @return {this} A new joined table.
+ * @example table.cross(other)
+ * @example table.cross(other, [['leftKey', 'leftVal'], ['rightVal']])
+ */
+ cross(other, values, options) {
+ return cross(this, other, values, options);
+ }
+
+ /**
+ * Perform a semi-join, filtering the left table to only rows that
+ * match a row in the right table.
+ * @param {import('./types.js').TableRef} other
+ * The other (right) table to join with.
+ * @param {import('./types.js').JoinPredicate} [on]
+ * The join criteria for matching table rows.
+ * If unspecified, the values of all columns with matching names
+ * are compared.
+ * If array-valued, a two-element array should be provided, containing
+ * the columns to compare for the left and right tables, respectively.
+ * If a one-element array or a string value is provided, the same
+ * column names will be drawn from both tables.
+ * If function-valued, should be a two-table table expression that
+ * returns a boolean value. When providing a custom predicate, note that
+ * join key values can be arrays or objects, and that normal join
+ * semantics do not consider null or undefined values to be equal (that is,
+ * null !== null). Use the op.equal function to handle these cases.
+ * @return {this} A new filtered table.
+ * @example table.semijoin(other)
+ * @example table.semijoin(other, ['keyL', 'keyR'])
+ * @example table.semijoin(other, (a, b) => op.equal(a.keyL, b.keyR))
+ */
+ semijoin(other, on) {
+ return semijoin(this, other, on);
+ }
+
+ /**
+ * Perform an anti-join, filtering the left table to only rows that
+ * do *not* match a row in the right table.
+ * @param {import('./types.js').TableRef} other
+ * The other (right) table to join with.
+ * @param {import('./types.js').JoinPredicate} [on]
+ * The join criteria for matching table rows.
+ * If unspecified, the values of all columns with matching names
+ * are compared.
+ * If array-valued, a two-element array should be provided, containing
+ * the columns to compare for the left and right tables, respectively.
+ * If a one-element array or a string value is provided, the same
+ * column names will be drawn from both tables.
+ * If function-valued, should be a two-table table expression that
+ * returns a boolean value. When providing a custom predicate, note that
+ * join key values can be arrays or objects, and that normal join
+ * semantics do not consider null or undefined values to be equal (that is,
+ * null !== null). Use the op.equal function to handle these cases.
+ * @return {this} A new filtered table.
+ * @example table.antijoin(other)
+ * @example table.antijoin(other, ['keyL', 'keyR'])
+ * @example table.antijoin(other, (a, b) => op.equal(a.keyL, b.keyR))
+ */
+ antijoin(other, on) {
+ return antijoin(this, other, on);
+ }
+
+ // -- Set Operations ------------------------------------------------------
+
+ /**
+ * Concatenate multiple tables into a single table, preserving all rows.
+ * This transformation mirrors the UNION_ALL operation in SQL.
+ * Only named columns in this table are included in the output.
+ * @param {...import('./types.js').TableRefList} tables
+ * A list of tables to concatenate.
+ * @return {this} A new concatenated table.
+ * @example table.concat(other)
+ * @example table.concat(other1, other2)
+ * @example table.concat([other1, other2])
+ */
+ concat(...tables) {
+ return concat(this, ...tables);
+ }
+
+ /**
+ * Union multiple tables into a single table, deduplicating all rows.
+ * This transformation mirrors the UNION operation in SQL. It is
+ * similar to *concat* but suppresses duplicate rows with
+ * values identical to another row.
+ * Only named columns in this table are included in the output.
+ * @param {...import('./types.js').TableRefList} tables
+ * A list of tables to union.
+ * @return {this} A new unioned table.
+ * @example table.union(other)
+ * @example table.union(other1, other2)
+ * @example table.union([other1, other2])
+ */
+ union(...tables) {
+ return union(this, ...tables);
+ }
+
+ /**
+ * Intersect multiple tables, keeping only rows whose with identical
+ * values for all columns in all tables, and deduplicates the rows.
+ * This transformation is similar to a series of *semijoin*.
+ * calls, but additionally suppresses duplicate rows.
+ * @param {...import('./types.js').TableRefList} tables
+ * A list of tables to intersect.
+ * @return {this} A new filtered table.
+ * @example table.intersect(other)
+ * @example table.intersect(other1, other2)
+ * @example table.intersect([other1, other2])
+ */
+ intersect(...tables) {
+ return intersect(this, ...tables);
+ }
+
+ /**
+ * Compute the set difference with multiple tables, keeping only rows in
+ * this table that whose values do not occur in the other tables.
+ * This transformation is similar to a series of *anitjoin*
+ * calls, but additionally suppresses duplicate rows.
+ * @param {...import('./types.js').TableRefList} tables
+ * A list of tables to difference.
+ * @return {this} A new filtered table.
+ * @example table.except(other)
+ * @example table.except(other1, other2)
+ * @example table.except([other1, other2])
+ */
+ except(...tables) {
+ return except(this, ...tables);
+ }
+
+ // -- Table Output Formats ------------------------------------------------
+
+ /**
+ * Format this table as an Apache Arrow table.
+ * @param {import('../arrow/types.js').ArrowFormatOptions} [options]
+ * The Arrow formatting options.
+ * @return {import('apache-arrow').Table} An Apache Arrow table.
+ */
+ toArrow(options) {
+ return toArrow(this, options);
+ }
+
+ /**
+ * Format this table as binary data in the Apache Arrow IPC format.
+ * @param {import('../arrow/types.js').ArrowIPCFormatOptions} [options]
+ * The Arrow IPC formatting options.
+ * @return {Uint8Array} A new Uint8Array of Arrow-encoded binary data.
+ */
+ toArrowIPC(options) {
+ return toArrowIPC(this, options);
+ }
+
+ /**
+ * Format this table as a comma-separated values (CSV) string. Other
+ * delimiters, such as tabs or pipes ('|'), can be specified using
+ * the options argument.
+ * @param {import('../format/to-csv.js').CSVFormatOptions} [options]
+ * The CSV formatting options.
+ * @return {string} A delimited value string.
+ */
+ toCSV(options) {
+ return toCSV(this, options);
+ }
+
+ /**
+ * Format this table as an HTML table string.
+ * @param {import('../format/to-html.js').HTMLFormatOptions} [options]
+ * The HTML formatting options.
+ * @return {string} An HTML table string.
+ */
+ toHTML(options) {
+ return toHTML(this, options);
+ }
+
+ /**
+ * Format this table as a JavaScript Object Notation (JSON) string.
+ * @param {import('../format/to-json.js').JSONFormatOptions} [options]
+ * The JSON formatting options.
+ * @return {string} A JSON string.
+ */
+ toJSON(options) {
+ return toJSON(this, options);
+ }
+
+ /**
+ * Format this table as a GitHub-Flavored Markdown table string.
+ * @param {import('../format/to-markdown.js').MarkdownFormatOptions} [options]
+ * The Markdown formatting options.
+ * @return {string} A GitHub-Flavored Markdown table string.
+ */
+ toMarkdown(options) {
+ return toMarkdown(this, options);
+ }
+}
diff --git a/src/table/Table.js b/src/table/Table.js
new file mode 100644
index 00000000..734f8e15
--- /dev/null
+++ b/src/table/Table.js
@@ -0,0 +1,656 @@
+import { nest, regroup, reindex } from './regroup.js';
+import { rowObjectBuilder } from '../expression/row-object.js';
+import resolve, { all } from '../helpers/selection.js';
+import arrayType from '../util/array-type.js';
+import error from '../util/error.js';
+import isArrayType from '../util/is-array-type.js';
+import isNumber from '../util/is-number.js';
+import repeat from '../util/repeat.js';
+
+/**
+ * Base class representing a column-oriented data table.
+ */
+export class Table {
+ /**
+ * Instantiate a Table instance.
+ * @param {import('./types.js').ColumnData} columns
+ * An object mapping column names to values.
+ * @param {string[]} [names]
+ * An ordered list of column names.
+ * @param {import('./BitSet.js').BitSet} [filter]
+ * A filtering BitSet.
+ * @param {import('./types.js').GroupBySpec} [group]
+ * A groupby specification.
+ * @param {import('./types.js').RowComparator} [order]
+ * A row comparator function.
+ * @param {import('./types.js').Params} [params]
+ * An object mapping parameter names to values.
+ */
+ constructor(columns, names, filter, group, order, params) {
+ const data = Object.freeze({ ...columns });
+ names = names?.slice() ?? Object.keys(data);
+ const nrows = names.length ? data[names[0]].length : 0;
+ /**
+ * @private
+ * @type {readonly string[]}
+ */
+ this._names = Object.freeze(names);
+ /**
+ * @private
+ * @type {import('./types.js').ColumnData}
+ */
+ this._data = data;
+ /**
+ * @private
+ * @type {number}
+ */
+ this._total = nrows;
+ /**
+ * @private
+ * @type {number}
+ */
+ this._nrows = filter?.count() ?? nrows;
+ /**
+ * @private
+ * @type {import('./BitSet.js').BitSet}
+ */
+ this._mask = filter ?? null;
+ /**
+ * @private
+ * @type {import('./types.js').GroupBySpec}
+ */
+ this._group = group ?? null;
+ /**
+ * @private
+ * @type {import('./types.js').RowComparator}
+ */
+ this._order = order ?? null;
+ /**
+ * @private
+ * @type {import('./types.js').Params}
+ */
+ this._params = params;
+ /**
+ * @private
+ * @type {Uint32Array}
+ */
+ this._index = null;
+ /**
+ * @private
+ * @type {number[][] | Uint32Array[]}
+ */
+ this._partitions = null;
+ }
+
+ /**
+ * Create a new table with the same type as this table.
+ * The new table may have different data, filter, grouping, or ordering
+ * based on the values of the optional configuration argument. If a
+ * setting is not specified, it is inherited from the current table.
+ * @param {import('./types.js').CreateOptions} [options]
+ * Creation options for the new table.
+ * @return {this} A newly created table.
+ */
+ create({
+ data = undefined,
+ names = undefined,
+ filter = undefined,
+ groups = undefined,
+ order = undefined
+ } = {}) {
+ const f = filter !== undefined ? filter : this.mask();
+ // @ts-ignore
+ return new this.constructor(
+ data || this._data,
+ names || (!data ? this._names : null),
+ f,
+ groups !== undefined ? groups : regroup(this._group, filter && f),
+ order !== undefined ? order : this._order,
+ this._params
+ );
+ }
+
+ /**
+ * Get or set table expression parameter values.
+ * If called with no arguments, returns the current parameter values
+ * as an object. Otherwise, adds the provided parameters to this
+ * table's parameter set and returns the table. Any prior parameters
+ * with names matching the input parameters are overridden.
+ * @param {import('./types.js').Params} [values]
+ * The parameter values.
+ * @return {this|import('./types.js').Params}
+ * The current parameter values (if called with no arguments) or this table.
+ */
+ params(values) {
+ if (arguments.length) {
+ if (values) {
+ this._params = { ...this._params, ...values };
+ }
+ return this;
+ } else {
+ return this._params;
+ }
+ }
+
+ /**
+ * Provide an informative object string tag.
+ */
+ get [Symbol.toStringTag]() {
+ if (!this._names) return 'Object'; // bail if called on prototype
+ const nr = this.numRows();
+ const nc = this.numCols();
+ const plural = v => v !== 1 ? 's' : '';
+ return `Table: ${nc} col${plural(nc)} x ${nr} row${plural(nr)}`
+ + (this.isFiltered() ? ` (${this.totalRows()} backing)` : '')
+ + (this.isGrouped() ? `, ${this._group.size} groups` : '')
+ + (this.isOrdered() ? ', ordered' : '');
+ }
+
+ /**
+ * Indicates if the table has a filter applied.
+ * @return {boolean} True if filtered, false otherwise.
+ */
+ isFiltered() {
+ return !!this._mask;
+ }
+
+ /**
+ * Indicates if the table has a groupby specification.
+ * @return {boolean} True if grouped, false otherwise.
+ */
+ isGrouped() {
+ return !!this._group;
+ }
+
+ /**
+ * Indicates if the table has a row order comparator.
+ * @return {boolean} True if ordered, false otherwise.
+ */
+ isOrdered() {
+ return !!this._order;
+ }
+
+ /**
+ * Get the backing column data for this table.
+ * @return {import('./types.js').ColumnData}
+ * Object of named column instances.
+ */
+ data() {
+ return this._data;
+ }
+
+ /**
+ * Returns the filter bitset mask, if defined.
+ * @return {import('./BitSet.js').BitSet} The filter bitset mask.
+ */
+ mask() {
+ return this._mask;
+ }
+
+ /**
+ * Returns the groupby specification, if defined.
+ * @return {import('./types.js').GroupBySpec} The groupby specification.
+ */
+ groups() {
+ return this._group;
+ }
+
+ /**
+ * Returns the row order comparator function, if specified.
+ * @return {import('./types.js').RowComparator}
+ * The row order comparator function.
+ */
+ comparator() {
+ return this._order;
+ }
+
+ /**
+ * The total number of rows in this table, counting both
+ * filtered and unfiltered rows.
+ * @return {number} The number of total rows.
+ */
+ totalRows() {
+ return this._total;
+ }
+
+ /**
+ * The number of active rows in this table. This number may be
+ * less than the *totalRows* if the table has been filtered.
+ * @return {number} The number of rows.
+ */
+ numRows() {
+ return this._nrows;
+ }
+
+ /**
+ * The number of active rows in this table. This number may be
+ * less than the *totalRows* if the table has been filtered.
+ * @return {number} The number of rows.
+ */
+ get size() {
+ return this._nrows;
+ }
+
+ /**
+ * The number of columns in this table.
+ * @return {number} The number of columns.
+ */
+ numCols() {
+ return this._names.length;
+ }
+
+ /**
+ * Filter function invoked for each column name.
+ * @callback NameFilter
+ * @param {string} name The column name.
+ * @param {number} index The column index.
+ * @param {string[]} array The array of names.
+ * @return {boolean} Returns true to retain the column name.
+ */
+
+ /**
+ * The table column names, optionally filtered.
+ * @param {NameFilter} [filter] An optional filter function.
+ * If unspecified, all column names are returned.
+ * @return {string[]} An array of matching column names.
+ */
+ columnNames(filter) {
+ return filter ? this._names.filter(filter) : this._names.slice();
+ }
+
+ /**
+ * The column name at the given index.
+ * @param {number} index The column index.
+ * @return {string} The column name,
+ * or undefined if the index is out of range.
+ */
+ columnName(index) {
+ return this._names[index];
+ }
+
+ /**
+ * The column index for the given name.
+ * @param {string} name The column name.
+ * @return {number} The column index, or -1 if the name is not found.
+ */
+ columnIndex(name) {
+ return this._names.indexOf(name);
+ }
+
+ /**
+ * Get the column instance with the given name.
+ * @param {string} name The column name.
+ * @return {import('./types.js').ColumnType | undefined}
+ * The named column, or undefined if it does not exist.
+ */
+ column(name) {
+ return this._data[name];
+ }
+
+ /**
+ * Get the column instance at the given index position.
+ * @param {number} index The zero-based column index.
+ * @return {import('./types.js').ColumnType | undefined}
+ * The column, or undefined if it does not exist.
+ */
+ columnAt(index) {
+ return this._data[this._names[index]];
+ }
+
+ /**
+ * Get an array of values contained in a column. The resulting array
+ * respects any table filter or orderby criteria.
+ * @param {string} name The column name.
+ * @param {ArrayConstructor | import('./types.js').TypedArrayConstructor} [constructor=Array]
+ * The array constructor for instantiating the output array.
+ * @return {import('./types.js').DataValue[] | import('./types.js').TypedArray}
+ * The array of column values.
+ */
+ array(name, constructor = Array) {
+ const column = this.column(name);
+ const array = new constructor(this.numRows());
+ let idx = -1;
+ this.scan(row => array[++idx] = column.at(row), true);
+ return array;
+ }
+
+ /**
+ * Get the value for the given column and row.
+ * @param {string} name The column name.
+ * @param {number} [row=0] The row index, defaults to zero if not specified.
+ * @return {import('./types.js').DataValue} The table value at (column, row).
+ */
+ get(name, row = 0) {
+ const column = this.column(name);
+ return this.isFiltered() || this.isOrdered()
+ ? column.at(this.indices()[row])
+ : column.at(row);
+ }
+
+ /**
+ * Returns an accessor ("getter") function for a column. The returned
+ * function takes a row index as its single argument and returns the
+ * corresponding column value.
+ * @param {string} name The column name.
+ * @return {import('./types.js').ColumnGetter} The column getter function.
+ */
+ getter(name) {
+ const column = this.column(name);
+ const indices = this.isFiltered() || this.isOrdered() ? this.indices() : null;
+ if (indices) {
+ return row => column.at(indices[row]);
+ } else if (column) {
+ return row => column.at(row);
+ } else {
+ error(`Unrecognized column: ${name}`);
+ }
+ }
+
+ /**
+ * Returns an object representing a table row.
+ * @param {number} [row=0] The row index, defaults to zero if not specified.
+ * @return {object} A row object with named properties for each column.
+ */
+ object(row = 0) {
+ return objectBuilder(this)(row);
+ }
+
+ /**
+ * Returns an array of objects representing table rows.
+ * @param {import('./types.js').ObjectsOptions} [options]
+ * The options for row object generation.
+ * @return {object[]} An array of row objects.
+ */
+ objects(options = {}) {
+ const { grouped, limit, offset } = options;
+
+ // generate array of row objects
+ const names = resolve(this, options.columns || all());
+ const createRow = rowObjectBuilder(this, names);
+ const obj = [];
+ this.scan(
+ (row, data) => obj.push(createRow(row, data)),
+ true, limit, offset
+ );
+
+ // produce nested output as requested
+ if (grouped && this.isGrouped()) {
+ const idx = [];
+ this.scan(row => idx.push(row), true, limit, offset);
+ return nest(this, idx, obj, grouped);
+ }
+
+ return obj;
+ }
+
+ /**
+ * Returns an iterator over objects representing table rows.
+ * @return {Iterator