Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add improved TypeScript support. #355

Merged
merged 26 commits into from
Apr 6, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ee72568
chore: Update TypeScript, add some jsdoc typings, address TS errors.
jheer Apr 1, 2024
ce98e35
feat: Add initial TS types for JSON specs.
jheer Apr 1, 2024
02256d4
test: Add TypeScript spec tests.
jheer Apr 1, 2024
4d6afe3
feat: Incremental progress on JSON specification types.
jheer Apr 2, 2024
f4645bc
docs: add types for setPlot and plot attributes (#353)
domoritz Apr 2, 2024
278991c
Use jsconfig and fix types
domoritz Apr 2, 2024
1ed053d
Enable checks for spec js
domoritz Apr 2, 2024
955db5b
Fix type errors with ASTNode
domoritz Apr 2, 2024
1fb10d8
Disable js tests in spec package
domoritz Apr 2, 2024
a546021
fix: Remove invalid plot attribute.
jheer Apr 3, 2024
1c8a9e2
feat: Publish types from mosaic-spec.
jheer Apr 3, 2024
bbd54f8
feat: Add spec interactor typings.
jheer Apr 3, 2024
93ed3c6
feat: Document spec transform types.
jheer Apr 3, 2024
f94eb5f
feat: Document more spec typings.
jheer Apr 3, 2024
875bae2
feat: Types and documentation for plot attributes.
jheer Apr 4, 2024
a10aace
Merge branch 'jh/spec-types' of github.com:uwdata/mosaic into jh/spec…
jheer Apr 4, 2024
b2670eb
feat: Initial progress towards spec mark types.
jheer Apr 4, 2024
f20b208
feat: Add Area and Line mark types.
jheer Apr 4, 2024
decc112
feat: Add more mark typings.
jheer Apr 5, 2024
8c5f77c
feat: Add vector and hexgrid mark typings.
jheer Apr 5, 2024
f21548c
feat: Add geo mark typings.
jheer Apr 5, 2024
0de518d
feat: Add delaunay mark types.
jheer Apr 5, 2024
71b3fd3
feat: Add arrow and link mark types.
jheer Apr 5, 2024
8459980
chore: Add missing outer mark doc comments.
jheer Apr 5, 2024
fd10c13
feat: Add spec types for image, raster, regression.
jheer Apr 5, 2024
e4015ac
feat: Finish spec typings.
jheer Apr 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
- name: Build
run: npm run docs:build

- uses: actions/configure-pages@v4
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: docs/.vitepress/dist
Expand Down
13 changes: 13 additions & 0 deletions bin/prepare-examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,21 @@ import { parse } from 'yaml';
const specDir = join('specs', 'yaml');
const esmTestDir = join('specs', 'esm');
const jsonTestDir = join('specs', 'json');
const tsTestDir = join('specs', 'ts');

const docsDir = 'docs';
const yamlDocsDir = join(docsDir, 'public', 'specs', 'yaml');
const jsonDocsDir = join(docsDir, 'public', 'specs', 'json');
const esmDocsDir = join(docsDir, 'public', 'specs', 'esm');
const exampleDir = join(docsDir, 'examples');

const specToTS = spec => {
return `import { Spec } from '@uwdata/mosaic-spec';

export const spec : Spec = ${JSON.stringify(spec, 0, 2)};
`;
}

const files = await Promise.allSettled((await readdir(specDir))
.filter(name => extname(name) === '.yaml')
.map(async name => {
Expand All @@ -43,6 +51,11 @@ const files = await Promise.allSettled((await readdir(specDir))
resolve(jsonTestDir, `${base}.json`),
JSON.stringify(ast.toJSON(), 0, 2)
),
// write TS JSON spec to tests
writeFile(
resolve(tsTestDir, `${base}.ts`),
specToTS(spec)
),
// copy YAML file to docs
copyFile(file, resolve(yamlDocsDir, `${base}.yaml`)),
// write JSON spec to docs
Expand Down
866 changes: 247 additions & 619 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@
"eslint": "^8.57.0",
"eslint-plugin-jsdoc": "^48.2.1",
"lerna": "^8.1.2",
"mocha": "^10.3.0",
"mocha": "^10.4.0",
"nodemon": "^3.1.0",
"rimraf": "^5.0.5",
"timezone-mock": "^1.3.6",
"typescript": "^5.3.3",
"vite": "^5.1.6",
"vitepress": "1.0.0-rc.45",
"typescript": "^5.4.3",
"vite": "^5.2.6",
"vitepress": "1.0.1",
"yaml": "^2.4.1"
},
"workspaces": [
Expand Down
14 changes: 10 additions & 4 deletions packages/core/src/Coordinator.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ let _instance;

/**
* Set or retrieve the coordinator instance.
*
* @param {Coordinator} instance the coordinator instance to set
* @param {Coordinator} [instance] the coordinator instance to set
domoritz marked this conversation as resolved.
Show resolved Hide resolved
* @returns {Coordinator} the coordinator instance
*/
export function coordinator(instance) {
Expand Down Expand Up @@ -42,7 +41,15 @@ export class Coordinator {
return this._logger;
}

configure({ cache = true, consolidate = true, indexes = true }) {
/**
* Set configuration options for this coordinator.
* @param {object} [options] Configration options.
* @param {boolean} [options.cache=true] Boolean flag to enable/disable query caching.
* @param {boolean} [options.consolidate=true] Boolean flag to enable/disable query consolidation.
* @param {boolean|object} [options.indexes=true] Boolean flag to enable/disable
* automatic data cube indexes or an index options object.
*/
configure({ cache = true, consolidate = true, indexes = true } = {}) {
this.manager.cache(cache);
this.manager.consolidate(consolidate);
this.indexes = indexes;
Expand Down Expand Up @@ -116,7 +123,6 @@ export class Coordinator {

/**
* Connect a client to the coordinator.
*
* @param {import('./MosaicClient.js').MosaicClient} client the client to disconnect
*/
async connect(client) {
Expand Down
18 changes: 13 additions & 5 deletions packages/core/src/MosaicClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,45 +48,51 @@ export class MosaicClient {

/**
* Return an array of fields queried by this client.
* @returns {object[]|null} The fields to retrieve info for.
*/
fields() {
return null;
}

/**
* Called by the coordinator to set the field info for this client.
* @param {*} info The field info result.
* @returns {this}
*/
fieldInfo() {
fieldInfo(info) { // eslint-disable-line no-unused-vars
return this;
}

/**
* Return a query specifying the data needed by this client.
* @param {*} [filter] The filtering criteria to apply in the query.
* @returns {*} The client query
*/
query() {
query(filter) { // eslint-disable-line no-unused-vars
return null;
}

/**
* Called by the coordinator to inform the client that a query is pending.
* @returns {this}
*/
queryPending() {
return this;
}

/**
* Called by the coordinator to return a query result.
*
* @param {*} data the query result
* @param {*} data The query result.
* @returns {this}
*/
queryResult() {
queryResult(data) { // eslint-disable-line no-unused-vars
return this;
}

/**
* Called by the coordinator to report a query execution error.
* @param {*} error
* @returns {this}
*/
queryError(error) {
console.error(error);
Expand Down Expand Up @@ -116,6 +122,8 @@ export class MosaicClient {
/**
* Requests a client update.
* For example to (re-)render an interface component.
*
* @returns {this | Promise<any>}
*/
update() {
return this;
Expand Down
6 changes: 4 additions & 2 deletions packages/core/src/Param.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { distinct } from './util/distinct.js';
/**
* Test if a value is a Param instance.
* @param {*} x The value to test.
* @returns {boolean} True if the input is a Param, false otherwise.
* @returns {x is Param} True if the input is a Param, false otherwise.
*/
export function isParam(x) {
return x instanceof Param;
Expand Down Expand Up @@ -43,7 +43,9 @@ export class Param extends AsyncDispatch {
static array(values) {
if (values.some(v => isParam(v))) {
const p = new Param();
const update = () => p.update(values.map(v => isParam(v) ? v.value : v));
const update = () => {
p.update(values.map(v => isParam(v) ? v.value : v));
};
update();
values.forEach(v => isParam(v) ? v.addEventListener('value', update) : 0);
return p;
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/QueryConsolidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ function wait(callback) {
const method = typeof requestAnimationFrame !== 'undefined'
? requestAnimationFrame
: typeof setImmediate !== 'undefined' ? setImmediate : setTimeout;
// @ts-ignore
return method(callback);
}

Expand Down Expand Up @@ -82,7 +83,9 @@ function consolidationKey(query, cache) {
const sql = `${query}`;
if (query instanceof Query && !cache.get(sql)) {
if (
// @ts-ignore
query.orderby().length || query.where().length ||
// @ts-ignore
query.qualify().length || query.having().length
) {
// do not try to analyze if query includes clauses
Expand All @@ -97,9 +100,12 @@ function consolidationKey(query, cache) {
// queries may refer to *derived* columns as group by criteria
// we resolve these against the true grouping expressions
const groupby = query.groupby();
// @ts-ignore
if (groupby.length) {
const map = {}; // expression map (as -> expr)
// @ts-ignore
query.select().forEach(({ as, expr }) => map[as] = expr);
// @ts-ignore
q.$groupby(groupby.map(e => (e instanceof Ref && map[e.column]) || e));
}

Expand Down
13 changes: 7 additions & 6 deletions packages/core/src/Selection.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Param } from './Param.js';
/**
* Test if a value is a Selection instance.
* @param {*} x The value to test.
* @returns {boolean} True if the input is a Selection, false otherwise.
* @returns {x is Selection} True if the input is a Selection, false otherwise.
*/
export function isSelection(x) {
return x instanceof Selection;
Expand Down Expand Up @@ -76,7 +76,7 @@ export class Selection extends Param {

/**
* Create a cloned copy of this Selection instance.
* @returns {this} A clone of this selection.
* @returns {Selection} A clone of this selection.
*/
clone() {
const s = new Selection(this._resolver);
Expand All @@ -88,7 +88,7 @@ export class Selection extends Param {
* Create a clone of this Selection with clauses corresponding
* to the provided source removed.
* @param {*} source The clause source to remove.
* @returns {this} A cloned and updated Selection.
* @returns {Selection} A cloned and updated Selection.
*/
remove(source) {
const s = this.clone();
Expand Down Expand Up @@ -168,9 +168,9 @@ export class Selection extends Param {
* Upon value-typed updates, returns a dispatch queue filter function.
* The return value depends on the selection resolution strategy.
* @param {string} type The event type.
* @param {*} value The input event value.
* @returns {*} For value-typed events, returns a dispatch queue filter
* function. Otherwise returns null.
* @param {*} value The new event value that will be enqueued.
* @returns {(value: *) => boolean|null} For value-typed events,
* returns a dispatch queue filter function. Otherwise returns null.
*/
emitQueueFilter(type, value) {
return type === 'value'
Expand Down Expand Up @@ -285,5 +285,6 @@ export class SelectionResolver {
const source = value.active?.source;
return clauses => clauses.active?.source !== source;
}
return null;
}
}
4 changes: 2 additions & 2 deletions packages/core/src/connectors/wasm.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function wasmConnector(options = {}) {
/**
* Get the backing DuckDB-WASM instance.
* Will lazily initialize DuckDB-WASM if not already loaded.
* @returns {duckdb.AsyncDuckDB} The DuckDB-WASM instance.
* @returns {Promise<duckdb.AsyncDuckDB>} The DuckDB-WASM instance.
*/
async function getDuckDB() {
if (!db) await load();
Expand All @@ -32,7 +32,7 @@ export function wasmConnector(options = {}) {
/**
* Get the backing DuckDB-WASM connection.
* Will lazily initialize DuckDB-WASM if not already loaded.
* @returns {duckdb.AsyncDuckDBConnection} The DuckDB-WASM connection.
* @returns {Promise<duckdb.AsyncDuckDBConnection>} The DuckDB-WASM connection.
*/
async function getConnection() {
if (!con) await load();
Expand Down
7 changes: 4 additions & 3 deletions packages/core/src/util/AsyncDispatch.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class AsyncDispatch {
/**
* Add an event listener callback for the provided event type.
* @param {string} type The event type.
* @param {(value: *) => Promise?} callback The event handler
* @param {(value: *) => void | Promise} callback The event handler
* callback function to add. If the callback has already been
* added for the event type, this method has no effect.
*/
Expand All @@ -35,7 +35,7 @@ export class AsyncDispatch {
/**
* Remove an event listener callback for the provided event type.
* @param {string} type The event type.
* @param {(value: *) => Promise?} callback The event handler
* @param {(value: *) => void | Promise} callback The event handler
* callback function to remove.
*/
removeEventListener(type, callback) {
Expand Down Expand Up @@ -64,11 +64,12 @@ export class AsyncDispatch {
* This default implementation simply returns null, indicating that
* any other unemitted event values should be dropped (that is, all
* queued events are filtered)
* @param {string} type The event type.
* @param {*} value The new event value that will be enqueued.
* @returns {(value: *) => boolean|null} A dispatch queue filter
* function, or null if all unemitted event values should be filtered.
*/
emitQueueFilter() {
emitQueueFilter(type, value) { // eslint-disable-line no-unused-vars
// removes all pending items
return null;
}
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/util/convert-arrow.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { DataType } from 'apache-arrow';
* As sometimes multiple Arrow versions may be used simultaneously,
* we use a "duck typing" approach and check for a getChild function.
* @param {*} values The value to test
* @returns true if the value duck types as Apache Arrow data
* @returns {values is import('apache-arrow').Table} true if the value duck types as Apache Arrow data
*/
export function isArrowTable(values) {
return typeof values?.getChild === 'function';
Expand All @@ -28,7 +28,7 @@ export function convertArrowArrayType(type) {
* Large integers (BigInt) are converted to Float64 numbers.
* Fixed-point decimal values are convert to Float64 numbers.
* Otherwise, the default Arrow values are used.
* @param {*} type an Apache Arrow column type
* @param {DataType} type an Apache Arrow column type
* @returns a value conversion function
*/
export function convertArrowValue(type) {
Expand Down Expand Up @@ -112,10 +112,10 @@ const BASE32 = Array.from(
/**
* 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} v a fixed decimal value
* @param {Uint32Array & { signed: boolean }} v a fixed decimal value
* @param {number} scale a scale factor, corresponding to the
* number of fractional decimal digits in the fixed point value
* @returns the resulting number
* @returns {number} the resulting number
*/
function decimalToNumber(v, scale) {
const n = v.length;
Expand Down
7 changes: 4 additions & 3 deletions packages/core/src/util/query-result.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ export function queryResult() {
let resolve;
let reject;
const p = new Promise((r, e) => { resolve = r; reject = e; });
p.fulfill = value => (resolve(value), p);
p.reject = err => (reject(err), p);
return p;
return Object.assign(p, {
fulfill: value => (resolve(value), p),
reject: err => (reject(err), p)
});
}
4 changes: 4 additions & 0 deletions packages/inputs/src/Menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ const isObject = v => {
export const menu = options => input(Menu, options);

export class Menu extends MosaicClient {
/**
* Create a new Menu instance.
* @param {object} options Options object
*/
constructor({
element,
filterBy,
Expand Down
4 changes: 4 additions & 0 deletions packages/inputs/src/Search.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ let _id = 0;
export const search = options => input(Search, options);

export class Search extends MosaicClient {
/**
* Create a new Search instance.
* @param {object} options Options object
*/
constructor({
element,
filterBy,
Expand Down
6 changes: 5 additions & 1 deletion packages/inputs/src/Slider.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ let _id = 0;
export const slider = options => input(Slider, options);

export class Slider extends MosaicClient {
/**
* Create a new Slider instance.
* @param {object} options Options object
*/
constructor({
element,
filterBy,
Expand Down Expand Up @@ -80,7 +84,7 @@ export class Slider extends MosaicClient {
const { min, max } = Array.from(data)[0];
if (this.min == null) this.slider.setAttribute('min', min);
if (this.max == null) this.slider.setAttribute('max', max);
if (this.step == null) this.slider.setAttribute('step', (max - min) / 500);
if (this.step == null) this.slider.setAttribute('step', String((max - min) / 500));
return this;
}

Expand Down
Loading