From d79b2afb5695700ba283d26542f4b88ef556ccc7 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 10 Mar 2024 15:06:37 -0500 Subject: [PATCH 01/34] Define RowArrayAdapter interface --- src/rowArrayAdapter.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/rowArrayAdapter.ts diff --git a/src/rowArrayAdapter.ts b/src/rowArrayAdapter.ts new file mode 100644 index 00000000..70384676 --- /dev/null +++ b/src/rowArrayAdapter.ts @@ -0,0 +1,31 @@ +// The point of rowArrayAdapter is that we don't have to copy data into c2m if a library +// we're integrating with already stores it differently. +// TODO: supporting sort/continuous might be a little verbose +// author: Andrew Pikul (ajpikul@gmail.com) + +import type { SupportedDataPointType } from "./dataPoint"; + +/** + * An interface that imitates an array to give c2m read-access to chart data stored elsewhere. + */ +export interface RowArrayAdapter { + length: () => number; + min: () => number; + max: () => number; + at: (index: number) => SupportedDataPointType; +} + +/** + * Check if an object implements the RowArrayAdapter interface. + * @param obj - the object to check + * @returns true if the object implements the interface + */ +export function isRowArrayAdapter(obj: unknown): obj is RowArrayAdapter { + return ( + typeof obj === "object" && + "length" in obj && + "min" in obj && + "max" in obj && + "at" in obj + ); +} From 212f64881faa3309ebf35ff6714a1deff3e99bfc Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 10 Mar 2024 16:02:52 -0500 Subject: [PATCH 02/34] Add test class to adapter and test it --- src/rowArrayAdapter.ts | 46 +++++++++++++++++++++++++++++++++++- test/rowArrayAdapter.test.ts | 16 +++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 test/rowArrayAdapter.test.ts diff --git a/src/rowArrayAdapter.ts b/src/rowArrayAdapter.ts index 70384676..71072adf 100644 --- a/src/rowArrayAdapter.ts +++ b/src/rowArrayAdapter.ts @@ -23,9 +23,53 @@ export interface RowArrayAdapter { export function isRowArrayAdapter(obj: unknown): obj is RowArrayAdapter { return ( typeof obj === "object" && - "length" in obj && + "length" in obj && // TODO: Could, if they give us "length()" instead of "length", we fix it for them? "min" in obj && "max" in obj && "at" in obj ); } + +/** + * Create a RowArrayAdapter from an actual array. This is meant to aid in testing. + */ +export class ArrayAsAdapter { + _array: number[]; + + /** + * Construct adapter from supplied array + * @param array - the underlying array from the adapter + */ + constructor(array: number[]) { + this._array = array; // Don't inherit array, we want to fail Array.isArray() + } + /** + * Shims the Array.length property + * @returns the length of the array + */ + get length(): number { + return this._array.length; + } + /** + * Implements a min() function, in this case a shim over Math.min() + * @returns the minimum value of the array + */ + min(): number { + return Math.min(...this._array); + } // TODO: I forget if we want value or index + /** + * Implements a max() function, in this case a shim over Math.max() + * @returns the maximum value of the array + */ + max(): number { + return Math.max(...this._array); + } // TODO: ^^ + /** + * Shims the Array.at() function + * @param index - the index of the value you'd like to access + * @returns the value at the supplied index + */ + at(index: number): number { + return this._array.at(index); + } +} diff --git a/test/rowArrayAdapter.test.ts b/test/rowArrayAdapter.test.ts new file mode 100644 index 00000000..06798f42 --- /dev/null +++ b/test/rowArrayAdapter.test.ts @@ -0,0 +1,16 @@ +import * as adapter from "../src/rowArrayAdapter"; + +test("test if functions return the correct value", () => { + const x = [1, 2, 3, 4]; + expect(adapter.isRowArrayAdapter(x)).toBeFalsy(); + + const xAdapter = new adapter.ArrayAsAdapter(x); + expect(adapter.isRowArrayAdapter(xAdapter)).toBeTruthy(); + expect(xAdapter.length).toBe(x.length); + expect(xAdapter.min()).toBe(Math.min(...x)); + expect(xAdapter.max()).toBe(Math.max(...x)); + x.forEach((el, i) => { + expect(xAdapter.at(i)).toBe(el); + }); + expect(xAdapter.length).toBe(x.length); +}); From 0c9c722c796377e311e406190393fe44af641ea1 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 10 Mar 2024 17:00:30 -0500 Subject: [PATCH 03/34] Add findIndex() to rowArrayAdapter and test --- src/rowArrayAdapter.ts | 16 +++++++++++++++- test/rowArrayAdapter.test.ts | 15 +++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/rowArrayAdapter.ts b/src/rowArrayAdapter.ts index 71072adf..95ffbd80 100644 --- a/src/rowArrayAdapter.ts +++ b/src/rowArrayAdapter.ts @@ -13,6 +13,7 @@ export interface RowArrayAdapter { min: () => number; max: () => number; at: (index: number) => SupportedDataPointType; + findIndex(test: (any) => boolean): number; } /** @@ -43,6 +44,7 @@ export class ArrayAsAdapter { constructor(array: number[]) { this._array = array; // Don't inherit array, we want to fail Array.isArray() } + /** * Shims the Array.length property * @returns the length of the array @@ -50,6 +52,7 @@ export class ArrayAsAdapter { get length(): number { return this._array.length; } + /** * Implements a min() function, in this case a shim over Math.min() * @returns the minimum value of the array @@ -57,13 +60,15 @@ export class ArrayAsAdapter { min(): number { return Math.min(...this._array); } // TODO: I forget if we want value or index + /** * Implements a max() function, in this case a shim over Math.max() * @returns the maximum value of the array */ max(): number { return Math.max(...this._array); - } // TODO: ^^ + } // TODO: Same as above + /** * Shims the Array.at() function * @param index - the index of the value you'd like to access @@ -72,4 +77,13 @@ export class ArrayAsAdapter { at(index: number): number { return this._array.at(index); } + + /** + * Shims the Array.findIndex() function, finds index of first element which satisfies the test function + * @param test - then function by which we test + * @returns index of first element + */ + findIndex(test: (any) => boolean): number { + return this._array.findIndex(test); + } } diff --git a/test/rowArrayAdapter.test.ts b/test/rowArrayAdapter.test.ts index 06798f42..5a94abdf 100644 --- a/test/rowArrayAdapter.test.ts +++ b/test/rowArrayAdapter.test.ts @@ -1,6 +1,6 @@ import * as adapter from "../src/rowArrayAdapter"; -test("test if functions return the correct value", () => { +test("test if ArrayAsAdapter successful constructs a proper RowArrayAdapter", () => { const x = [1, 2, 3, 4]; expect(adapter.isRowArrayAdapter(x)).toBeFalsy(); @@ -12,5 +12,16 @@ test("test if functions return the correct value", () => { x.forEach((el, i) => { expect(xAdapter.at(i)).toBe(el); }); - expect(xAdapter.length).toBe(x.length); + x.forEach((el, i) => { + expect( + xAdapter.findIndex((el2) => { + return el2 === el; + }) + ).toBe(i); + expect( + xAdapter.findIndex(() => { + return false; + }) + ).toBe(-1); + }); }); From e4909ace78f8c8efbf116e8d02f915696ddeda5c Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 10 Mar 2024 17:02:33 -0500 Subject: [PATCH 04/34] Add RowArrayAdapter type to data input[] types --- src/types.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/types.ts b/src/types.ts index 977d03a2..3435f684 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,6 +2,7 @@ import type { AudioEngine } from "./audio/"; import type { c2m } from "./c2mChart"; import type { SupportedDataPointType } from "./dataPoint"; +import type { RowArrayAdapter } from "./rowArrayAdapter"; /** * Details for a given hotkey @@ -71,7 +72,7 @@ export type SonifyTypes = { * The data that should be presented in this chart. * This key is required for all charts. */ - data: dataSet | SupportedInputType[]; + data: dataSet | SupportedInputType[] | RowArrayAdapter; /** * The HTML element in the DOM that represents this chart. * This will be used to handle keyboard events to enable the user to interact with the chart. @@ -111,7 +112,7 @@ export type SonifyTypes = { * A dictionary of data, where the key is the group name, and the value is the array of data points */ export type dataSet = { - [groupName: string]: SupportedInputType[] | null; + [groupName: string]: SupportedInputType[] | RowArrayAdapter | null; }; /** From 75648d121f79de32ef25aa4140eb9e13b97b8dda Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 10 Mar 2024 19:16:21 -0500 Subject: [PATCH 05/34] Fix adapter .length to be prop not fn --- src/rowArrayAdapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rowArrayAdapter.ts b/src/rowArrayAdapter.ts index 95ffbd80..7c279e1d 100644 --- a/src/rowArrayAdapter.ts +++ b/src/rowArrayAdapter.ts @@ -9,7 +9,7 @@ import type { SupportedDataPointType } from "./dataPoint"; * An interface that imitates an array to give c2m read-access to chart data stored elsewhere. */ export interface RowArrayAdapter { - length: () => number; + length: number; min: () => number; max: () => number; at: (index: number) => SupportedDataPointType; From 505895683078864ccc00c359ea6d52cc778a4502 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 10 Mar 2024 19:53:28 -0500 Subject: [PATCH 06/34] Fix ArrayAsAdapter to return a dataPoint, not array: The whole point of this is to convert datatypes to the one c2m expects. No clue why I was returning an array instead of a SupportDataPointType. --- src/rowArrayAdapter.ts | 20 +++++++++++--------- test/rowArrayAdapter.test.ts | 12 +++++++----- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/rowArrayAdapter.ts b/src/rowArrayAdapter.ts index 7c279e1d..8c7ad25f 100644 --- a/src/rowArrayAdapter.ts +++ b/src/rowArrayAdapter.ts @@ -4,7 +4,7 @@ // author: Andrew Pikul (ajpikul@gmail.com) import type { SupportedDataPointType } from "./dataPoint"; - +import { convertDataRow } from "./utils"; /** * An interface that imitates an array to give c2m read-access to chart data stored elsewhere. */ @@ -33,16 +33,18 @@ export function isRowArrayAdapter(obj: unknown): obj is RowArrayAdapter { /** * Create a RowArrayAdapter from an actual array. This is meant to aid in testing. + * If passed a number array, it will use convertDataRow just like C2M does. + * If you've already constructed an array of dataPoints, it just wraps it. */ export class ArrayAsAdapter { - _array: number[]; + _array: SupportedDataPointType[]; /** * Construct adapter from supplied array * @param array - the underlying array from the adapter */ constructor(array: number[]) { - this._array = array; // Don't inherit array, we want to fail Array.isArray() + this._array = convertDataRow(array); // Don't inherit array, we want to fail Array.isArray() } /** @@ -58,23 +60,23 @@ export class ArrayAsAdapter { * @returns the minimum value of the array */ min(): number { - return Math.min(...this._array); - } // TODO: I forget if we want value or index + return 0; // TODO implement when necessary + } /** * Implements a max() function, in this case a shim over Math.max() * @returns the maximum value of the array */ max(): number { - return Math.max(...this._array); - } // TODO: Same as above + return 0; // TODO implement when necessary + } /** * Shims the Array.at() function * @param index - the index of the value you'd like to access * @returns the value at the supplied index */ - at(index: number): number { + at(index: number): SupportedDataPointType { return this._array.at(index); } @@ -83,7 +85,7 @@ export class ArrayAsAdapter { * @param test - then function by which we test * @returns index of first element */ - findIndex(test: (any) => boolean): number { + findIndex(test: (RowArrayAdapter) => boolean): number { return this._array.findIndex(test); } } diff --git a/test/rowArrayAdapter.test.ts b/test/rowArrayAdapter.test.ts index 5a94abdf..2b4ba57e 100644 --- a/test/rowArrayAdapter.test.ts +++ b/test/rowArrayAdapter.test.ts @@ -7,15 +7,17 @@ test("test if ArrayAsAdapter successful constructs a proper RowArrayAdapter", () const xAdapter = new adapter.ArrayAsAdapter(x); expect(adapter.isRowArrayAdapter(xAdapter)).toBeTruthy(); expect(xAdapter.length).toBe(x.length); - expect(xAdapter.min()).toBe(Math.min(...x)); - expect(xAdapter.max()).toBe(Math.max(...x)); + // expect(xAdapter.min()).toBe(Math.min(...x)); // not yet + // expect(xAdapter.max()).toBe(Math.max(...x)); // not yet x.forEach((el, i) => { - expect(xAdapter.at(i)).toBe(el); + expect(xAdapter.at(i).y).toBe(el); + expect(xAdapter.at(i).x).toBe(i); }); x.forEach((el, i) => { expect( - xAdapter.findIndex((el2) => { - return el2 === el; + xAdapter.findIndex((el2: adapter.RowArrayAdapter) => { + if (!el2) return false; + return el2.y === el; }) ).toBe(i); expect( From 166662ef20cb736b27d9a2fc4a3235f7f4729d7d Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 10 Mar 2024 20:30:25 -0500 Subject: [PATCH 07/34] Add generic to test class for adapter: When specifying the adapter, you now specify exactly what type of datapoint it will return. This makes it easier for typescript to reason around things. --- src/rowArrayAdapter.ts | 10 +++++----- test/rowArrayAdapter.test.ts | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/rowArrayAdapter.ts b/src/rowArrayAdapter.ts index 8c7ad25f..f91e09c8 100644 --- a/src/rowArrayAdapter.ts +++ b/src/rowArrayAdapter.ts @@ -36,15 +36,15 @@ export function isRowArrayAdapter(obj: unknown): obj is RowArrayAdapter { * If passed a number array, it will use convertDataRow just like C2M does. * If you've already constructed an array of dataPoints, it just wraps it. */ -export class ArrayAsAdapter { - _array: SupportedDataPointType[]; +export class ArrayAsAdapter { + _array: T[]; /** * Construct adapter from supplied array * @param array - the underlying array from the adapter */ constructor(array: number[]) { - this._array = convertDataRow(array); // Don't inherit array, we want to fail Array.isArray() + this._array = convertDataRow(array) as T[]; // Don't inherit array, we want to fail Array.isArray() } /** @@ -76,7 +76,7 @@ export class ArrayAsAdapter { * @param index - the index of the value you'd like to access * @returns the value at the supplied index */ - at(index: number): SupportedDataPointType { + at(index: number): T { return this._array.at(index); } @@ -85,7 +85,7 @@ export class ArrayAsAdapter { * @param test - then function by which we test * @returns index of first element */ - findIndex(test: (RowArrayAdapter) => boolean): number { + findIndex(test: (T) => boolean): number { return this._array.findIndex(test); } } diff --git a/test/rowArrayAdapter.test.ts b/test/rowArrayAdapter.test.ts index 2b4ba57e..0f1b5f08 100644 --- a/test/rowArrayAdapter.test.ts +++ b/test/rowArrayAdapter.test.ts @@ -1,10 +1,11 @@ import * as adapter from "../src/rowArrayAdapter"; +import type { SimpleDataPoint } from "../src/dataPoint"; test("test if ArrayAsAdapter successful constructs a proper RowArrayAdapter", () => { const x = [1, 2, 3, 4]; expect(adapter.isRowArrayAdapter(x)).toBeFalsy(); - const xAdapter = new adapter.ArrayAsAdapter(x); + const xAdapter = new adapter.ArrayAsAdapter(x); expect(adapter.isRowArrayAdapter(xAdapter)).toBeTruthy(); expect(xAdapter.length).toBe(x.length); // expect(xAdapter.min()).toBe(Math.min(...x)); // not yet @@ -15,8 +16,7 @@ test("test if ArrayAsAdapter successful constructs a proper RowArrayAdapter", () }); x.forEach((el, i) => { expect( - xAdapter.findIndex((el2: adapter.RowArrayAdapter) => { - if (!el2) return false; + xAdapter.findIndex((el2: SimpleDataPoint) => { return el2.y === el; }) ).toBe(i); From 19e1b3deb3b6566bb4656cb888c26a8c75158d73 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 10 Mar 2024 21:41:16 -0500 Subject: [PATCH 08/34] Modify validate and validate.test to allow adapter: 1) Rows can't be accessed with [], they must be accessed with .at() All arrays support .at(), and only .at() can be overridden 2) Tests should function the same whether using an array for data or an array adapter: The tests are now parameterized (_adapter_uilities.ts) to be run 11 times w/ p-values [0, .1, .2, ..., 1], where each data array has p chances to be replaced with an array adapter. Each could be run slightly different, and every combination of real array/adapter array should pass and provide the correct error. There are specific cases where this isn't true, and it is commented. --- src/validate.ts | 39 ++-- test/_adapter_utilities.ts | 36 ++++ test/validate.test.ts | 363 +++++++++++++++++++++++-------------- 3 files changed, 281 insertions(+), 157 deletions(-) create mode 100644 test/_adapter_utilities.ts diff --git a/src/validate.ts b/src/validate.ts index 0062f01d..29e8693f 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -8,6 +8,10 @@ import { isOHLCDataPoint, isBoxDataPoint } from "./dataPoint"; +import { isRowArrayAdapter } from "./rowArrayAdapter"; + +import type { RowArrayAdapter } from "./rowArrayAdapter"; + import type { SonifyTypes } from "./types"; import { SUPPORTED_CHART_TYPES } from "./types"; @@ -136,7 +140,7 @@ export const validateInputAxes = (axes?: SonifyTypes["axes"]) => { }; export const validateInputDataHomogeneity = (data: SonifyTypes["data"]) => { - if (Array.isArray(data)) { + if (Array.isArray(data) || isRowArrayAdapter(data)) { return validateInputDataRowHomogeneity(data); } for (const key in data) { @@ -152,16 +156,18 @@ export const validateInputDataHomogeneity = (data: SonifyTypes["data"]) => { }; export const validateInputDataRowHomogeneity = ( - row: (number | SupportedDataPointType)[] + row: (number | SupportedDataPointType)[] | RowArrayAdapter ) => { - const first = row[0]; + const first = row.at(0); + // TODO: how does c2m handle empty data? + if (typeof first === "number") { const failure = row.findIndex((cell) => !(typeof cell === "number")); if (failure === -1) { return ""; } return `The first item is a number, but item index ${failure} is not (value: ${JSON.stringify( - row[failure] + row.at(failure) )}). All items should be of the same type.`; } @@ -178,7 +184,7 @@ export const validateInputDataRowHomogeneity = ( return ""; } return `The first item is a simple data point (x/y), but item index ${failure} is not (value: ${JSON.stringify( - row[failure] + row.at(failure) )}). All items should be of the same type.`; } if (isAlternateAxisDataPoint(first)) { @@ -189,7 +195,7 @@ export const validateInputDataRowHomogeneity = ( return ""; } return `The first item is an alternate axis data point (x/y2), but item index ${failure} is not (value: ${JSON.stringify( - row[failure] + row.at(failure) )}). All items should be of the same type.`; } if (isOHLCDataPoint(first)) { @@ -198,14 +204,14 @@ export const validateInputDataRowHomogeneity = ( return ""; } return `The first item is an OHLC data point (x/open/high/low/close), but item index ${failure} is not (value: ${JSON.stringify( - row[failure] + row.at(failure) )}). All items should be of the same type.`; } if (isBoxDataPoint(first)) { const failure = row.findIndex((cell) => !isBoxDataPoint(cell)); if (failure >= 0) { return `The first item is a box data point (x/low/q1/median/q3/high), but item index ${failure} is not (value: ${JSON.stringify( - row[failure] + row.at(failure) )}). All items should be of the same type.`; } @@ -216,7 +222,7 @@ export const validateInputDataRowHomogeneity = ( ); if (nonArray >= 0) { return `At least one box provided an outlier that was not an array. An outliers should be an array of numbers. The box is question is: ${JSON.stringify( - row[nonArray] + row.at(nonArray) )}`; } @@ -228,7 +234,7 @@ export const validateInputDataRowHomogeneity = ( ); if (nonArrayNumber >= 0) { return `At least one box has a non-numeric outlier. Box outliers must be an array of numbers. The box in question is: ${JSON.stringify( - row[nonArrayNumber] + row.at(nonArrayNumber) )}`; } return ""; @@ -239,7 +245,7 @@ export const validateInputDataRowHomogeneity = ( return ""; } return `The first item is a high low data point (x/high/low), but item index ${failure} is not (value: ${JSON.stringify( - row[failure] + row.at(failure) )}). All items should be of the same type.`; } @@ -269,7 +275,7 @@ export const validateHierarchyReferences = ( return ""; } - if (Array.isArray(data)) { + if (Array.isArray(data) || isRowArrayAdapter(data)) { return `Unexpected data structure. options.root="${root}", but "${root}" is not a key in data. Data is: ${JSON.stringify(data).replace(/^.{0,100}(.+)$/, "...")}`; } @@ -288,21 +294,22 @@ export const validateHierarchyReferences = ( // Go through each group, and find the "children" entries for (let groupIndex = 0; groupIndex < groups.length; groupIndex++) { - const group = groups[groupIndex]; + const group = groups.at(groupIndex); // TODO: was this tested const groupName = groupNames[groupIndex]; const omitter = (n) => n !== groupName && n !== root; - if (!Array.isArray(group)) { + if (!Array.isArray(group) && !isRowArrayAdapter(group)) { + // not sure this is being tested... continue; } for (let cell = 0; cell < group.length; cell++) { - if (typeof group[cell] !== "object") { + if (typeof group.at(cell) !== "object") { continue; } - const { children } = group[cell] as SupportedDataPointType; + const { children } = group.at(cell) as SupportedDataPointType; if (!children) { continue; } diff --git a/test/_adapter_utilities.ts b/test/_adapter_utilities.ts new file mode 100644 index 00000000..74782ca7 --- /dev/null +++ b/test/_adapter_utilities.ts @@ -0,0 +1,36 @@ +import type { SupportedDataPointType } from "../src/dataPoint"; +import * as adapter from "../src/rowArrayAdapter"; + +export const probabilities = [ + 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1 +]; + +/** + * AdapterTypeRandomizer provides a way to fuzz test, switching + * between regular arrays and adapters. + */ +export class AdapterTypeRandomizer { + proportion: number; + + /** + * constructor to set relative proportions + * @param proportion - how often should the input be wrapped in a adapter + */ + constructor(proportion: number | undefined) { + this.proportion = proportion || 0.5; + } + + /** + * a is the wrapper fucntion for arrays, it might return the equivalent adapter + * @param a - the array you want to wrap + * @returns either a or a wrapped a + */ + a(a: number[]): number[] | adapter.RowArrayAdapter { + //console.log(a); + const flip = Math.random(); + //console.log(`if (${flip} < ${this.proportion}) adapt!`); + if (flip < this.proportion) return new adapter.ArrayAsAdapter(a); + //console.log(`Not wrapped, returning: ${a}`); + return a; + } +} diff --git a/test/validate.test.ts b/test/validate.test.ts index 131a327a..89bb8e18 100644 --- a/test/validate.test.ts +++ b/test/validate.test.ts @@ -14,6 +14,8 @@ import { validateInputTypeCountsMatchData } from "../src/validate"; +import { AdapterTypeRandomizer, probabilities } from "./_adapter_utilities"; + const validTypes = "line, bar, band, pie, candlestick, histogram, box, matrix, scatter, treemap, unsupported"; const validLanguages = "en, de, es, fr, it"; @@ -148,9 +150,12 @@ test("validateInput", () => { ); }); -test("validateInputDataRowHomogeneity", () => { +test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { + const s = new AdapterTypeRandomizer(p); + const y2 = new AdapterTypeRandomizer(p); + // Confirm number homogeneity - expect(validateInputDataRowHomogeneity([1, 2, 3, 4, 5])).toBe(""); + expect(validateInputDataRowHomogeneity(s.a([1, 2, 3, 4, 5]))).toBe(""); // @ts-ignore - deliberately generating error condition // Invalidate on number heterogeneity @@ -160,188 +165,262 @@ test("validateInputDataRowHomogeneity", () => { // Confirm simple data point homogeneity expect( - validateInputDataRowHomogeneity([ - { x: 1, y: 1 }, - { x: 2, y: 2 }, - { x: 3, y: 3 } - ]) + validateInputDataRowHomogeneity( + s.a([ + { x: 1, y: 1 }, + { x: 2, y: 2 }, + { x: 3, y: 3 } + ]) + ) ).toBe(""); // Confirm simple data point homogeneity expect( validateInputDataRowHomogeneity([{ x: 1, y: 1 }, { x: 2, y: 2 }, 3]) ).toBe( `The first item is a simple data point (x/y), but item index 2 is not (value: 3). All items should be of the same type.` - ); + ); // wrapping in adapter would fix this error // Confirm simple data point homogeneity expect( - validateInputDataRowHomogeneity([ - { x: 1, y2: 1 }, - { x: 2, y2: 2 }, - { x: 3, y2: 3 } - ]) + validateInputDataRowHomogeneity( + y2.a([ + { x: 1, y2: 1 }, + { x: 2, y2: 2 }, + { x: 3, y2: 3 } + ]) + ) ).toBe(""); // Confirm simple data point homogeneity expect( - validateInputDataRowHomogeneity([ - { x: 1, y2: 1 }, - { x: 2, y: 2 }, - { x: 3, y2: 3 } - ]) + validateInputDataRowHomogeneity( + y2.a([ + { x: 1, y2: 1 }, + { x: 2, y: 2 }, + { x: 3, y2: 3 } + ]) + ) ).toBe( `The first item is an alternate axis data point (x/y2), but item index 1 is not (value: {"x":2,"y":2}). All items should be of the same type.` ); + const hl = new AdapterTypeRandomizer(p); + // Confirm simple data point homogeneity expect( - validateInputDataRowHomogeneity([ - { x: 1, high: 1, low: 1 }, - { x: 2, high: 2, low: 2 }, - { x: 3, high: 3, low: 3 } - ]) + validateInputDataRowHomogeneity( + hl.a([ + { x: 1, high: 1, low: 1 }, + { x: 2, high: 2, low: 2 }, + { x: 3, high: 3, low: 3 } + ]) + ) ).toBe(""); // Confirm simple data point homogeneity expect( - validateInputDataRowHomogeneity([ - { x: 1, high: 1, low: 1 }, - { x: 2, y: 2 }, - { x: 3, y2: 3 } - ]) + validateInputDataRowHomogeneity( + hl.a([ + { x: 1, high: 1, low: 1 }, + { x: 2, y: 2 }, + { x: 3, y2: 3 } + ]) + ) ).toBe( `The first item is a high low data point (x/high/low), but item index 1 is not (value: {"x":2,"y":2}). All items should be of the same type.` ); + const ohlc = new AdapterTypeRandomizer(p); // Confirm simple data point homogeneity expect( - validateInputDataRowHomogeneity([ - { x: 1, open: 1, high: 1, low: 1, close: 1 }, - { x: 2, open: 2, high: 2, low: 2, close: 2 }, - { x: 3, open: 3, high: 3, low: 3, close: 3 } - ]) + validateInputDataRowHomogeneity( + ohlc.a([ + { x: 1, open: 1, high: 1, low: 1, close: 1 }, + { x: 2, open: 2, high: 2, low: 2, close: 2 }, + { x: 3, open: 3, high: 3, low: 3, close: 3 } + ]) + ) ).toBe(""); // Confirm simple data point homogeneity expect( - validateInputDataRowHomogeneity([ - { x: 1, high: 1, low: 1, open: 1, close: 1 }, - { x: 2, high: 2, low: 1 }, - { x: 3, y2: 3 } - ]) + validateInputDataRowHomogeneity( + ohlc.a([ + { x: 1, high: 1, low: 1, open: 1, close: 1 }, + { x: 2, high: 2, low: 1 }, + { x: 3, y2: 3 } + ]) + ) ).toBe( `The first item is an OHLC data point (x/open/high/low/close), but item index 1 is not (value: {"x":2,"high":2,"low":1}). All items should be of the same type.` ); + const box = new AdapterTypeRandomizer(p); // Confirm simple data point homogeneity expect( - validateInputDataRowHomogeneity([ - { x: 0, low: 5.03, q1: 6.36, median: 6.91, q3: 7.34, high: 7.53 }, - { x: 1, low: 7.1, q1: 7.18, median: 7.25, q3: 7.33, high: 7.4 }, - { x: 2, low: 7.31, q1: 7.32, median: 7.32, q3: 7.33, high: 7.33 } - ]) + validateInputDataRowHomogeneity( + box.a([ + { + x: 0, + low: 5.03, + q1: 6.36, + median: 6.91, + q3: 7.34, + high: 7.53 + }, + { x: 1, low: 7.1, q1: 7.18, median: 7.25, q3: 7.33, high: 7.4 }, + { + x: 2, + low: 7.31, + q1: 7.32, + median: 7.32, + q3: 7.33, + high: 7.33 + } + ]) + ) ).toBe(""); // Confirm simple data point homogeneity expect( - validateInputDataRowHomogeneity([ - { x: 0, low: 5.03, q1: 6.36, median: 6.91, q3: 7.34, high: 7.53 }, - { x: 2, high: 2, low: 1 }, - { x: 3, y2: 3 } - ]) + validateInputDataRowHomogeneity( + box.a([ + { + x: 0, + low: 5.03, + q1: 6.36, + median: 6.91, + q3: 7.34, + high: 7.53 + }, + { x: 2, high: 2, low: 1 }, + { x: 3, y2: 3 } + ]) + ) ).toBe( `The first item is a box data point (x/low/q1/median/q3/high), but item index 1 is not (value: {"x":2,"high":2,"low":1}). All items should be of the same type.` ); expect( - validateInputDataRowHomogeneity([ - { x: 0, low: 5.03, q1: 6.36, median: 6.91, q3: 7.34, high: 7.53 }, - { - x: 1, - low: 5.03, - q1: 6.36, - median: 6.91, - q3: 7.34, - high: 7.53, - // @ts-ignore: Deliberately using invalid data in order to test error handling - outlier: null - } - ]) + validateInputDataRowHomogeneity( + box.a([ + { + x: 0, + low: 5.03, + q1: 6.36, + median: 6.91, + q3: 7.34, + high: 7.53 + }, + { + x: 1, + low: 5.03, + q1: 6.36, + median: 6.91, + q3: 7.34, + high: 7.53, + // @ts-ignore: Deliberately using invalid data in order to test error handling + outlier: null + } + ]) + ) ).toBe( `At least one box provided an outlier that was not an array. An outliers should be an array of numbers. The box is question is: {"x":1,"low":5.03,"q1":6.36,"median":6.91,"q3":7.34,"high":7.53,"outlier":null}` ); expect( - validateInputDataRowHomogeneity([ - { x: 0, low: 5.03, q1: 6.36, median: 6.91, q3: 7.34, high: 7.53 }, - { - x: 1, - low: 5.03, - q1: 6.36, - median: 6.91, - q3: 7.34, - high: 7.53, - // @ts-ignore: Deliberately using invalid data in order to test error handling - outlier: 5 - } - ]) + validateInputDataRowHomogeneity( + box.a([ + { + x: 0, + low: 5.03, + q1: 6.36, + median: 6.91, + q3: 7.34, + high: 7.53 + }, + { + x: 1, + low: 5.03, + q1: 6.36, + median: 6.91, + q3: 7.34, + high: 7.53, + // @ts-ignore: Deliberately using invalid data in order to test error handling + outlier: 5 + } + ]) + ) ).toBe( `At least one box provided an outlier that was not an array. An outliers should be an array of numbers. The box is question is: {"x":1,"low":5.03,"q1":6.36,"median":6.91,"q3":7.34,"high":7.53,"outlier":5}` ); expect( - validateInputDataRowHomogeneity([ - { x: 0, low: 5.03, q1: 6.36, median: 6.91, q3: 7.34, high: 7.53 }, - { - x: 1, - low: 5.03, - q1: 6.36, - median: 6.91, - q3: 7.34, - high: 7.53, - outlier: [5] - }, - { - x: 2, - low: 5.03, - q1: 6.36, - median: 6.91, - q3: 7.34, - high: 7.53, - // @ts-ignore: Deliberately using invalid data in order to test error handling - outlier: [5, null] - } - ]) + validateInputDataRowHomogeneity( + box.a([ + { + x: 0, + low: 5.03, + q1: 6.36, + median: 6.91, + q3: 7.34, + high: 7.53 + }, + { + x: 1, + low: 5.03, + q1: 6.36, + median: 6.91, + q3: 7.34, + high: 7.53, + outlier: [5] + }, + { + x: 2, + low: 5.03, + q1: 6.36, + median: 6.91, + q3: 7.34, + high: 7.53, + // @ts-ignore: Deliberately using invalid data in order to test error handling + outlier: [5, null] + } + ]) + ) ).toBe( `At least one box has a non-numeric outlier. Box outliers must be an array of numbers. The box in question is: {"x":2,"low":5.03,"q1":6.36,"median":6.91,"q3":7.34,"high":7.53,"outlier":[5,null]}` ); // @ts-ignore - deliberately generating error condition // Confirm number homogeneity - expect(validateInputDataRowHomogeneity(["1", 2, 3, 4, 5])).toBe( + expect(validateInputDataRowHomogeneity(s.a(["1", 2, 3, 4, 5]))).toBe( `The first item is of an unrecognized type (value: "1"). Supported types are: number, simple data point (x/y), alternative axis data point (x/y2), and high low data point (x/high/low).` ); expect( - validateInputDataRowHomogeneity([ - // @ts-ignore - { x: new Date(), y: 1 } - ]) + validateInputDataRowHomogeneity( + s.a([ + // @ts-ignore + { x: new Date(), y: 1 } + ]) + ) ).toContain( "The first item is a date, which is not a supported format type" ); }); -test("validateInputDataHomogeneity", () => { +test.each(probabilities)("validateInputDataHomogeneity", (p) => { + const s = new AdapterTypeRandomizer(p); + const y2 = new AdapterTypeRandomizer(p); // Good, 1 row - expect(validateInputDataHomogeneity([1, 2, 3, 4, 5])).toBe(""); + expect(validateInputDataHomogeneity(s.a([1, 2, 3, 4, 5]))).toBe(""); // Good, multiple rows expect( validateInputDataHomogeneity({ - a: [ + a: s.a([ { x: 1, y: 1 }, { x: 2, y: 2 }, { x: 3, y: 3 } - ], - b: [ + ]), + b: y2.a([ { x: 1, y2: 1 }, { x: 2, y2: 2 }, { x: 3, y2: 3 } - ] + ]) }) ).toBe(""); @@ -349,21 +428,21 @@ test("validateInputDataHomogeneity", () => { // Bad, 1 row expect(validateInputDataRowHomogeneity([1, 2, "a", 4, 5])).toBe( `The first item is a number, but item index 2 is not (value: "a"). All items should be of the same type.` - ); + ); // Error changes on type, so we don't use our type randomizer // Bad, multiple rows expect( validateInputDataHomogeneity({ - a: [ + a: s.a([ { x: 1, y: 1 }, { x: 2, y: 2 }, { x: 3, y: 3 } - ], - b: [ + ]), + b: y2.a([ { x: 1, y2: 1 }, { x: 2, y: 2 }, { x: 3, y2: 3 } - ] + ]) }) ).toBe( `Error for data category b: The first item is an alternate axis data point (x/y2), but item index 1 is not (value: {"x":2,"y":2}). All items should be of the same type.` @@ -371,11 +450,11 @@ test("validateInputDataHomogeneity", () => { expect( validateInputDataHomogeneity({ - a: [ + a: s.a([ { x: 1, y: 1 }, { x: 2, y: 2 }, { x: 3, y: 3 } - ], + ]), b: null }) ).toBe(""); @@ -401,13 +480,14 @@ test("validate img tag without cc property", () => { ); }); -test("validateHierarchyReferences", () => { +test.each(probabilities)("validateHierarchyReferences", (p) => { + const s = new AdapterTypeRandomizer(p); // happy path expect( validateHierarchyReferences( { - a: [{ x: 0, y: 1, children: "b" }], - b: [{ x: 1, y: 2 }] + a: s.a([{ x: 0, y: 1, children: "b" }]), + b: s.a([{ x: 1, y: 2 }]) }, { root: "a" } ) @@ -417,19 +497,19 @@ test("validateHierarchyReferences", () => { expect( validateHierarchyReferences( { - a: [{ x: 0, y: 1 }], - b: [{ x: 1, y: 2 }] + a: s.a([{ x: 0, y: 1 }]), + b: s.a([{ x: 1, y: 2 }]) }, { root: "a" } ) ).toBe(""); // data = number[], options = undefined - expect(validateHierarchyReferences([1, 2, 3, 4, 5])).toBe(""); + expect(validateHierarchyReferences(s.a([1, 2, 3, 4, 5]))).toBe(""); // data = number[], options = {root}} expect( - validateHierarchyReferences([1, 2, 3, 4, 5], { root: "a" }) + validateHierarchyReferences(s.a([1, 2, 3, 4, 5]), { root: "a" }) ).toContain( `Unexpected data structure. options.root="a", but "a" is not a key in data.` ); @@ -438,8 +518,8 @@ test("validateHierarchyReferences", () => { expect(() => validateHierarchyReferences( { - a: [1, 2, 3, 4, 5], - b: [6, 7, 8] + a: s.a([1, 2, 3, 4, 5]), + b: s.a([6, 7, 8]) }, { root: "a" } ) @@ -458,21 +538,21 @@ test("validateHierarchyReferences", () => { ).not.toThrow(); // data = {key: number[]}, options = undefined - expect(validateHierarchyReferences({ a: [1, 2, 3, 4, 5] })).toBe(""); + expect(validateHierarchyReferences({ a: s.a([1, 2, 3, 4, 5]) })).toBe(""); // data = { key: {x,y}[] }, options = undefined expect( validateHierarchyReferences({ - a: [{ x: 0, y: 1 }], - b: [{ x: 1, y: 2 }] + a: s.a([{ x: 0, y: 1 }]), + b: s.a([{ x: 1, y: 2 }]) }) ).toBe(""); // data = tree, options = {} expect( validateHierarchyReferences({ - a: [{ x: 0, y: 1, children: "b" }], - b: [{ x: 1, y: 2 }] + a: s.a([{ x: 0, y: 1, children: "b" }]), + b: s.a([{ x: 1, y: 2 }]) }) ).toBe(""); @@ -480,8 +560,8 @@ test("validateHierarchyReferences", () => { expect( validateHierarchyReferences( { - a: [{ x: 0, y: 1, children: "b" }], - b: [{ x: 1, y: 2 }] + a: s.a([{ x: 0, y: 1, children: "b" }]), + b: s.a([{ x: 1, y: 2 }]) }, { root: "z" } ) @@ -493,8 +573,8 @@ test("validateHierarchyReferences", () => { expect( validateHierarchyReferences( { - a: [{ x: 0, y: 1, children: "a" }], - b: [{ x: 1, y: 2 }] + a: s.a([{ x: 0, y: 1, children: "a" }]), + b: s.a([{ x: 1, y: 2 }]) }, { root: "a" } ) @@ -506,8 +586,8 @@ test("validateHierarchyReferences", () => { expect( validateHierarchyReferences( { - a: [{ x: 0, y: 1, children: "z" }], - b: [{ x: 1, y: 2 }] + a: s.a([{ x: 0, y: 1, children: "z" }]), + b: s.a([{ x: 1, y: 2 }]) }, { root: "a" } ) @@ -519,8 +599,8 @@ test("validateHierarchyReferences", () => { expect( validateHierarchyReferences( { - a: [{ x: 0, y: 1, children: "b" }], - b: [{ x: 1, y: 2, children: "a" }] + a: s.a([{ x: 0, y: 1, children: "b" }]), + b: s.a([{ x: 1, y: 2, children: "a" }]) }, { root: "a" } ) @@ -533,8 +613,8 @@ test("validateHierarchyReferences", () => { validateHierarchyReferences( { // @ts-ignore - a: [{ x: 0, y: 1, children: 1 }], - b: [{ x: 1, y: 2 }] + a: s.a([{ x: 0, y: 1, children: 1 }]), + b: s.a([{ x: 1, y: 2 }]) }, { root: "a" } ) @@ -543,10 +623,11 @@ test("validateHierarchyReferences", () => { ); }); -test("validateInputTypeCountsMatchData", () => { +test.each(probabilities)("validateInputTypeCountsMatchData", (p) => { + const s = new AdapterTypeRandomizer(p); const dataWith2Rows = { - A: [1, 2, 3], - B: [4, 5, 6] + A: s.a([1, 2, 3]), + B: s.a([4, 5, 6]) }; expect( From 015c1930fcbd308efb967e22e993b4aee2c08a31 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 10 Mar 2024 22:06:25 -0500 Subject: [PATCH 09/34] Change test adapter to support number[] --- src/rowArrayAdapter.ts | 10 +++++++--- test/rowArrayAdapter.test.ts | 5 ++++- test/validate.test.ts | 7 ++++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/rowArrayAdapter.ts b/src/rowArrayAdapter.ts index f91e09c8..00b770fa 100644 --- a/src/rowArrayAdapter.ts +++ b/src/rowArrayAdapter.ts @@ -4,7 +4,6 @@ // author: Andrew Pikul (ajpikul@gmail.com) import type { SupportedDataPointType } from "./dataPoint"; -import { convertDataRow } from "./utils"; /** * An interface that imitates an array to give c2m read-access to chart data stored elsewhere. */ @@ -43,8 +42,13 @@ export class ArrayAsAdapter { * Construct adapter from supplied array * @param array - the underlying array from the adapter */ - constructor(array: number[]) { - this._array = convertDataRow(array) as T[]; // Don't inherit array, we want to fail Array.isArray() + constructor(array: (number | SupportedDataPointType)[]) { + // I don't know how c2m handles empty data + if (!array) { + this._array = [] as T[]; + return; // This is bad, we should throw an error. + } + this._array = array as T[]; // Don't inherit array, we want to fail Array.isArray() } /** diff --git a/test/rowArrayAdapter.test.ts b/test/rowArrayAdapter.test.ts index 0f1b5f08..4477c456 100644 --- a/test/rowArrayAdapter.test.ts +++ b/test/rowArrayAdapter.test.ts @@ -1,11 +1,14 @@ import * as adapter from "../src/rowArrayAdapter"; import type { SimpleDataPoint } from "../src/dataPoint"; +import { convertDataRow } from "../src/utils"; test("test if ArrayAsAdapter successful constructs a proper RowArrayAdapter", () => { const x = [1, 2, 3, 4]; expect(adapter.isRowArrayAdapter(x)).toBeFalsy(); - const xAdapter = new adapter.ArrayAsAdapter(x); + const xAdapter = new adapter.ArrayAsAdapter( + convertDataRow(x) + ); expect(adapter.isRowArrayAdapter(xAdapter)).toBeTruthy(); expect(xAdapter.length).toBe(x.length); // expect(xAdapter.min()).toBe(Math.min(...x)); // not yet diff --git a/test/validate.test.ts b/test/validate.test.ts index 89bb8e18..b0c09758 100644 --- a/test/validate.test.ts +++ b/test/validate.test.ts @@ -151,6 +151,7 @@ test("validateInput", () => { }); test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { + const n = new AdapterTypeRandomizer(p); const s = new AdapterTypeRandomizer(p); const y2 = new AdapterTypeRandomizer(p); @@ -159,9 +160,9 @@ test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { // @ts-ignore - deliberately generating error condition // Invalidate on number heterogeneity - expect(validateInputDataRowHomogeneity([1, 2, "a", 4, 5])).toBe( + expect(validateInputDataRowHomogeneity(n.a([1, 2, "a", 4, 5]))).toBe( `The first item is a number, but item index 2 is not (value: "a"). All items should be of the same type.` - ); + ); // To keep typing simpler, adapters don't replace actual native arrays like number[], they will always // Confirm simple data point homogeneity expect( @@ -178,7 +179,7 @@ test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { validateInputDataRowHomogeneity([{ x: 1, y: 1 }, { x: 2, y: 2 }, 3]) ).toBe( `The first item is a simple data point (x/y), but item index 2 is not (value: 3). All items should be of the same type.` - ); // wrapping in adapter would fix this error + ); // wrapping in adapter would fix this error! // Confirm simple data point homogeneity expect( From a02a6415a3e6e3c3e1f2e0af02009396394a8631 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 10 Mar 2024 22:29:17 -0500 Subject: [PATCH 10/34] Fix test adapter to obey another corner case --- test/validate.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/validate.test.ts b/test/validate.test.ts index b0c09758..272cc9b8 100644 --- a/test/validate.test.ts +++ b/test/validate.test.ts @@ -176,10 +176,12 @@ test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { ).toBe(""); // Confirm simple data point homogeneity expect( - validateInputDataRowHomogeneity([{ x: 1, y: 1 }, { x: 2, y: 2 }, 3]) + validateInputDataRowHomogeneity( + s.a([{ x: 1, y: 1 }, { x: 2, y: 2 }, 3]) + ) ).toBe( `The first item is a simple data point (x/y), but item index 2 is not (value: 3). All items should be of the same type.` - ); // wrapping in adapter would fix this error! + ); // Confirm simple data point homogeneity expect( From 44486e82c3719cd4f8716851f5a2aa542e159cdd Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sat, 16 Mar 2024 20:15:19 -0500 Subject: [PATCH 11/34] Rework type system a bit for array-adapter: The array adapter interface accepts a type , just like any other array which wants to know what it is an array of. Previously, there had been enforcements of SupportedDataPointType as the type of T, which still isn't a terrible idea, but a) probably won't matter unless users are using typescript importing types and b) makes testing validation inconvenient because certain tests validate vanilla number arrays (number[]) instead of SupportedDataPointType[], as that is a shortcut to SimpleDataPoint (a supported type). --- src/rowArrayAdapter.ts | 22 +++++++++++----------- src/types.ts | 10 ++++++++-- src/validate.ts | 4 +++- test/_adapter_utilities.ts | 10 ++++------ test/rowArrayAdapter.test.ts | 1 - 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/rowArrayAdapter.ts b/src/rowArrayAdapter.ts index 00b770fa..ae005e7a 100644 --- a/src/rowArrayAdapter.ts +++ b/src/rowArrayAdapter.ts @@ -7,12 +7,12 @@ import type { SupportedDataPointType } from "./dataPoint"; /** * An interface that imitates an array to give c2m read-access to chart data stored elsewhere. */ -export interface RowArrayAdapter { +export interface RowArrayAdapter { length: number; min: () => number; max: () => number; - at: (index: number) => SupportedDataPointType; - findIndex(test: (any) => boolean): number; + at: (index: number) => T; + findIndex(test: (T) => boolean): number; } /** @@ -20,7 +20,9 @@ export interface RowArrayAdapter { * @param obj - the object to check * @returns true if the object implements the interface */ -export function isRowArrayAdapter(obj: unknown): obj is RowArrayAdapter { +export function isRowArrayAdapter( + obj: unknown +): obj is RowArrayAdapter { return ( typeof obj === "object" && "length" in obj && // TODO: Could, if they give us "length()" instead of "length", we fix it for them? @@ -31,22 +33,20 @@ export function isRowArrayAdapter(obj: unknown): obj is RowArrayAdapter { } /** - * Create a RowArrayAdapter from an actual array. This is meant to aid in testing. - * If passed a number array, it will use convertDataRow just like C2M does. - * If you've already constructed an array of dataPoints, it just wraps it. + * Create a RowArrayAdapter from an actual array. */ -export class ArrayAsAdapter { +export class ArrayAsAdapter { _array: T[]; /** * Construct adapter from supplied array * @param array - the underlying array from the adapter */ - constructor(array: (number | SupportedDataPointType)[]) { - // I don't know how c2m handles empty data + constructor(array: (T | SupportedDataPointType)[]) { + // NOTE: If you give us a SupportedDataPointType, we will attempt to cast it for you to type T if (!array) { this._array = [] as T[]; - return; // This is bad, we should throw an error. + return; // (Should throw error? don't think c2m allows empty data) } this._array = array as T[]; // Don't inherit array, we want to fail Array.isArray() } diff --git a/src/types.ts b/src/types.ts index 3435f684..5e3c378b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -72,7 +72,10 @@ export type SonifyTypes = { * The data that should be presented in this chart. * This key is required for all charts. */ - data: dataSet | SupportedInputType[] | RowArrayAdapter; + data: + | dataSet + | SupportedInputType[] + | RowArrayAdapter; /** * The HTML element in the DOM that represents this chart. * This will be used to handle keyboard events to enable the user to interact with the chart. @@ -112,7 +115,10 @@ export type SonifyTypes = { * A dictionary of data, where the key is the group name, and the value is the array of data points */ export type dataSet = { - [groupName: string]: SupportedInputType[] | RowArrayAdapter | null; + [groupName: string]: + | SupportedInputType[] + | RowArrayAdapter + | null; }; /** diff --git a/src/validate.ts b/src/validate.ts index 29e8693f..8a03c4be 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -156,7 +156,9 @@ export const validateInputDataHomogeneity = (data: SonifyTypes["data"]) => { }; export const validateInputDataRowHomogeneity = ( - row: (number | SupportedDataPointType)[] | RowArrayAdapter + row: + | (number | SupportedDataPointType)[] + | RowArrayAdapter ) => { const first = row.at(0); // TODO: how does c2m handle empty data? diff --git a/test/_adapter_utilities.ts b/test/_adapter_utilities.ts index 74782ca7..8dd566fb 100644 --- a/test/_adapter_utilities.ts +++ b/test/_adapter_utilities.ts @@ -1,4 +1,3 @@ -import type { SupportedDataPointType } from "../src/dataPoint"; import * as adapter from "../src/rowArrayAdapter"; export const probabilities = [ @@ -9,7 +8,7 @@ export const probabilities = [ * AdapterTypeRandomizer provides a way to fuzz test, switching * between regular arrays and adapters. */ -export class AdapterTypeRandomizer { +export class AdapterTypeRandomizer { proportion: number; /** @@ -25,12 +24,11 @@ export class AdapterTypeRandomizer { * @param a - the array you want to wrap * @returns either a or a wrapped a */ - a(a: number[]): number[] | adapter.RowArrayAdapter { - //console.log(a); + a(a: T[]): adapter.RowArrayAdapter | T[] { const flip = Math.random(); - //console.log(`if (${flip} < ${this.proportion}) adapt!`); + if (flip < this.proportion) return new adapter.ArrayAsAdapter(a); - //console.log(`Not wrapped, returning: ${a}`); + return a; } } diff --git a/test/rowArrayAdapter.test.ts b/test/rowArrayAdapter.test.ts index 4477c456..cc0ba0d2 100644 --- a/test/rowArrayAdapter.test.ts +++ b/test/rowArrayAdapter.test.ts @@ -5,7 +5,6 @@ import { convertDataRow } from "../src/utils"; test("test if ArrayAsAdapter successful constructs a proper RowArrayAdapter", () => { const x = [1, 2, 3, 4]; expect(adapter.isRowArrayAdapter(x)).toBeFalsy(); - const xAdapter = new adapter.ArrayAsAdapter( convertDataRow(x) ); From adcc0179829c53c23b0381e77dc96dd24f9eeea8 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 17 Mar 2024 19:39:16 -0500 Subject: [PATCH 12/34] Write min/max() for ArrayAsAdapter --- src/rowArrayAdapter.ts | 68 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/src/rowArrayAdapter.ts b/src/rowArrayAdapter.ts index ae005e7a..f13c7ecf 100644 --- a/src/rowArrayAdapter.ts +++ b/src/rowArrayAdapter.ts @@ -4,13 +4,15 @@ // author: Andrew Pikul (ajpikul@gmail.com) import type { SupportedDataPointType } from "./dataPoint"; +import { isHighLowDataPoint, isOHLCDataPoint } from "./dataPoint"; + /** * An interface that imitates an array to give c2m read-access to chart data stored elsewhere. */ export interface RowArrayAdapter { length: number; - min: () => number; - max: () => number; + min: (prop: string) => number; + max: (prop: string) => number; at: (index: number) => T; findIndex(test: (T) => boolean): number; } @@ -61,18 +63,72 @@ export class ArrayAsAdapter { /** * Implements a min() function, in this case a shim over Math.min() + * @param prop - a string indicating the property that is assessed for min * @returns the minimum value of the array */ - min(): number { - return 0; // TODO implement when necessary + min(prop: string): number { + if (!this._array) return NaN; + return this._array.reduce( + (localMinimum: number, point: T): number => { + let val: number = NaN; + if (typeof point === "number") { + if (prop) return NaN; + val = point; + } else if (prop in (point as SupportedDataPointType)) { + // online linter wants me to specify [index: string]:number to use `in` + // nor should I have to cast re: type exclusion, + val = point[prop] as number; + } else if (isOHLCDataPoint(point) && prop === "y") { + val = Math.min( + point.high, + point.low, + point.open, + point.close + ); + } else if (isHighLowDataPoint(point) && prop === "y") { + val = Math.min(point.high, point.low); + } else return localMinimum; + if (isNaN(localMinimum)) { + return val; + } + return val < localMinimum ? val : localMinimum; + }, + NaN // Initial value of reduce() + ); } /** * Implements a max() function, in this case a shim over Math.max() + * @param prop - a string indicating the property that is assessed for min * @returns the maximum value of the array */ - max(): number { - return 0; // TODO implement when necessary + max(prop: string): number { + if (!this._array) return NaN; + return this._array.reduce( + (localMaximum: number, point: T): number => { + let val: number = NaN; + if (typeof point === "number") { + if (prop) return NaN; + val = point; + } else if (prop in (point as SupportedDataPointType)) { + val = point[prop] as number; + } else if (isOHLCDataPoint(point) && prop === "y") { + val = Math.max( + point.high, + point.low, + point.open, + point.close + ); + } else if (isHighLowDataPoint(point) && prop === "y") { + val = Math.max(point.high, point.low); + } else return localMaximum; + if (isNaN(localMaximum)) { + return val; + } + return val > localMaximum ? val : localMaximum; + }, + NaN // Initial value of reduce() + ); } /** From 94834098b279d841d38e142a5d204aaf5c937365 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 17 Mar 2024 19:39:41 -0500 Subject: [PATCH 13/34] Improve type validations in adapter --- src/rowArrayAdapter.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rowArrayAdapter.ts b/src/rowArrayAdapter.ts index f13c7ecf..ce358264 100644 --- a/src/rowArrayAdapter.ts +++ b/src/rowArrayAdapter.ts @@ -26,6 +26,7 @@ export function isRowArrayAdapter( obj: unknown ): obj is RowArrayAdapter { return ( + obj && typeof obj === "object" && "length" in obj && // TODO: Could, if they give us "length()" instead of "length", we fix it for them? "min" in obj && @@ -37,7 +38,7 @@ export function isRowArrayAdapter( /** * Create a RowArrayAdapter from an actual array. */ -export class ArrayAsAdapter { +export class ArrayAsAdapter { _array: T[]; /** From eaa1c46d8b1e13cbcb6e748ce22e94edd8bd13ba Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 17 Mar 2024 19:40:06 -0500 Subject: [PATCH 14/34] Shim Array.forEach function in ArrayAsAdapter --- src/rowArrayAdapter.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/rowArrayAdapter.ts b/src/rowArrayAdapter.ts index ce358264..c44aa491 100644 --- a/src/rowArrayAdapter.ts +++ b/src/rowArrayAdapter.ts @@ -149,4 +149,27 @@ export class ArrayAsAdapter { findIndex(test: (T) => boolean): number { return this._array.findIndex(test); } + + /** + * forEach imitates an arrays forEach(): + * @param callbackFn - a function to execute for each element in the "array". + * It accepts three arguments: + * value: T + * index: number + * array: the current ArrayAsAdapter + * + * and returns nothing. + * @param thisArg - an optional argument to set as "this" in your callbackFn. + * As in Array.prototype.forEach(), if the callbackFn is defined by arrow syntax, + * the arrow syntax will lexically bind its own this and ignore thisArg. + * @returns nothing. + */ + forEach( + callbackFn: (value: T, index: number, array: ArrayAsAdapter) => void, + thisArg?: unknown + ): void { + this._array.forEach((innerValue: T, innerIndex: number, innerArray: T[]) => { + callbackFn.bind(thisArg)(innerValue, innerIndex, this); + }); // purposely use => because we need our lexically-scoped this + } } From f0e0733644b148c4d119ff21ff49fc38a4222b23 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 17 Mar 2024 19:41:17 -0500 Subject: [PATCH 15/34] Allow AdapterRandomizer tester to handle any type --- test/_adapter_utilities.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/_adapter_utilities.ts b/test/_adapter_utilities.ts index 8dd566fb..0a0f47ca 100644 --- a/test/_adapter_utilities.ts +++ b/test/_adapter_utilities.ts @@ -1,4 +1,5 @@ import * as adapter from "../src/rowArrayAdapter"; +import type { SupportedDataPointType } from "../src/dataPoint"; export const probabilities = [ 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1 @@ -8,7 +9,10 @@ export const probabilities = [ * AdapterTypeRandomizer provides a way to fuzz test, switching * between regular arrays and adapters. */ -export class AdapterTypeRandomizer { + +type ValidAdapterType = number | SupportedDataPointType; + +export class AdapterTypeRandomizer { proportion: number; /** @@ -24,11 +28,11 @@ export class AdapterTypeRandomizer { * @param a - the array you want to wrap * @returns either a or a wrapped a */ - a(a: T[]): adapter.RowArrayAdapter | T[] { + a(input: ValidAdapterType[]): adapter.RowArrayAdapter | ValidAdapterType[] { const flip = Math.random(); - if (flip < this.proportion) return new adapter.ArrayAsAdapter(a); + if (flip < this.proportion) return new adapter.ArrayAsAdapter(input); - return a; + return input; } } From 43b95ae12d53c4db135de31c1482a031a808987a Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 17 Mar 2024 19:42:02 -0500 Subject: [PATCH 16/34] Update tests to use new AdapterRandomizer API --- test/validate.test.ts | 111 ++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 59 deletions(-) diff --git a/test/validate.test.ts b/test/validate.test.ts index 272cc9b8..151687e3 100644 --- a/test/validate.test.ts +++ b/test/validate.test.ts @@ -151,23 +151,21 @@ test("validateInput", () => { }); test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { - const n = new AdapterTypeRandomizer(p); - const s = new AdapterTypeRandomizer(p); - const y2 = new AdapterTypeRandomizer(p); + const maybeMakeAdapter = new AdapterTypeRandomizer(p); // Confirm number homogeneity - expect(validateInputDataRowHomogeneity(s.a([1, 2, 3, 4, 5]))).toBe(""); + expect(validateInputDataRowHomogeneity(maybeMakeAdapter.a([1, 2, 3, 4, 5]))).toBe(""); // @ts-ignore - deliberately generating error condition // Invalidate on number heterogeneity - expect(validateInputDataRowHomogeneity(n.a([1, 2, "a", 4, 5]))).toBe( + expect(validateInputDataRowHomogeneity(maybeMakeAdapter.a([1, 2, "a", 4, 5]))).toBe( `The first item is a number, but item index 2 is not (value: "a"). All items should be of the same type.` ); // To keep typing simpler, adapters don't replace actual native arrays like number[], they will always // Confirm simple data point homogeneity expect( validateInputDataRowHomogeneity( - s.a([ + maybeMakeAdapter.a([ { x: 1, y: 1 }, { x: 2, y: 2 }, { x: 3, y: 3 } @@ -177,7 +175,7 @@ test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { // Confirm simple data point homogeneity expect( validateInputDataRowHomogeneity( - s.a([{ x: 1, y: 1 }, { x: 2, y: 2 }, 3]) + maybeMakeAdapter.a([{ x: 1, y: 1 }, { x: 2, y: 2 }, 3]) ) ).toBe( `The first item is a simple data point (x/y), but item index 2 is not (value: 3). All items should be of the same type.` @@ -186,7 +184,7 @@ test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { // Confirm simple data point homogeneity expect( validateInputDataRowHomogeneity( - y2.a([ + maybeMakeAdapter.a([ { x: 1, y2: 1 }, { x: 2, y2: 2 }, { x: 3, y2: 3 } @@ -196,7 +194,7 @@ test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { // Confirm simple data point homogeneity expect( validateInputDataRowHomogeneity( - y2.a([ + maybeMakeAdapter.a([ { x: 1, y2: 1 }, { x: 2, y: 2 }, { x: 3, y2: 3 } @@ -206,12 +204,10 @@ test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { `The first item is an alternate axis data point (x/y2), but item index 1 is not (value: {"x":2,"y":2}). All items should be of the same type.` ); - const hl = new AdapterTypeRandomizer(p); - // Confirm simple data point homogeneity expect( validateInputDataRowHomogeneity( - hl.a([ + maybeMakeAdapter.a([ { x: 1, high: 1, low: 1 }, { x: 2, high: 2, low: 2 }, { x: 3, high: 3, low: 3 } @@ -221,7 +217,7 @@ test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { // Confirm simple data point homogeneity expect( validateInputDataRowHomogeneity( - hl.a([ + maybeMakeAdapter.a([ { x: 1, high: 1, low: 1 }, { x: 2, y: 2 }, { x: 3, y2: 3 } @@ -231,11 +227,10 @@ test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { `The first item is a high low data point (x/high/low), but item index 1 is not (value: {"x":2,"y":2}). All items should be of the same type.` ); - const ohlc = new AdapterTypeRandomizer(p); // Confirm simple data point homogeneity expect( validateInputDataRowHomogeneity( - ohlc.a([ + maybeMakeAdapter.a([ { x: 1, open: 1, high: 1, low: 1, close: 1 }, { x: 2, open: 2, high: 2, low: 2, close: 2 }, { x: 3, open: 3, high: 3, low: 3, close: 3 } @@ -245,7 +240,7 @@ test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { // Confirm simple data point homogeneity expect( validateInputDataRowHomogeneity( - ohlc.a([ + maybeMakeAdapter.a([ { x: 1, high: 1, low: 1, open: 1, close: 1 }, { x: 2, high: 2, low: 1 }, { x: 3, y2: 3 } @@ -255,11 +250,10 @@ test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { `The first item is an OHLC data point (x/open/high/low/close), but item index 1 is not (value: {"x":2,"high":2,"low":1}). All items should be of the same type.` ); - const box = new AdapterTypeRandomizer(p); // Confirm simple data point homogeneity expect( validateInputDataRowHomogeneity( - box.a([ + maybeMakeAdapter.a([ { x: 0, low: 5.03, @@ -283,7 +277,7 @@ test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { // Confirm simple data point homogeneity expect( validateInputDataRowHomogeneity( - box.a([ + maybeMakeAdapter.a([ { x: 0, low: 5.03, @@ -301,7 +295,7 @@ test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { ); expect( validateInputDataRowHomogeneity( - box.a([ + maybeMakeAdapter.a([ { x: 0, low: 5.03, @@ -327,7 +321,7 @@ test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { ); expect( validateInputDataRowHomogeneity( - box.a([ + maybeMakeAdapter.a([ { x: 0, low: 5.03, @@ -353,7 +347,7 @@ test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { ); expect( validateInputDataRowHomogeneity( - box.a([ + maybeMakeAdapter.a([ { x: 0, low: 5.03, @@ -389,13 +383,13 @@ test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { // @ts-ignore - deliberately generating error condition // Confirm number homogeneity - expect(validateInputDataRowHomogeneity(s.a(["1", 2, 3, 4, 5]))).toBe( + expect(validateInputDataRowHomogeneity(maybeMakeAdapter.a(["1", 2, 3, 4, 5]))).toBe( `The first item is of an unrecognized type (value: "1"). Supported types are: number, simple data point (x/y), alternative axis data point (x/y2), and high low data point (x/high/low).` ); expect( validateInputDataRowHomogeneity( - s.a([ + maybeMakeAdapter.a([ // @ts-ignore { x: new Date(), y: 1 } ]) @@ -406,20 +400,19 @@ test.each(probabilities)("validateInputDataRowHomogeneity", (p) => { }); test.each(probabilities)("validateInputDataHomogeneity", (p) => { - const s = new AdapterTypeRandomizer(p); - const y2 = new AdapterTypeRandomizer(p); + const maybeMakeAdapter = new AdapterTypeRandomizer(p); // Good, 1 row - expect(validateInputDataHomogeneity(s.a([1, 2, 3, 4, 5]))).toBe(""); + expect(validateInputDataHomogeneity(maybeMakeAdapter.a([1, 2, 3, 4, 5]))).toBe(""); // Good, multiple rows expect( validateInputDataHomogeneity({ - a: s.a([ + a: maybeMakeAdapter.a([ { x: 1, y: 1 }, { x: 2, y: 2 }, { x: 3, y: 3 } ]), - b: y2.a([ + b: maybeMakeAdapter.a([ { x: 1, y2: 1 }, { x: 2, y2: 2 }, { x: 3, y2: 3 } @@ -436,12 +429,12 @@ test.each(probabilities)("validateInputDataHomogeneity", (p) => { // Bad, multiple rows expect( validateInputDataHomogeneity({ - a: s.a([ + a: maybeMakeAdapter.a([ { x: 1, y: 1 }, { x: 2, y: 2 }, { x: 3, y: 3 } ]), - b: y2.a([ + b: maybeMakeAdapter.a([ { x: 1, y2: 1 }, { x: 2, y: 2 }, { x: 3, y2: 3 } @@ -453,7 +446,7 @@ test.each(probabilities)("validateInputDataHomogeneity", (p) => { expect( validateInputDataHomogeneity({ - a: s.a([ + a: maybeMakeAdapter.a([ { x: 1, y: 1 }, { x: 2, y: 2 }, { x: 3, y: 3 } @@ -484,13 +477,13 @@ test("validate img tag without cc property", () => { }); test.each(probabilities)("validateHierarchyReferences", (p) => { - const s = new AdapterTypeRandomizer(p); + const maybeMakeAdapter = new AdapterTypeRandomizer(p); // happy path expect( validateHierarchyReferences( { - a: s.a([{ x: 0, y: 1, children: "b" }]), - b: s.a([{ x: 1, y: 2 }]) + a: maybeMakeAdapter.a([{ x: 0, y: 1, children: "b" }]), + b: maybeMakeAdapter.a([{ x: 1, y: 2 }]) }, { root: "a" } ) @@ -500,19 +493,19 @@ test.each(probabilities)("validateHierarchyReferences", (p) => { expect( validateHierarchyReferences( { - a: s.a([{ x: 0, y: 1 }]), - b: s.a([{ x: 1, y: 2 }]) + a: maybeMakeAdapter.a([{ x: 0, y: 1 }]), + b: maybeMakeAdapter.a([{ x: 1, y: 2 }]) }, { root: "a" } ) ).toBe(""); // data = number[], options = undefined - expect(validateHierarchyReferences(s.a([1, 2, 3, 4, 5]))).toBe(""); + expect(validateHierarchyReferences(maybeMakeAdapter.a([1, 2, 3, 4, 5]))).toBe(""); // data = number[], options = {root}} expect( - validateHierarchyReferences(s.a([1, 2, 3, 4, 5]), { root: "a" }) + validateHierarchyReferences(maybeMakeAdapter.a([1, 2, 3, 4, 5]), { root: "a" }) ).toContain( `Unexpected data structure. options.root="a", but "a" is not a key in data.` ); @@ -521,8 +514,8 @@ test.each(probabilities)("validateHierarchyReferences", (p) => { expect(() => validateHierarchyReferences( { - a: s.a([1, 2, 3, 4, 5]), - b: s.a([6, 7, 8]) + a: maybeMakeAdapter.a([1, 2, 3, 4, 5]), + b: maybeMakeAdapter.a([6, 7, 8]) }, { root: "a" } ) @@ -541,21 +534,21 @@ test.each(probabilities)("validateHierarchyReferences", (p) => { ).not.toThrow(); // data = {key: number[]}, options = undefined - expect(validateHierarchyReferences({ a: s.a([1, 2, 3, 4, 5]) })).toBe(""); + expect(validateHierarchyReferences({ a: maybeMakeAdapter.a([1, 2, 3, 4, 5]) })).toBe(""); // data = { key: {x,y}[] }, options = undefined expect( validateHierarchyReferences({ - a: s.a([{ x: 0, y: 1 }]), - b: s.a([{ x: 1, y: 2 }]) + a: maybeMakeAdapter.a([{ x: 0, y: 1 }]), + b: maybeMakeAdapter.a([{ x: 1, y: 2 }]) }) ).toBe(""); // data = tree, options = {} expect( validateHierarchyReferences({ - a: s.a([{ x: 0, y: 1, children: "b" }]), - b: s.a([{ x: 1, y: 2 }]) + a: maybeMakeAdapter.a([{ x: 0, y: 1, children: "b" }]), + b: maybeMakeAdapter.a([{ x: 1, y: 2 }]) }) ).toBe(""); @@ -563,8 +556,8 @@ test.each(probabilities)("validateHierarchyReferences", (p) => { expect( validateHierarchyReferences( { - a: s.a([{ x: 0, y: 1, children: "b" }]), - b: s.a([{ x: 1, y: 2 }]) + a: maybeMakeAdapter.a([{ x: 0, y: 1, children: "b" }]), + b: maybeMakeAdapter.a([{ x: 1, y: 2 }]) }, { root: "z" } ) @@ -576,8 +569,8 @@ test.each(probabilities)("validateHierarchyReferences", (p) => { expect( validateHierarchyReferences( { - a: s.a([{ x: 0, y: 1, children: "a" }]), - b: s.a([{ x: 1, y: 2 }]) + a: maybeMakeAdapter.a([{ x: 0, y: 1, children: "a" }]), + b: maybeMakeAdapter.a([{ x: 1, y: 2 }]) }, { root: "a" } ) @@ -589,8 +582,8 @@ test.each(probabilities)("validateHierarchyReferences", (p) => { expect( validateHierarchyReferences( { - a: s.a([{ x: 0, y: 1, children: "z" }]), - b: s.a([{ x: 1, y: 2 }]) + a: maybeMakeAdapter.a([{ x: 0, y: 1, children: "z" }]), + b: maybeMakeAdapter.a([{ x: 1, y: 2 }]) }, { root: "a" } ) @@ -602,8 +595,8 @@ test.each(probabilities)("validateHierarchyReferences", (p) => { expect( validateHierarchyReferences( { - a: s.a([{ x: 0, y: 1, children: "b" }]), - b: s.a([{ x: 1, y: 2, children: "a" }]) + a: maybeMakeAdapter.a([{ x: 0, y: 1, children: "b" }]), + b: maybeMakeAdapter.a([{ x: 1, y: 2, children: "a" }]) }, { root: "a" } ) @@ -616,8 +609,8 @@ test.each(probabilities)("validateHierarchyReferences", (p) => { validateHierarchyReferences( { // @ts-ignore - a: s.a([{ x: 0, y: 1, children: 1 }]), - b: s.a([{ x: 1, y: 2 }]) + a: maybeMakeAdapter.a([{ x: 0, y: 1, children: 1 }]), + b: maybeMakeAdapter.a([{ x: 1, y: 2 }]) }, { root: "a" } ) @@ -627,10 +620,10 @@ test.each(probabilities)("validateHierarchyReferences", (p) => { }); test.each(probabilities)("validateInputTypeCountsMatchData", (p) => { - const s = new AdapterTypeRandomizer(p); + const maybeMakeAdapter = new AdapterTypeRandomizer(p); const dataWith2Rows = { - A: s.a([1, 2, 3]), - B: s.a([4, 5, 6]) + A: maybeMakeAdapter.a([1, 2, 3]), + B: maybeMakeAdapter.a([4, 5, 6]) }; expect( From fd0334dabab57ae710cde368bb714d5a5ca0921d Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 17 Mar 2024 19:42:19 -0500 Subject: [PATCH 17/34] Test new min/max and forEach functions, reorganize --- test/rowArrayAdapter.test.ts | 89 +++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 16 deletions(-) diff --git a/test/rowArrayAdapter.test.ts b/test/rowArrayAdapter.test.ts index cc0ba0d2..75fc5874 100644 --- a/test/rowArrayAdapter.test.ts +++ b/test/rowArrayAdapter.test.ts @@ -2,30 +2,87 @@ import * as adapter from "../src/rowArrayAdapter"; import type { SimpleDataPoint } from "../src/dataPoint"; import { convertDataRow } from "../src/utils"; +const plainArray = [1123]; // , 2342, 301823, 409]; +const adaptedArray = new adapter.ArrayAsAdapter(plainArray) +const simplePointArray = new adapter.ArrayAsAdapter(convertDataRow(plainArray)) + test("test if ArrayAsAdapter successful constructs a proper RowArrayAdapter", () => { - const x = [1, 2, 3, 4]; - expect(adapter.isRowArrayAdapter(x)).toBeFalsy(); - const xAdapter = new adapter.ArrayAsAdapter( - convertDataRow(x) - ); - expect(adapter.isRowArrayAdapter(xAdapter)).toBeTruthy(); - expect(xAdapter.length).toBe(x.length); - // expect(xAdapter.min()).toBe(Math.min(...x)); // not yet - // expect(xAdapter.max()).toBe(Math.max(...x)); // not yet - x.forEach((el, i) => { - expect(xAdapter.at(i).y).toBe(el); - expect(xAdapter.at(i).x).toBe(i); + expect(adapter.isRowArrayAdapter(plainArray)).toBeFalsy(); + expect(adapter.isRowArrayAdapter(adaptedArray)).toBeTruthy(); + + // Test length + expect(adaptedArray.length).toBe(plainArray.length); + expect(simplePointArray.length).toBe(plainArray.length); + + // Test min/max + expect(adaptedArray.min("x")).toBe(NaN); + expect(adaptedArray.max("y")).toBe(NaN); + expect(adaptedArray.min("")).toBe(Math.min(...plainArray)); + expect(simplePointArray.min("x")).toBe(0); + expect(simplePointArray.min("y")).toBe(Math.min(...plainArray)); + expect(simplePointArray.max("x")).toBe(plainArray.length-1); + expect(simplePointArray.max("y")).toBe(Math.max(...plainArray)); + + // Test at(i) + plainArray.forEach((el, i) => { + expect(adaptedArray.at(i)).toBe(el); + expect(simplePointArray.at(i).x).toBe(i); + expect(simplePointArray.at(i).y).toBe(el); }); - x.forEach((el, i) => { + + // Test indexOf(i) + plainArray.forEach((val, i) => { expect( - xAdapter.findIndex((el2: SimpleDataPoint) => { - return el2.y === el; + adaptedArray.findIndex((point: number) => { + return point === val; }) ).toBe(i); expect( - xAdapter.findIndex(() => { + simplePointArray.findIndex((point: SimpleDataPoint) => { + return point.y === val; + }) + ).toBe(i); + expect( + simplePointArray.findIndex(() => { return false; }) ).toBe(-1); }); + + // Test forEach: + adaptedArray.forEach(function(element, index, array) { + expect(element).toBe(plainArray[index]); + expect(array).toBe(adaptedArray); + expect(this).toBe("this replaces `this`"); + }, "this replaces `this`") + // NOTE: can't use => if we want to replace this + // because => does a permanent bind + adaptedArray.forEach(function(element, index, array) { + expect(element).toBe(plainArray[index]); + expect(array).toBe(adaptedArray); + // an unspecified value of `this` will be `undefined` + // in a testing context, but in a browser it will depend on + // whether 'use strict`; Testing for it may be prudent. + }) + simplePointArray.forEach(function(element, index, array) { + expect(element.y).toBe(plainArray[index]); + expect(array).toBe(simplePointArray); + expect(this).toBe("this replaces `this`"); + }, "this replaces `this`") + simplePointArray.forEach(function(element, index, array) { + expect(element.y).toBe(plainArray[index]); + expect(array).toBe(simplePointArray); + }) + + // Implement: + // [x] length + // [x] indexOf + // [x] min + // [x] max + // [x] at + // [x] forEach + // [ ] map + // [ ] filter + // [ ] for(in of) + // [ ] reduce }); From 1134d0b69c509187274952bbefe739b10d1d065c Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 17 Mar 2024 19:44:17 -0500 Subject: [PATCH 18/34] Fix arrayAdapter tests to test more data --- test/rowArrayAdapter.test.ts | 54 +++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/test/rowArrayAdapter.test.ts b/test/rowArrayAdapter.test.ts index 75fc5874..5aa57508 100644 --- a/test/rowArrayAdapter.test.ts +++ b/test/rowArrayAdapter.test.ts @@ -2,9 +2,11 @@ import * as adapter from "../src/rowArrayAdapter"; import type { SimpleDataPoint } from "../src/dataPoint"; import { convertDataRow } from "../src/utils"; -const plainArray = [1123]; // , 2342, 301823, 409]; -const adaptedArray = new adapter.ArrayAsAdapter(plainArray) -const simplePointArray = new adapter.ArrayAsAdapter(convertDataRow(plainArray)) +const plainArray = [1123, 2342, 301823, 409]; +const adaptedArray = new adapter.ArrayAsAdapter(plainArray); +const simplePointArray = new adapter.ArrayAsAdapter( + convertDataRow(plainArray) +); test("test if ArrayAsAdapter successful constructs a proper RowArrayAdapter", () => { expect(adapter.isRowArrayAdapter(plainArray)).toBeFalsy(); @@ -20,7 +22,7 @@ test("test if ArrayAsAdapter successful constructs a proper RowArrayAdapter", () expect(adaptedArray.min("")).toBe(Math.min(...plainArray)); expect(simplePointArray.min("x")).toBe(0); expect(simplePointArray.min("y")).toBe(Math.min(...plainArray)); - expect(simplePointArray.max("x")).toBe(plainArray.length-1); + expect(simplePointArray.max("x")).toBe(plainArray.length - 1); expect(simplePointArray.max("y")).toBe(Math.max(...plainArray)); // Test at(i) @@ -50,31 +52,31 @@ test("test if ArrayAsAdapter successful constructs a proper RowArrayAdapter", () }); // Test forEach: - adaptedArray.forEach(function(element, index, array) { - expect(element).toBe(plainArray[index]); - expect(array).toBe(adaptedArray); - expect(this).toBe("this replaces `this`"); - }, "this replaces `this`") + adaptedArray.forEach(function (element, index, array) { + expect(element).toBe(plainArray[index]); + expect(array).toBe(adaptedArray); + expect(this).toBe("this replaces `this`"); + }, "this replaces `this`"); // NOTE: can't use => if we want to replace this // because => does a permanent bind - adaptedArray.forEach(function(element, index, array) { - expect(element).toBe(plainArray[index]); - expect(array).toBe(adaptedArray); - // an unspecified value of `this` will be `undefined` - // in a testing context, but in a browser it will depend on - // whether 'use strict`; Testing for it may be prudent. - }) - simplePointArray.forEach(function(element, index, array) { - expect(element.y).toBe(plainArray[index]); - expect(array).toBe(simplePointArray); - expect(this).toBe("this replaces `this`"); - }, "this replaces `this`") - simplePointArray.forEach(function(element, index, array) { - expect(element.y).toBe(plainArray[index]); - expect(array).toBe(simplePointArray); - }) + adaptedArray.forEach(function (element, index, array) { + expect(element).toBe(plainArray[index]); + expect(array).toBe(adaptedArray); + // an unspecified value of `this` will be `undefined` + // in a testing context, but in a browser it will depend on + // whether 'use strict`; Testing for it may be prudent. + }); + simplePointArray.forEach(function (element, index, array) { + expect(element.y).toBe(plainArray[index]); + expect(array).toBe(simplePointArray); + expect(this).toBe("this replaces `this`"); + }, "this replaces `this`"); + simplePointArray.forEach(function (element, index, array) { + expect(element.y).toBe(plainArray[index]); + expect(array).toBe(simplePointArray); + }); - // Implement: + // Implement: // [x] length // [x] indexOf // [x] min From 10be8ee4b034b16b826116625c91e016adda6395 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 17 Mar 2024 19:45:34 -0500 Subject: [PATCH 19/34] Make utils.ts min/max use adapter min/max and test --- src/utils.ts | 162 ++++++++++++++++++++++++--------------------- test/utils.test.ts | 15 +++-- 2 files changed, 97 insertions(+), 80 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index b8e2dddf..54914a00 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -22,6 +22,8 @@ import type { dataSet, AxisScale } from "./types"; +import type { RowArrayAdapter } from "./rowArrayAdapter"; +import { isRowArrayAdapter } from "./rowArrayAdapter"; export const interpolateBin = ( point: number, @@ -62,103 +64,115 @@ export const calcPan = (pct: number) => (isNaN(pct) ? 0 : (pct * 2 - 1) * 0.98); const isNotNull = (tmp: unknown) => tmp !== null; +// Question: Howcome the pre-adapter code didn't support Boxpoint for minimum/maximum? export const calculateAxisMinimum = ( - data: SupportedDataPointType[][], + data: ( + | SupportedDataPointType[] + | RowArrayAdapter + )[], prop: "x" | "y" | "y2", filterGroupIndex?: number ) => { - let dataToProcess: SupportedDataPointType[] = data.flat().filter(isNotNull); - if (filterGroupIndex >= 0 && filterGroupIndex < data.length) { - dataToProcess = data[filterGroupIndex]; + data = [data[filterGroupIndex]]; } - const values: number[] = dataToProcess - .map((point: SupportedDataPointType): number => { - if (isSimpleDataPoint(point)) { - if (prop === "x" || prop === "y") { - return point[prop]; - } - } else if (isAlternateAxisDataPoint(point)) { - if (prop === "x" || prop === "y2") { - return point[prop]; - } - } else if (isOHLCDataPoint(point)) { - if (prop === "x") { - return point.x; - } - if (prop === "y") { - return Math.min( - point.high, - point.low, - point.open, - point.close - ); - } - } else if (isHighLowDataPoint(point)) { - if (prop === "x") { - return point.x; - } - if (prop === "y") { - return Math.min(point.high, point.low); - } + const localMinimums: number[] = data + .map( + ( + row: + | SupportedDataPointType[] + | RowArrayAdapter + ): number => { + if (!row) return NaN; + if (isRowArrayAdapter(row)) return row.min(prop); + return row.reduce( + ( + localMinimum: number, + point: SupportedDataPointType + ): number => { + let val: number = NaN; + if (prop in point) { + val = point[prop] as number; + } else if (isOHLCDataPoint(point) && prop === "y") { + val = Math.min( + point.high, + point.low, + point.open, + point.close + ); + } else if (isHighLowDataPoint(point) && prop === "y") { + val = Math.min(point.high, point.low); + } else return localMinimum; + if (isNaN(localMinimum)) { + return val; + } + return val < localMinimum ? val : localMinimum; + }, + NaN // Initial value of reduce() + ); } - return NaN; - }) + ) .filter((num) => !isNaN(num)); - if (values.length === 0) { + if (localMinimums.length === 0) { return NaN; } - return Math.min(...values); + return Math.min(...localMinimums); }; + export const calculateAxisMaximum = ( - data: SupportedDataPointType[][], + data: ( + | SupportedDataPointType[] + | RowArrayAdapter + )[], prop: "x" | "y" | "y2", filterGroupIndex?: number ) => { - let dataToProcess: SupportedDataPointType[] = data.flat().filter(isNotNull); - if (filterGroupIndex >= 0 && filterGroupIndex < data.length) { - dataToProcess = data[filterGroupIndex]; + data = [data[filterGroupIndex]]; } - const values: number[] = dataToProcess - .map((point: SupportedDataPointType): number => { - if (isSimpleDataPoint(point)) { - if (prop === "x" || prop === "y") { - return point[prop]; - } - } else if (isAlternateAxisDataPoint(point)) { - if (prop === "x" || prop === "y2") { - return point[prop]; - } - } else if (isOHLCDataPoint(point)) { - if (prop === "x") { - return point.x; - } - if (prop === "y") { - return Math.max( - point.high, - point.low, - point.open, - point.close - ); - } - } else if (isHighLowDataPoint(point)) { - if (prop === "x") { - return point.x; - } - if (prop === "y") { - return Math.max(point.high, point.low); - } + const localMaximums: number[] = data + .map( + ( + row: + | SupportedDataPointType[] + | RowArrayAdapter + ): number => { + if (!row) return NaN; + if (isRowArrayAdapter(row)) return row.max(prop); + return row.reduce( + ( + localMaximum: number, + point: SupportedDataPointType + ): number => { + let val: number = NaN; + if (prop in point) { + val = point[prop] as number; + } else if (isOHLCDataPoint(point) && prop === "y") { + val = Math.max( + point.high, + point.low, + point.open, + point.close + ); + } else if (isHighLowDataPoint(point) && prop === "y") { + val = Math.max(point.high, point.low); + } else return localMaximum; + if (isNaN(localMaximum)) { + return val; + } + return val > localMaximum ? val : localMaximum; + }, + NaN // Initial value of reduce() + ); } - return NaN; - }) + ) .filter((num) => !isNaN(num)); - if (values.length === 0) { + if (localMaximums.length === 0) { return NaN; } - return Math.max(...values); + return Math.max(...localMaximums); }; export const defaultFormat = (value: number) => `${value}`; diff --git a/test/utils.test.ts b/test/utils.test.ts index e4312bd9..244a358b 100644 --- a/test/utils.test.ts +++ b/test/utils.test.ts @@ -1,4 +1,3 @@ -import type { SimpleDataPoint } from "../src/dataPoint"; import { calcPan, calculateAxisMaximum, @@ -13,6 +12,7 @@ import { convertDataRow, detectIfMobile } from "../src/utils"; +import { AdapterTypeRandomizer, probabilities } from "./_adapter_utilities"; describe("utils", () => { test("interpolate bin - linear", () => { @@ -34,11 +34,14 @@ describe("utils", () => { expect(calcPan(NaN)).toBe(0); }); - test("calculate axis min/max", () => { - const singleRow: SimpleDataPoint[][] = [ - [1, 5, 2, 0, 3, 6].map((x) => { - return { x, y: 0 }; - }) + test.each(probabilities)("calculate axis min/max", (p) => { + const maybeMakeAdapter = new AdapterTypeRandomizer(p); + const singleRow = [ + maybeMakeAdapter.a( + [1, 5, 2, 0, 3, 6].map((x) => { + return { x, y: 0 }; + }) + ) ]; const multiRow = [ [1, 5, 2, 0, 3, 6].map((x) => { From c574b2d62e631a5c4bde4d92ba59c4f06b339186 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 17 Mar 2024 20:11:44 -0500 Subject: [PATCH 20/34] Resolve some linter errors --- src/rowArrayAdapter.ts | 11 +++++++---- src/utils.ts | 9 +++++---- test/rowArrayAdapter.test.ts | 1 + 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/rowArrayAdapter.ts b/src/rowArrayAdapter.ts index c44aa491..13d1b053 100644 --- a/src/rowArrayAdapter.ts +++ b/src/rowArrayAdapter.ts @@ -75,7 +75,7 @@ export class ArrayAsAdapter { if (typeof point === "number") { if (prop) return NaN; val = point; - } else if (prop in (point as SupportedDataPointType)) { + } else if (prop in point) { // online linter wants me to specify [index: string]:number to use `in` // nor should I have to cast re: type exclusion, val = point[prop] as number; @@ -111,7 +111,7 @@ export class ArrayAsAdapter { if (typeof point === "number") { if (prop) return NaN; val = point; - } else if (prop in (point as SupportedDataPointType)) { + } else if (prop in point) { val = point[prop] as number; } else if (isOHLCDataPoint(point) && prop === "y") { val = Math.max( @@ -162,13 +162,16 @@ export class ArrayAsAdapter { * @param thisArg - an optional argument to set as "this" in your callbackFn. * As in Array.prototype.forEach(), if the callbackFn is defined by arrow syntax, * the arrow syntax will lexically bind its own this and ignore thisArg. - * @returns nothing. */ forEach( callbackFn: (value: T, index: number, array: ArrayAsAdapter) => void, thisArg?: unknown ): void { - this._array.forEach((innerValue: T, innerIndex: number, innerArray: T[]) => { + this._array.forEach((innerValue: T, innerIndex: number) => { + // typescript doesn't like us binding thisArg which is `unknown` type + // but we're shimming a javascript function and the user has the right + // to assign legitimately any value they'd like as `this` + // eslint-disable-next-line @typescript-eslint/no-unsafe-call callbackFn.bind(thisArg)(innerValue, innerIndex, this); }); // purposely use => because we need our lexically-scoped this } diff --git a/src/utils.ts b/src/utils.ts index 54914a00..8779aff5 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -255,7 +255,7 @@ export const generatePointDescription = ( }; export const usesAxis = ( - data: SupportedDataPointType[][], + data: SupportedDataPointType[][], // TODO axisName: "x" | "y" | "y2" ) => { const firstUseOfAxis = data.filter(isNotNull).find((row) => { @@ -269,7 +269,7 @@ export const usesAxis = ( * @param data - the X/Y values */ export const calculateMetadataByGroup = ( - data: (SupportedDataPointType[] | null)[] + data: (SupportedDataPointType[] | null)[] // TODO ): groupedMetadata[] => { return data.map((row, index) => { if (row === null) { @@ -339,7 +339,7 @@ export const calculateMetadataByGroup = ( * @param filterGroupIndex - */ export const initializeAxis = ( - data: SupportedDataPointType[][], + data: SupportedDataPointType[][], // TODO axisName: validAxes, userAxis?: AxisData, filterGroupIndex?: number @@ -396,7 +396,7 @@ export const detectDataPointType = (query: unknown): detectableDataPoint => { }; export const convertDataRow = ( - row: (SupportedDataPointType | number)[] | null + row: (SupportedDataPointType | number)[] | null // TODO ) => { if (row === null) { return null; @@ -552,6 +552,7 @@ export const prepChartElement = ( }; export const checkForNumberInput = ( + // TODO What is this lol metadataByGroup: groupedMetadata[], data: SonifyTypes["data"] ) => { diff --git a/test/rowArrayAdapter.test.ts b/test/rowArrayAdapter.test.ts index 5aa57508..cf11df7c 100644 --- a/test/rowArrayAdapter.test.ts +++ b/test/rowArrayAdapter.test.ts @@ -83,6 +83,7 @@ test("test if ArrayAsAdapter successful constructs a proper RowArrayAdapter", () // [x] max // [x] at // [x] forEach + // [ ] find // [ ] map // [ ] filter // [ ] for(in of) From 1f43a332c3237a6257bfc66fce897ca4a3742c19 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 17 Mar 2024 20:18:29 -0500 Subject: [PATCH 21/34] Resolve another linter error --- src/rowArrayAdapter.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/rowArrayAdapter.ts b/src/rowArrayAdapter.ts index 13d1b053..805ea41e 100644 --- a/src/rowArrayAdapter.ts +++ b/src/rowArrayAdapter.ts @@ -75,7 +75,12 @@ export class ArrayAsAdapter { if (typeof point === "number") { if (prop) return NaN; val = point; - } else if (prop in point) { + // eslint and tsc disagree about whether or not the above condition + // is sufficient to guarentee type exclusion, tsc says no. the argument + // gets rather abstract wrt `extends`, but this is just a test implementation + // and real implementations should not support numbers anyway + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + } else if (prop in (point as SupportedDataPointType)) { // online linter wants me to specify [index: string]:number to use `in` // nor should I have to cast re: type exclusion, val = point[prop] as number; @@ -111,7 +116,8 @@ export class ArrayAsAdapter { if (typeof point === "number") { if (prop) return NaN; val = point; - } else if (prop in point) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + } else if (prop in (point as SupportedDataPointType)) { val = point[prop] as number; } else if (isOHLCDataPoint(point) && prop === "y") { val = Math.max( From 05e1825b74aee334f50c4bd94e36dac781d143f5 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 17 Mar 2024 20:21:57 -0500 Subject: [PATCH 22/34] Format and spell-check --- src/rowArrayAdapter.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rowArrayAdapter.ts b/src/rowArrayAdapter.ts index 805ea41e..fa69d4c1 100644 --- a/src/rowArrayAdapter.ts +++ b/src/rowArrayAdapter.ts @@ -76,13 +76,12 @@ export class ArrayAsAdapter { if (prop) return NaN; val = point; // eslint and tsc disagree about whether or not the above condition - // is sufficient to guarentee type exclusion, tsc says no. the argument + // is sufficient to guarantee type exclusion, tsc says no. the argument // gets rather abstract wrt `extends`, but this is just a test implementation // and real implementations should not support numbers anyway // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion } else if (prop in (point as SupportedDataPointType)) { // online linter wants me to specify [index: string]:number to use `in` - // nor should I have to cast re: type exclusion, val = point[prop] as number; } else if (isOHLCDataPoint(point) && prop === "y") { val = Math.min( From e850567b3f093da7a7a0644e9ca696eace93a2ca Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Mon, 15 Apr 2024 09:56:21 -0500 Subject: [PATCH 23/34] Add min/maxWithIndex functions to rowArrayAdapter --- src/rowArrayAdapter.ts | 67 ++++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/src/rowArrayAdapter.ts b/src/rowArrayAdapter.ts index fa69d4c1..a8e76417 100644 --- a/src/rowArrayAdapter.ts +++ b/src/rowArrayAdapter.ts @@ -12,13 +12,15 @@ import { isHighLowDataPoint, isOHLCDataPoint } from "./dataPoint"; export interface RowArrayAdapter { length: number; min: (prop: string) => number; + minWithIndex: (prop: string) => [number, number]; max: (prop: string) => number; + maxWithIndex: (prop: string) => [number, number]; at: (index: number) => T; findIndex(test: (T) => boolean): number; } /** - * Check if an object implements the RowArrayAdapter interface. + * Check if an object implements the RowArrayAdapter interface. Used where we use Array.isArray(). * @param obj - the object to check * @returns true if the object implements the interface */ @@ -68,12 +70,25 @@ export class ArrayAsAdapter { * @returns the minimum value of the array */ min(prop: string): number { - if (!this._array) return NaN; + return this.minWithIndex(prop)[1]; + } + + /** + * Implements a function like min() but returns an array of [index, value] + * @param prop - a string indicating the property that is assessed for min + * @returns [index, value] corresponding to the minimum of the row + */ + minWithIndex(prop: string): [number, number] { + if (!this._array) return [-1, NaN]; return this._array.reduce( - (localMinimum: number, point: T): number => { + ( + localMinimum: [number, number], + point: T, + currentIndex: number + ): [number, number] => { let val: number = NaN; if (typeof point === "number") { - if (prop) return NaN; + if (prop) return [-1, NaN]; val = point; // eslint and tsc disagree about whether or not the above condition // is sufficient to guarantee type exclusion, tsc says no. the argument @@ -93,12 +108,14 @@ export class ArrayAsAdapter { } else if (isHighLowDataPoint(point) && prop === "y") { val = Math.min(point.high, point.low); } else return localMinimum; - if (isNaN(localMinimum)) { - return val; + if (isNaN(localMinimum[1])) { + return [currentIndex, val]; } - return val < localMinimum ? val : localMinimum; + return val < localMinimum[1] + ? [currentIndex, val] + : localMinimum; }, - NaN // Initial value of reduce() + [-1, NaN] // Initial value of reduce() ); } @@ -108,15 +125,33 @@ export class ArrayAsAdapter { * @returns the maximum value of the array */ max(prop: string): number { - if (!this._array) return NaN; + return this.maxWithIndex(prop)[1]; + } + + /** + * Implements a function like max(), but returns an array of [index, value] + * @param prop - a string indicating the property that is assessed for min + * @returns [index, value] coresponding to the maximum of the row + */ + maxWithIndex(prop: string): [number, number] { + if (!this._array) return [-1, NaN]; return this._array.reduce( - (localMaximum: number, point: T): number => { + ( + localMaximum: [number, number], + point: T, + currentIndex: number + ): [number, number] => { let val: number = NaN; if (typeof point === "number") { - if (prop) return NaN; + if (prop) return [-1, NaN]; val = point; + // eslint and tsc disagree about whether or not the above condition + // is sufficient to guarantee type exclusion, tsc says no. the argument + // gets rather abstract wrt `extends`, but this is just a test implementation + // and real implementations should not support numbers anyway // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion } else if (prop in (point as SupportedDataPointType)) { + // online linter wants me to specify [index: string]:number to use `in` val = point[prop] as number; } else if (isOHLCDataPoint(point) && prop === "y") { val = Math.max( @@ -128,12 +163,14 @@ export class ArrayAsAdapter { } else if (isHighLowDataPoint(point) && prop === "y") { val = Math.max(point.high, point.low); } else return localMaximum; - if (isNaN(localMaximum)) { - return val; + if (isNaN(localMaximum[1])) { + return [currentIndex, val]; } - return val > localMaximum ? val : localMaximum; + return val > localMaximum[1] + ? [currentIndex, val] + : localMaximum; }, - NaN // Initial value of reduce() + [-1, NaN] // Initial value of reduce() ); } From 100c1fa25096779418c36eccdf9518e4ee11b033 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Mon, 15 Apr 2024 10:26:24 -0500 Subject: [PATCH 24/34] Return [index, value] from min/max in util.ts --- src/utils.ts | 151 +++++++++++++++++++++++++++------------------------ 1 file changed, 81 insertions(+), 70 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 0665d58d..e5456e04 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -64,6 +64,47 @@ export const calcPan = (pct: number) => (isNaN(pct) ? 0 : (pct * 2 - 1) * 0.98); const isNotNull = (tmp: unknown) => tmp !== null; +/** + * calculateRowMinimum will return [index, value] for the lowest value in the data row (a row is like a graph line, or trace, or function). + * @param row - the row in question + * @param prop - the name of property used to calculate the minimum ('y', 'y2', etc) + * @returns [index, value] - the index and value of the minimum + */ +function calculateRowMinimum( + row: SupportedDataPointType[] | RowArrayAdapter, + prop: string +): [number, number] { + // [index, value] + if (!row) return [-1, NaN]; + if (isRowArrayAdapter(row)) return row.minWithIndex(prop); + return row.reduce( + ( + localMinimum: [number, number], + point: SupportedDataPointType, + currentIndex: number + ): [number, number] => { + let val: number = NaN; + if (prop in point) { + val = point[prop] as number; + } else if (isOHLCDataPoint(point) && prop === "y") { + val = Math.min(point.high, point.low, point.open, point.close); + } else if (isHighLowDataPoint(point) && prop === "y") { + val = Math.min(point.high, point.low); + } else { + return localMinimum; + } + if (isNaN(val) || val === null) { + return localMinimum; + } + if (isNaN(localMinimum[1])) { + return [currentIndex, val]; + } + return val < localMinimum[1] ? [currentIndex, val] : localMinimum; + }, + [-1, NaN] // Initial value of reduce() + ); +} + // Question: Howcome the pre-adapter code didn't support Boxpoint for minimum/maximum? export const calculateAxisMinimum = ( data: ( @@ -78,41 +119,7 @@ export const calculateAxisMinimum = ( } const localMinimums: number[] = data - .map( - ( - row: - | SupportedDataPointType[] - | RowArrayAdapter - ): number => { - if (!row) return NaN; - if (isRowArrayAdapter(row)) return row.min(prop); - return row.reduce( - ( - localMinimum: number, - point: SupportedDataPointType - ): number => { - let val: number = NaN; - if (prop in point) { - val = point[prop] as number; - } else if (isOHLCDataPoint(point) && prop === "y") { - val = Math.min( - point.high, - point.low, - point.open, - point.close - ); - } else if (isHighLowDataPoint(point) && prop === "y") { - val = Math.min(point.high, point.low); - } else return localMinimum; - if (isNaN(localMinimum)) { - return val; - } - return val < localMinimum ? val : localMinimum; - }, - NaN // Initial value of reduce() - ); - } - ) + .map((row) => calculateRowMinimum(row, prop)[1]) .filter((num) => !isNaN(num)); if (localMinimums.length === 0) { return NaN; @@ -120,6 +127,44 @@ export const calculateAxisMinimum = ( return Math.min(...localMinimums); }; +/** + * calculateRowMaximum will return [index, value] for the highest value in the data row (a row is like a graph line, or trace, or function). + * @param row - the row in question + * @param prop - the name of property used to calculate the maximum ('y', 'y2', etc) + * @returns [index, value] - the index and value of the maximum + */ +function calculateRowMaximum( + row: SupportedDataPointType[] | RowArrayAdapter, + prop: string +): [number, number] { + if (!row) return [-1, NaN]; + if (isRowArrayAdapter(row)) return row.maxWithIndex(prop); + return row.reduce( + ( + localMaximum: [number, number], + point: SupportedDataPointType, + currentIndex: number + ): [number, number] => { + let val: number = NaN; + if (prop in point) { + val = point[prop] as number; + } else if (isOHLCDataPoint(point) && prop === "y") { + val = Math.max(point.high, point.low, point.open, point.close); + } else if (isHighLowDataPoint(point) && prop === "y") { + val = Math.max(point.high, point.low); + } else return localMaximum; + if (isNaN(val) || val === null) { + return localMaximum; + } + if (isNaN(localMaximum[1])) { + return [currentIndex, val]; + } + return val > localMaximum[1] ? [currentIndex, val] : localMaximum; + }, + [-1, NaN] // Initial value of reduce() + ); +} + export const calculateAxisMaximum = ( data: ( | SupportedDataPointType[] @@ -133,41 +178,7 @@ export const calculateAxisMaximum = ( } const localMaximums: number[] = data - .map( - ( - row: - | SupportedDataPointType[] - | RowArrayAdapter - ): number => { - if (!row) return NaN; - if (isRowArrayAdapter(row)) return row.max(prop); - return row.reduce( - ( - localMaximum: number, - point: SupportedDataPointType - ): number => { - let val: number = NaN; - if (prop in point) { - val = point[prop] as number; - } else if (isOHLCDataPoint(point) && prop === "y") { - val = Math.max( - point.high, - point.low, - point.open, - point.close - ); - } else if (isHighLowDataPoint(point) && prop === "y") { - val = Math.max(point.high, point.low); - } else return localMaximum; - if (isNaN(localMaximum)) { - return val; - } - return val > localMaximum ? val : localMaximum; - }, - NaN // Initial value of reduce() - ); - } - ) + .map((row) => calculateRowMaximum(row, prop)[1]) .filter((num) => !isNaN(num)); if (localMaximums.length === 0) { return NaN; From 95a0cbf1f6101e7090afebbce978ac4ebd9cffd5 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Mon, 15 Apr 2024 10:41:31 -0500 Subject: [PATCH 25/34] Fix calculateMeta... to use ArrayAdapter --- src/utils.ts | 50 +++++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index e5456e04..ee8b97f5 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,9 +1,4 @@ -import type { - AlternateAxisDataPoint, - OHLCDataPoint, - SimpleDataPoint, - SupportedDataPointType -} from "./dataPoint"; +import type { OHLCDataPoint, SupportedDataPointType } from "./dataPoint"; import { isOHLCDataPoint, isAlternateAxisDataPoint, @@ -280,7 +275,11 @@ export const usesAxis = ( * @param data - the X/Y values */ export const calculateMetadataByGroup = ( - data: (SupportedDataPointType[] | null)[] // TODO + data: ( + | SupportedDataPointType[] + | RowArrayAdapter + | null + )[] ): groupedMetadata[] => { return data.map((row, index) => { if (row === null) { @@ -298,44 +297,41 @@ export const calculateMetadataByGroup = ( }; } - let yValues: number[] = []; + let min = -1, + indexMin = -1, + max = -1, + indexMax = -1; let availableStats = []; - if (isSimpleDataPoint(row[0])) { - yValues = (row as SimpleDataPoint[]).map(({ y }) => y); - } else if (isAlternateAxisDataPoint(row[0])) { - yValues = (row as AlternateAxisDataPoint[]).map(({ y2 }) => y2); - } else if (isOHLCDataPoint(row[0])) { + const firstPoint = row.at(0); + if (isSimpleDataPoint(firstPoint)) { + [indexMin, min] = calculateRowMinimum(row, "y"); + [indexMax, max] = calculateRowMaximum(row, "y"); + } else if (isAlternateAxisDataPoint(firstPoint)) { + [indexMin, min] = calculateRowMinimum(row, "y2"); + [indexMax, max] = calculateRowMaximum(row, "y2"); + } else if (isOHLCDataPoint(firstPoint)) { // Don't calculate min/max for high/low availableStats = ["open", "high", "low", "close"]; - } else if (isBoxDataPoint(row[0])) { + } else if (isBoxDataPoint(firstPoint)) { availableStats = ["high", "q3", "median", "q1", "low", "outlier"]; - } else if (isHighLowDataPoint(row[0])) { + } else if (isHighLowDataPoint(firstPoint)) { // Don't calculate min/max for high/low availableStats = ["high", "low"]; } - const filteredYValues = yValues.filter((num) => !isNaN(num)); - - // Calculate min/max - // (set to -1 if there are no values to calculate, such as in the case of OHLC data) - const [min, max] = - filteredYValues.length > 0 - ? [Math.min(...filteredYValues), Math.max(...filteredYValues)] - : [-1, -1]; - // Calculate tenths const tenths = Math.round(row.length / 10); return { index, - minimumPointIndex: yValues.indexOf(min), - maximumPointIndex: yValues.indexOf(max), + minimumPointIndex: indexMin, + maximumPointIndex: indexMax, minimumValue: min, maximumValue: max, tenths, availableStats, statIndex: -1, - inputType: detectDataPointType(row[0]), + inputType: detectDataPointType(row.at(0)), size: row.length }; }); From 7e9666d17f703ae18001ed76230a4a26444ebeae Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Mon, 15 Apr 2024 11:23:20 -0500 Subject: [PATCH 26/34] Fix mabeMaybeAdapter to accept 0 proability --- test/_adapter_utilities.ts | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/test/_adapter_utilities.ts b/test/_adapter_utilities.ts index 0a0f47ca..caafd01f 100644 --- a/test/_adapter_utilities.ts +++ b/test/_adapter_utilities.ts @@ -6,33 +6,38 @@ export const probabilities = [ ]; /** - * AdapterTypeRandomizer provides a way to fuzz test, switching - * between regular arrays and adapters. + * ValidAdapterType is a list of types we'll accept to be randomized into adapters */ - type ValidAdapterType = number | SupportedDataPointType; +/** + * AdapterTypeRandomizer provides a way to fuzz test, switching + * between regular arrays and adapters. + */ export class AdapterTypeRandomizer { proportion: number; /** * constructor to set relative proportions - * @param proportion - how often should the input be wrapped in a adapter + * @param proportion - how often should the input be wrapped in an adapter + * @returns void */ constructor(proportion: number | undefined) { - this.proportion = proportion || 0.5; + if (typeof proportion === "undefined") proportion = 0.5; + this.proportion = proportion; } /** * a is the wrapper fucntion for arrays, it might return the equivalent adapter - * @param a - the array you want to wrap + * @param input - the array you want to wrap * @returns either a or a wrapped a */ - a(input: ValidAdapterType[]): adapter.RowArrayAdapter | ValidAdapterType[] { + a( + input: ValidAdapterType[] + ): adapter.RowArrayAdapter | ValidAdapterType[] { const flip = Math.random(); - - if (flip < this.proportion) return new adapter.ArrayAsAdapter(input); - + if (flip < this.proportion) + return new adapter.ArrayAsAdapter(input); return input; } } From 85e4e4ba9b1024dc50f6dade829b6c12be57c3f1 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Mon, 15 Apr 2024 11:25:10 -0500 Subject: [PATCH 27/34] Make min/max in test adapter skip nan/null vals --- src/rowArrayAdapter.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rowArrayAdapter.ts b/src/rowArrayAdapter.ts index a8e76417..f3efe239 100644 --- a/src/rowArrayAdapter.ts +++ b/src/rowArrayAdapter.ts @@ -108,6 +108,9 @@ export class ArrayAsAdapter { } else if (isHighLowDataPoint(point) && prop === "y") { val = Math.min(point.high, point.low); } else return localMinimum; + if (isNaN(val) || val === null) { + return localMinimum; + } if (isNaN(localMinimum[1])) { return [currentIndex, val]; } @@ -163,6 +166,9 @@ export class ArrayAsAdapter { } else if (isHighLowDataPoint(point) && prop === "y") { val = Math.max(point.high, point.low); } else return localMaximum; + if (isNaN(val) || val === null) { + return localMaximum; + } if (isNaN(localMaximum[1])) { return [currentIndex, val]; } From e8dd1c19e4cc5c05d12990c1dedf2289c5e407de Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Mon, 15 Apr 2024 11:26:27 -0500 Subject: [PATCH 28/34] Test calculateMeta... w/ stochastic adapter wrapping --- test/utils.test.ts | 91 +++++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/test/utils.test.ts b/test/utils.test.ts index 244a358b..3edd0aa3 100644 --- a/test/utils.test.ts +++ b/test/utils.test.ts @@ -44,47 +44,55 @@ describe("utils", () => { ) ]; const multiRow = [ - [1, 5, 2, 0, 3, 6].map((x) => { - return { x, y: 0 }; - }), - [11, 15, 12, 10, 13, 16].map((x) => { - return { x, y: 0 }; - }) + maybeMakeAdapter.a( + [1, 5, 2, 0, 3, 6].map((x) => { + return { x, y: 0 }; + }) + ), + maybeMakeAdapter.a( + [11, 15, 12, 10, 13, 16].map((x) => { + return { x, y: 0 }; + }) + ) ]; const bundledRow = [ - [ + maybeMakeAdapter.a([ { x: 5, high: 50, low: 20 }, { x: 5, high: 51, low: 15 }, { x: 5, high: 52, low: 30 }, { x: 5, high: 53, low: 45 } - ] + ]) ]; const ohlcRow = [ - [ + maybeMakeAdapter.a([ { x: 5, open: 8, high: 50, low: 20, close: 20 }, { x: 5, open: 20, high: 51, low: 15, close: 20 }, { x: 5, open: 20, high: 52, low: 30, close: 20 }, { x: 5, open: 20, high: 53, low: 45, close: 55 } - ] + ]) ]; const mixMultiRow = [ - [100, 101, 102, 103].map((y, x) => { - return { x, y }; - }), - [200, 201, 202, 203].map((y2, x) => { - return { x, y2 }; - }) + maybeMakeAdapter.a( + [100, 101, 102, 103].map((y, x) => { + return { x, y }; + }) + ), + maybeMakeAdapter.a( + [200, 201, 202, 203].map((y2, x) => { + return { x, y2 }; + }) + ) ]; const hierarchyRows = [ - [ + maybeMakeAdapter.a([ { x: 0, y: 1 }, { x: 1, y: 2 }, { x: 2, y: 3 } - ], - [ + ]), + maybeMakeAdapter.a([ { x: 3, y: 10 }, { x: 4, y: 11 } - ] + ]) ]; expect(calculateAxisMinimum(singleRow, "x")).toBe(0); expect(calculateAxisMinimum(multiRow, "x")).toBe(0); @@ -388,15 +396,16 @@ describe("utils", () => { ).toBe("0, 23, 2 of 2"); }); - test("Calculate metadata by group", () => { + test.each(probabilities)("Calculate metadata by group", (p) => { + const maybeMakeAdapter = new AdapterTypeRandomizer(p); // Simple test expect( calculateMetadataByGroup([ - [ + maybeMakeAdapter.a([ { x: 1, y: 1 }, { x: 2, y: 2 }, { x: 3, y: 3 } - ] + ]) ]) ).toEqual([ { @@ -416,16 +425,16 @@ describe("utils", () => { // Multiple groups expect( calculateMetadataByGroup([ - [ + maybeMakeAdapter.a([ { x: 1, y: 1 }, { x: 2, y: 2 }, { x: 3, y: 3 } - ], - [ + ]), + maybeMakeAdapter.a([ { x: 1, y: 8 }, { x: 2, y: 6 }, { x: 3, y: 7 } - ] + ]) ]) ).toEqual([ { @@ -457,11 +466,11 @@ describe("utils", () => { // Contains y2 expect( calculateMetadataByGroup([ - [ + maybeMakeAdapter.a([ { x: 1, y2: 1 }, { x: 2, y2: 2 }, { x: 3, y2: 3 } - ] + ]) ]) ).toEqual([ { @@ -481,11 +490,11 @@ describe("utils", () => { // Contains high/low expect( calculateMetadataByGroup([ - [ + maybeMakeAdapter.a([ { x: 1, high: 1, low: 1 }, { x: 2, high: 2, low: 2 }, { x: 3, high: 3, low: 3 } - ] + ]) ]) ).toEqual([ { @@ -505,11 +514,11 @@ describe("utils", () => { // Contains OHLC expect( calculateMetadataByGroup([ - [ + maybeMakeAdapter.a([ { x: 1, open: 1, high: 1, low: 1, close: 1 }, { x: 2, open: 2, high: 2, low: 2, close: 2 }, { x: 3, open: 3, high: 3, low: 3, close: 3 } - ] + ]) ]) ).toEqual([ { @@ -529,7 +538,7 @@ describe("utils", () => { // Contains Box plot expect( calculateMetadataByGroup([ - [ + maybeMakeAdapter.a([ { x: 0, low: 5.03, @@ -610,7 +619,7 @@ describe("utils", () => { q3: 4.96, high: 5.2 } - ] + ]) ]) ).toEqual([ { @@ -637,29 +646,29 @@ describe("utils", () => { // Minimum point when multiple values have the same minimum expect( calculateMetadataByGroup([ - [ + maybeMakeAdapter.a([ { x: 1, y: 0 }, { x: 2, y: 0 }, { x: 3, y: 0 } - ] + ]) ])[0].minimumPointIndex ).toBe(0); // Maximum point when multiple values have the same minimum expect( calculateMetadataByGroup([ - [ + maybeMakeAdapter.a([ { x: 1, y: 0 }, { x: 2, y: 3 }, { x: 3, y: 3 } - ] + ]) ])[0].maximumPointIndex ).toBe(1); // Can calculate minimum/maximum values even when there are NaNs expect( calculateMetadataByGroup([ - [ + maybeMakeAdapter.a([ { x: 0, y: 1 }, { x: 1, y: 2 }, { x: 2, y: 3 }, @@ -682,7 +691,7 @@ describe("utils", () => { { x: 2, y: 4 }, { x: 2, y: 2 }, { x: 2, y: 0 } - ] + ]) ]) ).toEqual([ { From 4ae773b94240ed1bb53cf938c7423df6720a99b1 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Tue, 16 Apr 2024 09:40:13 -0500 Subject: [PATCH 29/34] Adapt usesAxis to RowArrayAdapter w/ tests --- src/utils.ts | 18 ++++++++++++++---- test/utils.test.ts | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index ee8b97f5..180e8b19 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -261,13 +261,23 @@ export const generatePointDescription = ( }; export const usesAxis = ( - data: SupportedDataPointType[][], // TODO + data: ( + | SupportedDataPointType[] + | RowArrayAdapter + )[], axisName: "x" | "y" | "y2" -) => { +): boolean => { const firstUseOfAxis = data.filter(isNotNull).find((row) => { - return row.find((point) => axisName in point); + if (isRowArrayAdapter(row)) { + if (row.length === 0) return false; + // RowArrayAdapter doesn't support heterogenous data arrays, + // so its either 0 or not there + return axisName in row.at(0); + } else { + return row.find((point) => axisName in point); + } }); - return typeof firstUseOfAxis !== "undefined"; + return typeof firstUseOfAxis !== "undefined"; // firstUseOfAxis }; /** diff --git a/test/utils.test.ts b/test/utils.test.ts index 3edd0aa3..608c818e 100644 --- a/test/utils.test.ts +++ b/test/utils.test.ts @@ -7,6 +7,7 @@ import { generatePointDescription, calculateMetadataByGroup, detectDataPointType, + usesAxis, generateChartSummary, generateAxisSummary, convertDataRow, @@ -396,6 +397,43 @@ describe("utils", () => { ).toBe("0, 23, 2 of 2"); }); + // This test was mainly meant to get through coverage but it does not + // test a lot of possibilities + test.each(probabilities)("Test if uses axis", (p) => { + const maybeMakeAdapter = new AdapterTypeRandomizer(p); + const withoutRows: number[] = []; + const withoutPoints = [maybeMakeAdapter.a([]), maybeMakeAdapter.a([])]; + const simplePoints = [ + maybeMakeAdapter.a([ + { x: 1, y: 1 }, + { x: 2, y: 2 }, + { x: 3, y: 3 } + ]) + ]; + const alternativePoints = [ + maybeMakeAdapter.a([ + { x: 1, y2: 1 }, + { x: 2, y2: 2 }, + { x: 3, y2: 3 } + ]) + ]; + + expect(usesAxis(withoutRows, "x")).toEqual(false); + expect(usesAxis(withoutRows, "y")).toEqual(false); + expect(usesAxis(withoutRows, "y2")).toEqual(false); + + expect(usesAxis(withoutPoints, "x")).toEqual(false); + expect(usesAxis(withoutPoints, "y")).toEqual(false); + expect(usesAxis(withoutPoints, "y2")).toEqual(false); + + expect(usesAxis(simplePoints, "x")).toEqual(true); + expect(usesAxis(simplePoints, "y")).toEqual(true); + expect(usesAxis(simplePoints, "y2")).toEqual(false); + + expect(usesAxis(alternativePoints, "x")).toEqual(true); + expect(usesAxis(alternativePoints, "y")).toEqual(false); + expect(usesAxis(alternativePoints, "y2")).toEqual(true); + }); test.each(probabilities)("Calculate metadata by group", (p) => { const maybeMakeAdapter = new AdapterTypeRandomizer(p); // Simple test From b1d2871f3c1dc8bd135ebae34c7971dc9f56da89 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Tue, 16 Apr 2024 10:19:10 -0500 Subject: [PATCH 30/34] Add forEach to RowArrayAdapter interface type decl --- src/rowArrayAdapter.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/rowArrayAdapter.ts b/src/rowArrayAdapter.ts index f3efe239..2454a1f2 100644 --- a/src/rowArrayAdapter.ts +++ b/src/rowArrayAdapter.ts @@ -9,7 +9,7 @@ import { isHighLowDataPoint, isOHLCDataPoint } from "./dataPoint"; /** * An interface that imitates an array to give c2m read-access to chart data stored elsewhere. */ -export interface RowArrayAdapter { +export interface RowArrayAdapter { length: number; min: (prop: string) => number; minWithIndex: (prop: string) => [number, number]; @@ -17,6 +17,10 @@ export interface RowArrayAdapter { maxWithIndex: (prop: string) => [number, number]; at: (index: number) => T; findIndex(test: (T) => boolean): number; + forEach( + callbackFn: (value: T, index: number, array: ArrayAsAdapter) => void, + thisArg?: unknown + ): void; } /** @@ -26,7 +30,7 @@ export interface RowArrayAdapter { */ export function isRowArrayAdapter( obj: unknown -): obj is RowArrayAdapter { +): obj is RowArrayAdapter { return ( obj && typeof obj === "object" && From 5d6a470a2ff6e9928807783e530b51540fa9b074 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Tue, 16 Apr 2024 10:35:36 -0500 Subject: [PATCH 31/34] Allow adpaters in last utils methods, write tests --- src/utils.ts | 13 ++++++++++--- test/utils.test.ts | 12 ++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 180e8b19..6091d555 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -356,7 +356,10 @@ export const calculateMetadataByGroup = ( * @param filterGroupIndex - */ export const initializeAxis = ( - data: SupportedDataPointType[][], // TODO + data: ( + | SupportedDataPointType[] + | RowArrayAdapter + )[], axisName: validAxes, userAxis?: AxisData, filterGroupIndex?: number @@ -413,12 +416,16 @@ export const detectDataPointType = (query: unknown): detectableDataPoint => { }; export const convertDataRow = ( - row: (SupportedDataPointType | number)[] | null // TODO + row: + | (SupportedDataPointType | number)[] + | RowArrayAdapter + | null ) => { if (row === null) { return null; } - + // If it's already a rowArrayAdapter we def don't need to do a conversion + if (isRowArrayAdapter(row)) return row; return row.map((point: number | SupportedDataPointType, index: number) => { if (typeof point === "number") { return { diff --git a/test/utils.test.ts b/test/utils.test.ts index 608c818e..b8913e16 100644 --- a/test/utils.test.ts +++ b/test/utils.test.ts @@ -14,6 +14,7 @@ import { detectIfMobile } from "../src/utils"; import { AdapterTypeRandomizer, probabilities } from "./_adapter_utilities"; +import { ArrayAsAdapter } from "../src/rowArrayAdapter"; describe("utils", () => { test("interpolate bin - linear", () => { @@ -899,8 +900,19 @@ describe("utils", () => { ).toBe(`Alternate Y is "Revenue" from $0 to $1,000,000.`); }); + // Probabilistic type conversions don't make sense here as we're testing type conversions test("convertDataRow", () => { + const simplePoints = [ + { x: 0, y: 1 }, + { x: 1, y: 2 }, + { x: 2, y: 3 } + ]; + const adaptedSimplePoints = new ArrayAsAdapter( + simplePoints + ); expect(convertDataRow(null)).toBeNull(); + expect(convertDataRow([1, 2, 3])).toEqual(simplePoints); + expect(convertDataRow(adaptedSimplePoints)).toBe(adaptedSimplePoints); }); test("detectIfMobile", () => { From 6b4d65ee1c7dbf2685fa064338b34d612c5ed45b Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Tue, 16 Apr 2024 11:27:28 -0500 Subject: [PATCH 32/34] Revert "Merge branch 'main' into pikul-feature-array-...:" This reverts commit ceb36c74a377c98f759850b34ce0db4a2fa4d2b8, reversing changes made to 5d6a470a2ff6e9928807783e530b51540fa9b074. --- .github/workflows/publish.yml | 22 -- changelog.md | 7 - package-lock.json | 558 ++++++++++++++++------------------ package.json | 10 +- src/c2mChart.ts | 55 ++-- src/utils.ts | 4 +- 6 files changed, 286 insertions(+), 370 deletions(-) delete mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 7d1e1bb2..00000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Publish npm package -on: - workflow_dispatch: -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - # Setup .npmrc file to publish to npm - - uses: actions/setup-node@v4 - with: - node-version: '20.x' - registry-url: 'https://registry.npmjs.org' - - - run: npm ci - - - run: npm run build - - - run: npm publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/changelog.md b/changelog.md index ca15282b..2b5d8907 100644 --- a/changelog.md +++ b/changelog.md @@ -1,14 +1,7 @@ # Changelog -## 1.16.2 -* Use the `.at()` method when working with provided arrays. This is an early step to provide support for spoofed arrays. - -## 1.16.1 -* When the .cleanUp method is called, the help dialog should be removed from the DOM. - ## 1.16.0 * Add a .cleanUp() method, which removes event listeners and attributes from the provided chart element -* Add classnames to dialogs. All dialogs will have the classname `chart2music-dialog`. Each dialog also has its own specific classname: `chart2music-help-dialog`, `chart2music-info-dialog`, and `chart2music-option-dialog`. ## 1.15.0 * Added French, German, and Italian translations (thank you [glow](https://github.com/Neo-Oli) and their team at the [Swiss Red ross](https://github.com/swissredcross)) diff --git a/package-lock.json b/package-lock.json index 3b7f516a..d4fe432a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "chart2music", - "version": "1.16.2", + "version": "1.16.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "chart2music", - "version": "1.16.2", + "version": "1.16.0", "license": "MIT", "dependencies": { "@formatjs/intl": "^2.9.11" @@ -19,8 +19,8 @@ "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "11.1.6", "@types/jest": "^29.2.1", - "@typescript-eslint/eslint-plugin": "7.6.0", - "@typescript-eslint/parser": "7.6.0", + "@typescript-eslint/eslint-plugin": "7.5.0", + "@typescript-eslint/parser": "7.5.0", "concurrently": "8.2.2", "depcheck": "1.4.7", "eslint": "8.57.0", @@ -33,12 +33,12 @@ "lint-staged": "15.2.2", "prettier": "3.2.5", "rimraf": "5.0.5", - "rollup": "4.14.2", + "rollup": "4.13.0", "rollup-plugin-delete": "2.0.0", "rollup-plugin-dts": "6.1.0", "ts-jest": "29.1.2", "tslib": "2.6.2", - "typescript": "5.4.5" + "typescript": "5.4.3" }, "engines": { "node": ">16.14" @@ -2047,9 +2047,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -2786,9 +2786,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.2.tgz", - "integrity": "sha512-ahxSgCkAEk+P/AVO0vYr7DxOD3CwAQrT0Go9BJyGQ9Ef0QxVOfjDZMiF4Y2s3mLyPrjonchIMH/tbWHucJMykQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", + "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", "cpu": [ "arm" ], @@ -2799,9 +2799,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.2.tgz", - "integrity": "sha512-lAarIdxZWbFSHFSDao9+I/F5jDaKyCqAPMq5HqnfpBw8dKDiCaaqM0lq5h1pQTLeIqueeay4PieGR5jGZMWprw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", + "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", "cpu": [ "arm64" ], @@ -2812,9 +2812,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.2.tgz", - "integrity": "sha512-SWsr8zEUk82KSqquIMgZEg2GE5mCSfr9sE/thDROkX6pb3QQWPp8Vw8zOq2GyxZ2t0XoSIUlvHDkrf5Gmf7x3Q==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", + "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", "cpu": [ "arm64" ], @@ -2825,9 +2825,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.2.tgz", - "integrity": "sha512-o/HAIrQq0jIxJAhgtIvV5FWviYK4WB0WwV91SLUnsliw1lSAoLsmgEEgRWzDguAFeUEUUoIWXiJrPqU7vGiVkA==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", + "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", "cpu": [ "x64" ], @@ -2838,9 +2838,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.2.tgz", - "integrity": "sha512-nwlJ65UY9eGq91cBi6VyDfArUJSKOYt5dJQBq8xyLhvS23qO+4Nr/RreibFHjP6t+5ap2ohZrUJcHv5zk5ju/g==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", + "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", "cpu": [ "arm" ], @@ -2851,9 +2851,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.2.tgz", - "integrity": "sha512-Pg5TxxO2IVlMj79+c/9G0LREC9SY3HM+pfAwX7zj5/cAuwrbfj2Wv9JbMHIdPCfQpYsI4g9mE+2Bw/3aeSs2rQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", + "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", "cpu": [ "arm64" ], @@ -2864,9 +2864,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.2.tgz", - "integrity": "sha512-cAOTjGNm84gc6tS02D1EXtG7tDRsVSDTBVXOLbj31DkwfZwgTPYZ6aafSU7rD/4R2a34JOwlF9fQayuTSkoclA==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", + "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", "cpu": [ "arm64" ], @@ -2876,23 +2876,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.2.tgz", - "integrity": "sha512-4RyT6v1kXb7C0fn6zV33rvaX05P0zHoNzaXI/5oFHklfKm602j+N4mn2YvoezQViRLPnxP8M1NaY4s/5kXO5cw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.2.tgz", - "integrity": "sha512-KNUH6jC/vRGAKSorySTyc/yRYlCwN/5pnMjXylfBniwtJx5O7X17KG/0efj8XM3TZU7raYRXJFFReOzNmL1n1w==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", + "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", "cpu": [ "riscv64" ], @@ -2902,23 +2889,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.2.tgz", - "integrity": "sha512-xPV4y73IBEXToNPa3h5lbgXOi/v0NcvKxU0xejiFw6DtIYQqOTMhZ2DN18/HrrP0PmiL3rGtRG9gz1QE8vFKXQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.2.tgz", - "integrity": "sha512-QBhtr07iFGmF9egrPOWyO5wciwgtzKkYPNLVCFZTmr4TWmY0oY2Dm/bmhHjKRwZoGiaKdNcKhFtUMBKvlchH+Q==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", + "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", "cpu": [ "x64" ], @@ -2929,9 +2903,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.2.tgz", - "integrity": "sha512-8zfsQRQGH23O6qazZSFY5jP5gt4cFvRuKTpuBsC1ZnSWxV8ZKQpPqOZIUtdfMOugCcBvFGRa1pDC/tkf19EgBw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", + "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", "cpu": [ "x64" ], @@ -2942,9 +2916,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.2.tgz", - "integrity": "sha512-H4s8UjgkPnlChl6JF5empNvFHp77Jx+Wfy2EtmYPe9G22XV+PMuCinZVHurNe8ggtwoaohxARJZbaH/3xjB/FA==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", + "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", "cpu": [ "arm64" ], @@ -2955,9 +2929,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.2.tgz", - "integrity": "sha512-djqpAjm/i8erWYF0K6UY4kRO3X5+T4TypIqw60Q8MTqSBaQNpNXDhxdjpZ3ikgb+wn99svA7jxcXpiyg9MUsdw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", + "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", "cpu": [ "ia32" ], @@ -2968,9 +2942,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.2.tgz", - "integrity": "sha512-teAqzLT0yTYZa8ZP7zhFKEx4cotS8Tkk5XiqNMJhD4CpaWB1BHARE4Qy+RzwnXvSAYv+Q3jAqCVBS+PS+Yee8Q==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", + "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", "cpu": [ "x64" ], @@ -3188,22 +3162,22 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.6.0.tgz", - "integrity": "sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.5.0.tgz", + "integrity": "sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.6.0", - "@typescript-eslint/type-utils": "7.6.0", - "@typescript-eslint/utils": "7.6.0", - "@typescript-eslint/visitor-keys": "7.6.0", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "7.5.0", + "@typescript-eslint/type-utils": "7.5.0", + "@typescript-eslint/utils": "7.5.0", + "@typescript-eslint/visitor-keys": "7.5.0", "debug": "^4.3.4", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^5.2.4", "natural-compare": "^1.4.0", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3223,15 +3197,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.5.0.tgz", + "integrity": "sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.6.0", - "@typescript-eslint/types": "7.6.0", - "@typescript-eslint/typescript-estree": "7.6.0", - "@typescript-eslint/visitor-keys": "7.6.0", + "@typescript-eslint/scope-manager": "7.5.0", + "@typescript-eslint/types": "7.5.0", + "@typescript-eslint/typescript-estree": "7.5.0", + "@typescript-eslint/visitor-keys": "7.5.0", "debug": "^4.3.4" }, "engines": { @@ -3251,13 +3225,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz", - "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.5.0.tgz", + "integrity": "sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.6.0", - "@typescript-eslint/visitor-keys": "7.6.0" + "@typescript-eslint/types": "7.5.0", + "@typescript-eslint/visitor-keys": "7.5.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3268,15 +3242,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.6.0.tgz", - "integrity": "sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.5.0.tgz", + "integrity": "sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.6.0", - "@typescript-eslint/utils": "7.6.0", + "@typescript-eslint/typescript-estree": "7.5.0", + "@typescript-eslint/utils": "7.5.0", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^1.0.1" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3295,9 +3269,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz", - "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.5.0.tgz", + "integrity": "sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3308,19 +3282,19 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz", - "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.5.0.tgz", + "integrity": "sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.6.0", - "@typescript-eslint/visitor-keys": "7.6.0", + "@typescript-eslint/types": "7.5.0", + "@typescript-eslint/visitor-keys": "7.5.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3345,9 +3319,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -3360,18 +3334,18 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.6.0.tgz", - "integrity": "sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.5.0.tgz", + "integrity": "sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.15", - "@types/semver": "^7.5.8", - "@typescript-eslint/scope-manager": "7.6.0", - "@typescript-eslint/types": "7.6.0", - "@typescript-eslint/typescript-estree": "7.6.0", - "semver": "^7.6.0" + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "7.5.0", + "@typescript-eslint/types": "7.5.0", + "@typescript-eslint/typescript-estree": "7.5.0", + "semver": "^7.5.4" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3385,13 +3359,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz", - "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.5.0.tgz", + "integrity": "sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.6.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "7.5.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -5936,9 +5910,9 @@ } }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { "node": ">= 4" @@ -8484,9 +8458,9 @@ } }, "node_modules/rollup": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.2.tgz", - "integrity": "sha512-WkeoTWvuBoFjFAhsEOHKRoZ3r9GfTyhh7Vff1zwebEFLEFjT1lG3784xEgKiTa7E+e70vsC81roVL2MP4tgEEQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", + "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -8499,21 +8473,19 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.14.2", - "@rollup/rollup-android-arm64": "4.14.2", - "@rollup/rollup-darwin-arm64": "4.14.2", - "@rollup/rollup-darwin-x64": "4.14.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.14.2", - "@rollup/rollup-linux-arm64-gnu": "4.14.2", - "@rollup/rollup-linux-arm64-musl": "4.14.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.14.2", - "@rollup/rollup-linux-riscv64-gnu": "4.14.2", - "@rollup/rollup-linux-s390x-gnu": "4.14.2", - "@rollup/rollup-linux-x64-gnu": "4.14.2", - "@rollup/rollup-linux-x64-musl": "4.14.2", - "@rollup/rollup-win32-arm64-msvc": "4.14.2", - "@rollup/rollup-win32-ia32-msvc": "4.14.2", - "@rollup/rollup-win32-x64-msvc": "4.14.2", + "@rollup/rollup-android-arm-eabi": "4.13.0", + "@rollup/rollup-android-arm64": "4.13.0", + "@rollup/rollup-darwin-arm64": "4.13.0", + "@rollup/rollup-darwin-x64": "4.13.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", + "@rollup/rollup-linux-arm64-gnu": "4.13.0", + "@rollup/rollup-linux-arm64-musl": "4.13.0", + "@rollup/rollup-linux-riscv64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-musl": "4.13.0", + "@rollup/rollup-win32-arm64-msvc": "4.13.0", + "@rollup/rollup-win32-ia32-msvc": "4.13.0", + "@rollup/rollup-win32-x64-msvc": "4.13.0", "fsevents": "~2.3.2" } }, @@ -9078,12 +9050,12 @@ } }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.2.tgz", + "integrity": "sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ==", "dev": true, "engines": { - "node": ">=16" + "node": ">=16.13.0" }, "peerDependencies": { "typescript": ">=4.2.0" @@ -9171,9 +9143,9 @@ } }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", + "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", "devOptional": true, "bin": { "tsc": "bin/tsc", @@ -11065,9 +11037,9 @@ } }, "@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true }, "@eslint/eslintrc": { @@ -11622,107 +11594,93 @@ } }, "@rollup/rollup-android-arm-eabi": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.2.tgz", - "integrity": "sha512-ahxSgCkAEk+P/AVO0vYr7DxOD3CwAQrT0Go9BJyGQ9Ef0QxVOfjDZMiF4Y2s3mLyPrjonchIMH/tbWHucJMykQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", + "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", "dev": true, "optional": true }, "@rollup/rollup-android-arm64": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.2.tgz", - "integrity": "sha512-lAarIdxZWbFSHFSDao9+I/F5jDaKyCqAPMq5HqnfpBw8dKDiCaaqM0lq5h1pQTLeIqueeay4PieGR5jGZMWprw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", + "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", "dev": true, "optional": true }, "@rollup/rollup-darwin-arm64": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.2.tgz", - "integrity": "sha512-SWsr8zEUk82KSqquIMgZEg2GE5mCSfr9sE/thDROkX6pb3QQWPp8Vw8zOq2GyxZ2t0XoSIUlvHDkrf5Gmf7x3Q==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", + "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", "dev": true, "optional": true }, "@rollup/rollup-darwin-x64": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.2.tgz", - "integrity": "sha512-o/HAIrQq0jIxJAhgtIvV5FWviYK4WB0WwV91SLUnsliw1lSAoLsmgEEgRWzDguAFeUEUUoIWXiJrPqU7vGiVkA==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", + "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.2.tgz", - "integrity": "sha512-nwlJ65UY9eGq91cBi6VyDfArUJSKOYt5dJQBq8xyLhvS23qO+4Nr/RreibFHjP6t+5ap2ohZrUJcHv5zk5ju/g==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", + "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-gnu": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.2.tgz", - "integrity": "sha512-Pg5TxxO2IVlMj79+c/9G0LREC9SY3HM+pfAwX7zj5/cAuwrbfj2Wv9JbMHIdPCfQpYsI4g9mE+2Bw/3aeSs2rQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", + "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-musl": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.2.tgz", - "integrity": "sha512-cAOTjGNm84gc6tS02D1EXtG7tDRsVSDTBVXOLbj31DkwfZwgTPYZ6aafSU7rD/4R2a34JOwlF9fQayuTSkoclA==", - "dev": true, - "optional": true - }, - "@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.2.tgz", - "integrity": "sha512-4RyT6v1kXb7C0fn6zV33rvaX05P0zHoNzaXI/5oFHklfKm602j+N4mn2YvoezQViRLPnxP8M1NaY4s/5kXO5cw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", + "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", "dev": true, "optional": true }, "@rollup/rollup-linux-riscv64-gnu": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.2.tgz", - "integrity": "sha512-KNUH6jC/vRGAKSorySTyc/yRYlCwN/5pnMjXylfBniwtJx5O7X17KG/0efj8XM3TZU7raYRXJFFReOzNmL1n1w==", - "dev": true, - "optional": true - }, - "@rollup/rollup-linux-s390x-gnu": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.2.tgz", - "integrity": "sha512-xPV4y73IBEXToNPa3h5lbgXOi/v0NcvKxU0xejiFw6DtIYQqOTMhZ2DN18/HrrP0PmiL3rGtRG9gz1QE8vFKXQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", + "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-gnu": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.2.tgz", - "integrity": "sha512-QBhtr07iFGmF9egrPOWyO5wciwgtzKkYPNLVCFZTmr4TWmY0oY2Dm/bmhHjKRwZoGiaKdNcKhFtUMBKvlchH+Q==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", + "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-musl": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.2.tgz", - "integrity": "sha512-8zfsQRQGH23O6qazZSFY5jP5gt4cFvRuKTpuBsC1ZnSWxV8ZKQpPqOZIUtdfMOugCcBvFGRa1pDC/tkf19EgBw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", + "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", "dev": true, "optional": true }, "@rollup/rollup-win32-arm64-msvc": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.2.tgz", - "integrity": "sha512-H4s8UjgkPnlChl6JF5empNvFHp77Jx+Wfy2EtmYPe9G22XV+PMuCinZVHurNe8ggtwoaohxARJZbaH/3xjB/FA==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", + "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", "dev": true, "optional": true }, "@rollup/rollup-win32-ia32-msvc": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.2.tgz", - "integrity": "sha512-djqpAjm/i8erWYF0K6UY4kRO3X5+T4TypIqw60Q8MTqSBaQNpNXDhxdjpZ3ikgb+wn99svA7jxcXpiyg9MUsdw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", + "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", "dev": true, "optional": true }, "@rollup/rollup-win32-x64-msvc": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.2.tgz", - "integrity": "sha512-teAqzLT0yTYZa8ZP7zhFKEx4cotS8Tkk5XiqNMJhD4CpaWB1BHARE4Qy+RzwnXvSAYv+Q3jAqCVBS+PS+Yee8Q==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", + "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", "dev": true, "optional": true }, @@ -11931,79 +11889,79 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.6.0.tgz", - "integrity": "sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.5.0.tgz", + "integrity": "sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==", "dev": true, "requires": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.6.0", - "@typescript-eslint/type-utils": "7.6.0", - "@typescript-eslint/utils": "7.6.0", - "@typescript-eslint/visitor-keys": "7.6.0", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "7.5.0", + "@typescript-eslint/type-utils": "7.5.0", + "@typescript-eslint/utils": "7.5.0", + "@typescript-eslint/visitor-keys": "7.5.0", "debug": "^4.3.4", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^5.2.4", "natural-compare": "^1.4.0", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/parser": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.5.0.tgz", + "integrity": "sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "7.6.0", - "@typescript-eslint/types": "7.6.0", - "@typescript-eslint/typescript-estree": "7.6.0", - "@typescript-eslint/visitor-keys": "7.6.0", + "@typescript-eslint/scope-manager": "7.5.0", + "@typescript-eslint/types": "7.5.0", + "@typescript-eslint/typescript-estree": "7.5.0", + "@typescript-eslint/visitor-keys": "7.5.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz", - "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.5.0.tgz", + "integrity": "sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==", "dev": true, "requires": { - "@typescript-eslint/types": "7.6.0", - "@typescript-eslint/visitor-keys": "7.6.0" + "@typescript-eslint/types": "7.5.0", + "@typescript-eslint/visitor-keys": "7.5.0" } }, "@typescript-eslint/type-utils": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.6.0.tgz", - "integrity": "sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.5.0.tgz", + "integrity": "sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "7.6.0", - "@typescript-eslint/utils": "7.6.0", + "@typescript-eslint/typescript-estree": "7.5.0", + "@typescript-eslint/utils": "7.5.0", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/types": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz", - "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.5.0.tgz", + "integrity": "sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz", - "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.5.0.tgz", + "integrity": "sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==", "dev": true, "requires": { - "@typescript-eslint/types": "7.6.0", - "@typescript-eslint/visitor-keys": "7.6.0", + "@typescript-eslint/types": "7.5.0", + "@typescript-eslint/visitor-keys": "7.5.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "dependencies": { "brace-expansion": { @@ -12016,9 +11974,9 @@ } }, "minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -12027,28 +11985,28 @@ } }, "@typescript-eslint/utils": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.6.0.tgz", - "integrity": "sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.5.0.tgz", + "integrity": "sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.15", - "@types/semver": "^7.5.8", - "@typescript-eslint/scope-manager": "7.6.0", - "@typescript-eslint/types": "7.6.0", - "@typescript-eslint/typescript-estree": "7.6.0", - "semver": "^7.6.0" + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "7.5.0", + "@typescript-eslint/types": "7.5.0", + "@typescript-eslint/typescript-estree": "7.5.0", + "semver": "^7.5.4" } }, "@typescript-eslint/visitor-keys": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz", - "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.5.0.tgz", + "integrity": "sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==", "dev": true, "requires": { - "@typescript-eslint/types": "7.6.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "7.5.0", + "eslint-visitor-keys": "^3.4.1" } }, "@ungap/structured-clone": { @@ -13939,9 +13897,9 @@ } }, "ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true }, "import-fresh": { @@ -15763,26 +15721,24 @@ } }, "rollup": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.2.tgz", - "integrity": "sha512-WkeoTWvuBoFjFAhsEOHKRoZ3r9GfTyhh7Vff1zwebEFLEFjT1lG3784xEgKiTa7E+e70vsC81roVL2MP4tgEEQ==", - "dev": true, - "requires": { - "@rollup/rollup-android-arm-eabi": "4.14.2", - "@rollup/rollup-android-arm64": "4.14.2", - "@rollup/rollup-darwin-arm64": "4.14.2", - "@rollup/rollup-darwin-x64": "4.14.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.14.2", - "@rollup/rollup-linux-arm64-gnu": "4.14.2", - "@rollup/rollup-linux-arm64-musl": "4.14.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.14.2", - "@rollup/rollup-linux-riscv64-gnu": "4.14.2", - "@rollup/rollup-linux-s390x-gnu": "4.14.2", - "@rollup/rollup-linux-x64-gnu": "4.14.2", - "@rollup/rollup-linux-x64-musl": "4.14.2", - "@rollup/rollup-win32-arm64-msvc": "4.14.2", - "@rollup/rollup-win32-ia32-msvc": "4.14.2", - "@rollup/rollup-win32-x64-msvc": "4.14.2", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", + "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.13.0", + "@rollup/rollup-android-arm64": "4.13.0", + "@rollup/rollup-darwin-arm64": "4.13.0", + "@rollup/rollup-darwin-x64": "4.13.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", + "@rollup/rollup-linux-arm64-gnu": "4.13.0", + "@rollup/rollup-linux-arm64-musl": "4.13.0", + "@rollup/rollup-linux-riscv64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-musl": "4.13.0", + "@rollup/rollup-win32-arm64-msvc": "4.13.0", + "@rollup/rollup-win32-ia32-msvc": "4.13.0", + "@rollup/rollup-win32-x64-msvc": "4.13.0", "@types/estree": "1.0.5", "fsevents": "~2.3.2" } @@ -16199,9 +16155,9 @@ "dev": true }, "ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.2.tgz", + "integrity": "sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ==", "dev": true, "requires": {} }, @@ -16248,9 +16204,9 @@ "dev": true }, "typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", + "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", "devOptional": true }, "unicode-canonical-property-names-ecmascript": { diff --git a/package.json b/package.json index 85ef0001..99ddbf62 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chart2music", - "version": "1.16.2", + "version": "1.16.0", "main": "dist/index.js", "module": "dist/index.mjs", "exports": { @@ -58,8 +58,8 @@ "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "11.1.6", "@types/jest": "^29.2.1", - "@typescript-eslint/eslint-plugin": "7.6.0", - "@typescript-eslint/parser": "7.6.0", + "@typescript-eslint/eslint-plugin": "7.5.0", + "@typescript-eslint/parser": "7.5.0", "concurrently": "8.2.2", "depcheck": "1.4.7", "eslint": "8.57.0", @@ -72,12 +72,12 @@ "lint-staged": "15.2.2", "prettier": "3.2.5", "rimraf": "5.0.5", - "rollup": "4.14.2", + "rollup": "4.13.0", "rollup-plugin-delete": "2.0.0", "rollup-plugin-dts": "6.1.0", "ts-jest": "29.1.2", "tslib": "2.6.2", - "typescript": "5.4.5" + "typescript": "5.4.3" }, "dependencies": { "@formatjs/intl": "^2.9.11" diff --git a/src/c2mChart.ts b/src/c2mChart.ts index 8702747a..4acaa30c 100644 --- a/src/c2mChart.ts +++ b/src/c2mChart.ts @@ -48,16 +48,6 @@ import { launchOptionDialog } from "./optionDialog"; import { launchInfoDialog } from "./infoDialog"; import { AudioNotificationType } from "./audio/AudioEngine"; import { DEFAULT_LANGUAGE, translate, AVAILABLE_LANGUAGES } from "./translator"; -import type { RowArrayAdapter } from "./rowArrayAdapter"; - -/** - * ValidUserInputRows are the types the user can submit as data rows. - * Note that number will be converted to SimpleDataPoint[], - * so the effective types we accept is more restrictive. - */ -type ValidUserInputRows = - | RowArrayAdapter - | (number | SupportedDataPointType)[]; /** * Metadata about previous levels. Used to quickly return to parents. @@ -275,7 +265,7 @@ export class c2m { * Index for the current group */ get _groupIndex() { - return this._visible_group_indices.at(this._visibleGroupIndex); + return this._visible_group_indices[this._visibleGroupIndex]; } /** @@ -291,7 +281,7 @@ export class c2m { private get _currentGroupType() { if (Array.isArray(this._type)) { // Example type: ["bar", "line"] - return this._type.at(this._visibleGroupIndex); + return this._type[this._visibleGroupIndex]; } else { // Example type: "bar" return this._type; @@ -302,7 +292,7 @@ export class c2m { * The current group's data */ private get _currentDataRow() { - return this._data.at(this._groupIndex); + return this._data[this._groupIndex]; } /** @@ -323,14 +313,14 @@ export class c2m { if (this._currentDataRow === null) { return null; } - return this._currentDataRow.at(this._pointIndex); + return this._currentDataRow[this._pointIndex]; } /** * Get the name of the current group */ private get _currentGroupName() { - return this._groups.at(this._groupIndex); + return this._groups[this._groupIndex]; } /** @@ -413,7 +403,7 @@ export class c2m { this._visibleGroupIndex++; this._playCurrent(); } - }, SPEEDS.at(this._speedRateIndex)); + }, SPEEDS[this._speedRateIndex]); this._playCurrent(); }, play_backward_category: () => { @@ -430,7 +420,7 @@ export class c2m { this._visibleGroupIndex--; this._playCurrent(); } - }, SPEEDS.at(this._speedRateIndex)) as NodeJS.Timeout; + }, SPEEDS[this._speedRateIndex]) as NodeJS.Timeout; this._playCurrent(); }, stop_play: () => { @@ -609,7 +599,7 @@ export class c2m { } this._sr.render( translate(this._language, "kbr-speed", { - rate_in_ms: SPEEDS.at(this._speedRateIndex) + rate_in_ms: SPEEDS[this._speedRateIndex] }) ); }, @@ -620,7 +610,7 @@ export class c2m { } this._sr.render( translate(this._language, "kbr-speed", { - rate_in_ms: SPEEDS.at(this._speedRateIndex) + rate_in_ms: SPEEDS[this._speedRateIndex] }) ); }, @@ -662,7 +652,7 @@ export class c2m { this._speedRateIndex = speedIndex; this._sr.render( translate(this._language, "kbr-speed", { - rate_in_ms: SPEEDS.at(this._speedRateIndex) + rate_in_ms: SPEEDS[this._speedRateIndex] }) ); } @@ -674,7 +664,7 @@ export class c2m { }, (hertzIndex: number) => { this._audioEngine?.playDataPoint( - this._options.hertzes.at(hertzIndex), + this._options.hertzes[hertzIndex], 0, NOTE_LENGTH ); @@ -1395,14 +1385,14 @@ export class c2m { (value, index) => index ); this._data = Object.values(userData).map((row) => - convertDataRow(row as ValidUserInputRows) + convertDataRow(row) ); return; } this._groups = [""]; this._visible_group_indices = [0]; - this._data = [convertDataRow(userData as ValidUserInputRows)]; + this._data = [convertDataRow(userData)]; } /** @@ -1721,7 +1711,7 @@ export class c2m { this._outlierIndex--; this._playCurrent(); } - }, SPEEDS.at(this._speedRateIndex)) as NodeJS.Timeout; + }, SPEEDS[this._speedRateIndex]) as NodeJS.Timeout; this._playCurrent(); } @@ -1746,7 +1736,7 @@ export class c2m { this._pointIndex--; this._playCurrent(); } - }, SPEEDS.at(this._speedRateIndex)) as NodeJS.Timeout; + }, SPEEDS[this._speedRateIndex]) as NodeJS.Timeout; this._playCurrent(); } @@ -1771,7 +1761,7 @@ export class c2m { this._outlierIndex++; this._playCurrent(); } - }, SPEEDS.at(this._speedRateIndex)); + }, SPEEDS[this._speedRateIndex]); this._playCurrent(); } @@ -1782,7 +1772,7 @@ export class c2m { const startIndex = this._pointIndex; const startX = this.getCurrent().point.x; const row = this._currentDataRow.slice(startIndex); - const totalTime = SPEEDS.at(this._speedRateIndex) * 10; + const totalTime = SPEEDS[this._speedRateIndex] * 10; const xMin = this._xAxis.minimum; const range = this._xAxis.maximum - xMin; const change = @@ -1817,7 +1807,7 @@ export class c2m { const startIndex = this._pointIndex; const startX = this.getCurrent().point.x; const row = this._currentDataRow.slice(0, startIndex + 1); - const totalTime = SPEEDS.at(this._speedRateIndex) * 10; + const totalTime = SPEEDS[this._speedRateIndex] * 10; const xMin = this._xAxis.minimum; const range = this._xAxis.maximum - xMin; const change = @@ -1867,7 +1857,7 @@ export class c2m { this._pointIndex++; this._playCurrent(); } - }, SPEEDS.at(this._speedRateIndex)); + }, SPEEDS[this._speedRateIndex]); this._playCurrent(); } @@ -2145,7 +2135,7 @@ export class c2m { NOTE_LENGTH ); }, - SPEEDS.at(this._speedRateIndex) * interval * index + SPEEDS[this._speedRateIndex] * interval * index ); }); } @@ -2180,9 +2170,8 @@ export class c2m { return; } - const { statIndex, availableStats } = this._metadataByGroup.at( - this._groupIndex - ); + const { statIndex, availableStats } = + this._metadataByGroup[this._groupIndex]; if (this._flagNewStat && availableStats.length === 0) { this._flagNewStat = false; } diff --git a/src/utils.ts b/src/utils.ts index 070a6585..6091d555 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -206,7 +206,7 @@ export const generatePointDescription = ( if (isBoxDataPoint(point) && outlierIndex !== null) { return translate(language, "point-outlier", { x: xFormat(point.x), - y: point.outlier.at(outlierIndex), + y: point.outlier[outlierIndex], index: outlierIndex + 1, count: point.outlier.length }); @@ -584,7 +584,7 @@ export const checkForNumberInput = ( data: SonifyTypes["data"] ) => { if (Array.isArray(data) && typeof data[0] === "number") { - metadataByGroup.at(0).inputType = "number"; + metadataByGroup[0].inputType = "number"; } else { let index = 0; for (const group in data) { From ce64aade6fb3b2844796d047e67fc42af66eb966 Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 28 Apr 2024 19:43:57 -0500 Subject: [PATCH 33/34] Reapply "Merge branch 'main' into pikul-feature-array-...:" This reverts commit 6b4d65ee1c7dbf2685fa064338b34d612c5ed45b. --- .github/workflows/publish.yml | 22 ++ changelog.md | 7 + package-lock.json | 558 ++++++++++++++++++---------------- package.json | 10 +- src/c2mChart.ts | 55 ++-- src/utils.ts | 4 +- 6 files changed, 370 insertions(+), 286 deletions(-) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..7d1e1bb2 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,22 @@ +name: Publish npm package +on: + workflow_dispatch: +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # Setup .npmrc file to publish to npm + - uses: actions/setup-node@v4 + with: + node-version: '20.x' + registry-url: 'https://registry.npmjs.org' + + - run: npm ci + + - run: npm run build + + - run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/changelog.md b/changelog.md index 2b5d8907..ca15282b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,14 @@ # Changelog +## 1.16.2 +* Use the `.at()` method when working with provided arrays. This is an early step to provide support for spoofed arrays. + +## 1.16.1 +* When the .cleanUp method is called, the help dialog should be removed from the DOM. + ## 1.16.0 * Add a .cleanUp() method, which removes event listeners and attributes from the provided chart element +* Add classnames to dialogs. All dialogs will have the classname `chart2music-dialog`. Each dialog also has its own specific classname: `chart2music-help-dialog`, `chart2music-info-dialog`, and `chart2music-option-dialog`. ## 1.15.0 * Added French, German, and Italian translations (thank you [glow](https://github.com/Neo-Oli) and their team at the [Swiss Red ross](https://github.com/swissredcross)) diff --git a/package-lock.json b/package-lock.json index d4fe432a..3b7f516a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "chart2music", - "version": "1.16.0", + "version": "1.16.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "chart2music", - "version": "1.16.0", + "version": "1.16.2", "license": "MIT", "dependencies": { "@formatjs/intl": "^2.9.11" @@ -19,8 +19,8 @@ "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "11.1.6", "@types/jest": "^29.2.1", - "@typescript-eslint/eslint-plugin": "7.5.0", - "@typescript-eslint/parser": "7.5.0", + "@typescript-eslint/eslint-plugin": "7.6.0", + "@typescript-eslint/parser": "7.6.0", "concurrently": "8.2.2", "depcheck": "1.4.7", "eslint": "8.57.0", @@ -33,12 +33,12 @@ "lint-staged": "15.2.2", "prettier": "3.2.5", "rimraf": "5.0.5", - "rollup": "4.13.0", + "rollup": "4.14.2", "rollup-plugin-delete": "2.0.0", "rollup-plugin-dts": "6.1.0", "ts-jest": "29.1.2", "tslib": "2.6.2", - "typescript": "5.4.3" + "typescript": "5.4.5" }, "engines": { "node": ">16.14" @@ -2047,9 +2047,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", - "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -2786,9 +2786,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", - "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.2.tgz", + "integrity": "sha512-ahxSgCkAEk+P/AVO0vYr7DxOD3CwAQrT0Go9BJyGQ9Ef0QxVOfjDZMiF4Y2s3mLyPrjonchIMH/tbWHucJMykQ==", "cpu": [ "arm" ], @@ -2799,9 +2799,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", - "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.2.tgz", + "integrity": "sha512-lAarIdxZWbFSHFSDao9+I/F5jDaKyCqAPMq5HqnfpBw8dKDiCaaqM0lq5h1pQTLeIqueeay4PieGR5jGZMWprw==", "cpu": [ "arm64" ], @@ -2812,9 +2812,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", - "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.2.tgz", + "integrity": "sha512-SWsr8zEUk82KSqquIMgZEg2GE5mCSfr9sE/thDROkX6pb3QQWPp8Vw8zOq2GyxZ2t0XoSIUlvHDkrf5Gmf7x3Q==", "cpu": [ "arm64" ], @@ -2825,9 +2825,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", - "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.2.tgz", + "integrity": "sha512-o/HAIrQq0jIxJAhgtIvV5FWviYK4WB0WwV91SLUnsliw1lSAoLsmgEEgRWzDguAFeUEUUoIWXiJrPqU7vGiVkA==", "cpu": [ "x64" ], @@ -2838,9 +2838,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", - "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.2.tgz", + "integrity": "sha512-nwlJ65UY9eGq91cBi6VyDfArUJSKOYt5dJQBq8xyLhvS23qO+4Nr/RreibFHjP6t+5ap2ohZrUJcHv5zk5ju/g==", "cpu": [ "arm" ], @@ -2851,9 +2851,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", - "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.2.tgz", + "integrity": "sha512-Pg5TxxO2IVlMj79+c/9G0LREC9SY3HM+pfAwX7zj5/cAuwrbfj2Wv9JbMHIdPCfQpYsI4g9mE+2Bw/3aeSs2rQ==", "cpu": [ "arm64" ], @@ -2864,9 +2864,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", - "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.2.tgz", + "integrity": "sha512-cAOTjGNm84gc6tS02D1EXtG7tDRsVSDTBVXOLbj31DkwfZwgTPYZ6aafSU7rD/4R2a34JOwlF9fQayuTSkoclA==", "cpu": [ "arm64" ], @@ -2876,10 +2876,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.2.tgz", + "integrity": "sha512-4RyT6v1kXb7C0fn6zV33rvaX05P0zHoNzaXI/5oFHklfKm602j+N4mn2YvoezQViRLPnxP8M1NaY4s/5kXO5cw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", - "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.2.tgz", + "integrity": "sha512-KNUH6jC/vRGAKSorySTyc/yRYlCwN/5pnMjXylfBniwtJx5O7X17KG/0efj8XM3TZU7raYRXJFFReOzNmL1n1w==", "cpu": [ "riscv64" ], @@ -2889,10 +2902,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.2.tgz", + "integrity": "sha512-xPV4y73IBEXToNPa3h5lbgXOi/v0NcvKxU0xejiFw6DtIYQqOTMhZ2DN18/HrrP0PmiL3rGtRG9gz1QE8vFKXQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", - "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.2.tgz", + "integrity": "sha512-QBhtr07iFGmF9egrPOWyO5wciwgtzKkYPNLVCFZTmr4TWmY0oY2Dm/bmhHjKRwZoGiaKdNcKhFtUMBKvlchH+Q==", "cpu": [ "x64" ], @@ -2903,9 +2929,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", - "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.2.tgz", + "integrity": "sha512-8zfsQRQGH23O6qazZSFY5jP5gt4cFvRuKTpuBsC1ZnSWxV8ZKQpPqOZIUtdfMOugCcBvFGRa1pDC/tkf19EgBw==", "cpu": [ "x64" ], @@ -2916,9 +2942,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", - "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.2.tgz", + "integrity": "sha512-H4s8UjgkPnlChl6JF5empNvFHp77Jx+Wfy2EtmYPe9G22XV+PMuCinZVHurNe8ggtwoaohxARJZbaH/3xjB/FA==", "cpu": [ "arm64" ], @@ -2929,9 +2955,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", - "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.2.tgz", + "integrity": "sha512-djqpAjm/i8erWYF0K6UY4kRO3X5+T4TypIqw60Q8MTqSBaQNpNXDhxdjpZ3ikgb+wn99svA7jxcXpiyg9MUsdw==", "cpu": [ "ia32" ], @@ -2942,9 +2968,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", - "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.2.tgz", + "integrity": "sha512-teAqzLT0yTYZa8ZP7zhFKEx4cotS8Tkk5XiqNMJhD4CpaWB1BHARE4Qy+RzwnXvSAYv+Q3jAqCVBS+PS+Yee8Q==", "cpu": [ "x64" ], @@ -3162,22 +3188,22 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.5.0.tgz", - "integrity": "sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.6.0.tgz", + "integrity": "sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.5.0", - "@typescript-eslint/type-utils": "7.5.0", - "@typescript-eslint/utils": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/type-utils": "7.6.0", + "@typescript-eslint/utils": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3197,15 +3223,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.5.0.tgz", - "integrity": "sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz", + "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.5.0", - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/typescript-estree": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4" }, "engines": { @@ -3225,13 +3251,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.5.0.tgz", - "integrity": "sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz", + "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0" + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3242,15 +3268,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.5.0.tgz", - "integrity": "sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.6.0.tgz", + "integrity": "sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.5.0", - "@typescript-eslint/utils": "7.5.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/utils": "7.6.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3269,9 +3295,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.5.0.tgz", - "integrity": "sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz", + "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3282,19 +3308,19 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.5.0.tgz", - "integrity": "sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz", + "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3319,9 +3345,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -3334,18 +3360,18 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.5.0.tgz", - "integrity": "sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.5.0", - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/typescript-estree": "7.5.0", - "semver": "^7.5.4" + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "semver": "^7.6.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3359,13 +3385,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.5.0.tgz", - "integrity": "sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz", + "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.5.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "7.6.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -5910,9 +5936,9 @@ } }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" @@ -8458,9 +8484,9 @@ } }, "node_modules/rollup": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", - "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.2.tgz", + "integrity": "sha512-WkeoTWvuBoFjFAhsEOHKRoZ3r9GfTyhh7Vff1zwebEFLEFjT1lG3784xEgKiTa7E+e70vsC81roVL2MP4tgEEQ==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -8473,19 +8499,21 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.0", - "@rollup/rollup-android-arm64": "4.13.0", - "@rollup/rollup-darwin-arm64": "4.13.0", - "@rollup/rollup-darwin-x64": "4.13.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", - "@rollup/rollup-linux-arm64-gnu": "4.13.0", - "@rollup/rollup-linux-arm64-musl": "4.13.0", - "@rollup/rollup-linux-riscv64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-musl": "4.13.0", - "@rollup/rollup-win32-arm64-msvc": "4.13.0", - "@rollup/rollup-win32-ia32-msvc": "4.13.0", - "@rollup/rollup-win32-x64-msvc": "4.13.0", + "@rollup/rollup-android-arm-eabi": "4.14.2", + "@rollup/rollup-android-arm64": "4.14.2", + "@rollup/rollup-darwin-arm64": "4.14.2", + "@rollup/rollup-darwin-x64": "4.14.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.2", + "@rollup/rollup-linux-arm64-gnu": "4.14.2", + "@rollup/rollup-linux-arm64-musl": "4.14.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.2", + "@rollup/rollup-linux-riscv64-gnu": "4.14.2", + "@rollup/rollup-linux-s390x-gnu": "4.14.2", + "@rollup/rollup-linux-x64-gnu": "4.14.2", + "@rollup/rollup-linux-x64-musl": "4.14.2", + "@rollup/rollup-win32-arm64-msvc": "4.14.2", + "@rollup/rollup-win32-ia32-msvc": "4.14.2", + "@rollup/rollup-win32-x64-msvc": "4.14.2", "fsevents": "~2.3.2" } }, @@ -9050,12 +9078,12 @@ } }, "node_modules/ts-api-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.2.tgz", - "integrity": "sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "engines": { - "node": ">=16.13.0" + "node": ">=16" }, "peerDependencies": { "typescript": ">=4.2.0" @@ -9143,9 +9171,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "devOptional": true, "bin": { "tsc": "bin/tsc", @@ -11037,9 +11065,9 @@ } }, "@eslint-community/regexpp": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", - "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true }, "@eslint/eslintrc": { @@ -11594,93 +11622,107 @@ } }, "@rollup/rollup-android-arm-eabi": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", - "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.2.tgz", + "integrity": "sha512-ahxSgCkAEk+P/AVO0vYr7DxOD3CwAQrT0Go9BJyGQ9Ef0QxVOfjDZMiF4Y2s3mLyPrjonchIMH/tbWHucJMykQ==", "dev": true, "optional": true }, "@rollup/rollup-android-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", - "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.2.tgz", + "integrity": "sha512-lAarIdxZWbFSHFSDao9+I/F5jDaKyCqAPMq5HqnfpBw8dKDiCaaqM0lq5h1pQTLeIqueeay4PieGR5jGZMWprw==", "dev": true, "optional": true }, "@rollup/rollup-darwin-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", - "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.2.tgz", + "integrity": "sha512-SWsr8zEUk82KSqquIMgZEg2GE5mCSfr9sE/thDROkX6pb3QQWPp8Vw8zOq2GyxZ2t0XoSIUlvHDkrf5Gmf7x3Q==", "dev": true, "optional": true }, "@rollup/rollup-darwin-x64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", - "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.2.tgz", + "integrity": "sha512-o/HAIrQq0jIxJAhgtIvV5FWviYK4WB0WwV91SLUnsliw1lSAoLsmgEEgRWzDguAFeUEUUoIWXiJrPqU7vGiVkA==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", - "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.2.tgz", + "integrity": "sha512-nwlJ65UY9eGq91cBi6VyDfArUJSKOYt5dJQBq8xyLhvS23qO+4Nr/RreibFHjP6t+5ap2ohZrUJcHv5zk5ju/g==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", - "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.2.tgz", + "integrity": "sha512-Pg5TxxO2IVlMj79+c/9G0LREC9SY3HM+pfAwX7zj5/cAuwrbfj2Wv9JbMHIdPCfQpYsI4g9mE+2Bw/3aeSs2rQ==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", - "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.2.tgz", + "integrity": "sha512-cAOTjGNm84gc6tS02D1EXtG7tDRsVSDTBVXOLbj31DkwfZwgTPYZ6aafSU7rD/4R2a34JOwlF9fQayuTSkoclA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.2.tgz", + "integrity": "sha512-4RyT6v1kXb7C0fn6zV33rvaX05P0zHoNzaXI/5oFHklfKm602j+N4mn2YvoezQViRLPnxP8M1NaY4s/5kXO5cw==", "dev": true, "optional": true }, "@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", - "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.2.tgz", + "integrity": "sha512-KNUH6jC/vRGAKSorySTyc/yRYlCwN/5pnMjXylfBniwtJx5O7X17KG/0efj8XM3TZU7raYRXJFFReOzNmL1n1w==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-s390x-gnu": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.2.tgz", + "integrity": "sha512-xPV4y73IBEXToNPa3h5lbgXOi/v0NcvKxU0xejiFw6DtIYQqOTMhZ2DN18/HrrP0PmiL3rGtRG9gz1QE8vFKXQ==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", - "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.2.tgz", + "integrity": "sha512-QBhtr07iFGmF9egrPOWyO5wciwgtzKkYPNLVCFZTmr4TWmY0oY2Dm/bmhHjKRwZoGiaKdNcKhFtUMBKvlchH+Q==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", - "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.2.tgz", + "integrity": "sha512-8zfsQRQGH23O6qazZSFY5jP5gt4cFvRuKTpuBsC1ZnSWxV8ZKQpPqOZIUtdfMOugCcBvFGRa1pDC/tkf19EgBw==", "dev": true, "optional": true }, "@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", - "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.2.tgz", + "integrity": "sha512-H4s8UjgkPnlChl6JF5empNvFHp77Jx+Wfy2EtmYPe9G22XV+PMuCinZVHurNe8ggtwoaohxARJZbaH/3xjB/FA==", "dev": true, "optional": true }, "@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", - "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.2.tgz", + "integrity": "sha512-djqpAjm/i8erWYF0K6UY4kRO3X5+T4TypIqw60Q8MTqSBaQNpNXDhxdjpZ3ikgb+wn99svA7jxcXpiyg9MUsdw==", "dev": true, "optional": true }, "@rollup/rollup-win32-x64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", - "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.2.tgz", + "integrity": "sha512-teAqzLT0yTYZa8ZP7zhFKEx4cotS8Tkk5XiqNMJhD4CpaWB1BHARE4Qy+RzwnXvSAYv+Q3jAqCVBS+PS+Yee8Q==", "dev": true, "optional": true }, @@ -11889,79 +11931,79 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.5.0.tgz", - "integrity": "sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.6.0.tgz", + "integrity": "sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==", "dev": true, "requires": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.5.0", - "@typescript-eslint/type-utils": "7.5.0", - "@typescript-eslint/utils": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/type-utils": "7.6.0", + "@typescript-eslint/utils": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" } }, "@typescript-eslint/parser": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.5.0.tgz", - "integrity": "sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz", + "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "7.5.0", - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/typescript-estree": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.5.0.tgz", - "integrity": "sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz", + "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==", "dev": true, "requires": { - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0" + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0" } }, "@typescript-eslint/type-utils": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.5.0.tgz", - "integrity": "sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.6.0.tgz", + "integrity": "sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "7.5.0", - "@typescript-eslint/utils": "7.5.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/utils": "7.6.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" } }, "@typescript-eslint/types": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.5.0.tgz", - "integrity": "sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz", + "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.5.0.tgz", - "integrity": "sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz", + "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==", "dev": true, "requires": { - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "dependencies": { "brace-expansion": { @@ -11974,9 +12016,9 @@ } }, "minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -11985,28 +12027,28 @@ } }, "@typescript-eslint/utils": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.5.0.tgz", - "integrity": "sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.5.0", - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/typescript-estree": "7.5.0", - "semver": "^7.5.4" + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "semver": "^7.6.0" } }, "@typescript-eslint/visitor-keys": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.5.0.tgz", - "integrity": "sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz", + "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==", "dev": true, "requires": { - "@typescript-eslint/types": "7.5.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "7.6.0", + "eslint-visitor-keys": "^3.4.3" } }, "@ungap/structured-clone": { @@ -13897,9 +13939,9 @@ } }, "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true }, "import-fresh": { @@ -15721,24 +15763,26 @@ } }, "rollup": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", - "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", - "dev": true, - "requires": { - "@rollup/rollup-android-arm-eabi": "4.13.0", - "@rollup/rollup-android-arm64": "4.13.0", - "@rollup/rollup-darwin-arm64": "4.13.0", - "@rollup/rollup-darwin-x64": "4.13.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", - "@rollup/rollup-linux-arm64-gnu": "4.13.0", - "@rollup/rollup-linux-arm64-musl": "4.13.0", - "@rollup/rollup-linux-riscv64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-musl": "4.13.0", - "@rollup/rollup-win32-arm64-msvc": "4.13.0", - "@rollup/rollup-win32-ia32-msvc": "4.13.0", - "@rollup/rollup-win32-x64-msvc": "4.13.0", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.2.tgz", + "integrity": "sha512-WkeoTWvuBoFjFAhsEOHKRoZ3r9GfTyhh7Vff1zwebEFLEFjT1lG3784xEgKiTa7E+e70vsC81roVL2MP4tgEEQ==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.14.2", + "@rollup/rollup-android-arm64": "4.14.2", + "@rollup/rollup-darwin-arm64": "4.14.2", + "@rollup/rollup-darwin-x64": "4.14.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.2", + "@rollup/rollup-linux-arm64-gnu": "4.14.2", + "@rollup/rollup-linux-arm64-musl": "4.14.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.2", + "@rollup/rollup-linux-riscv64-gnu": "4.14.2", + "@rollup/rollup-linux-s390x-gnu": "4.14.2", + "@rollup/rollup-linux-x64-gnu": "4.14.2", + "@rollup/rollup-linux-x64-musl": "4.14.2", + "@rollup/rollup-win32-arm64-msvc": "4.14.2", + "@rollup/rollup-win32-ia32-msvc": "4.14.2", + "@rollup/rollup-win32-x64-msvc": "4.14.2", "@types/estree": "1.0.5", "fsevents": "~2.3.2" } @@ -16155,9 +16199,9 @@ "dev": true }, "ts-api-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.2.tgz", - "integrity": "sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "requires": {} }, @@ -16204,9 +16248,9 @@ "dev": true }, "typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "devOptional": true }, "unicode-canonical-property-names-ecmascript": { diff --git a/package.json b/package.json index 99ddbf62..85ef0001 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chart2music", - "version": "1.16.0", + "version": "1.16.2", "main": "dist/index.js", "module": "dist/index.mjs", "exports": { @@ -58,8 +58,8 @@ "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-typescript": "11.1.6", "@types/jest": "^29.2.1", - "@typescript-eslint/eslint-plugin": "7.5.0", - "@typescript-eslint/parser": "7.5.0", + "@typescript-eslint/eslint-plugin": "7.6.0", + "@typescript-eslint/parser": "7.6.0", "concurrently": "8.2.2", "depcheck": "1.4.7", "eslint": "8.57.0", @@ -72,12 +72,12 @@ "lint-staged": "15.2.2", "prettier": "3.2.5", "rimraf": "5.0.5", - "rollup": "4.13.0", + "rollup": "4.14.2", "rollup-plugin-delete": "2.0.0", "rollup-plugin-dts": "6.1.0", "ts-jest": "29.1.2", "tslib": "2.6.2", - "typescript": "5.4.3" + "typescript": "5.4.5" }, "dependencies": { "@formatjs/intl": "^2.9.11" diff --git a/src/c2mChart.ts b/src/c2mChart.ts index 4acaa30c..8702747a 100644 --- a/src/c2mChart.ts +++ b/src/c2mChart.ts @@ -48,6 +48,16 @@ import { launchOptionDialog } from "./optionDialog"; import { launchInfoDialog } from "./infoDialog"; import { AudioNotificationType } from "./audio/AudioEngine"; import { DEFAULT_LANGUAGE, translate, AVAILABLE_LANGUAGES } from "./translator"; +import type { RowArrayAdapter } from "./rowArrayAdapter"; + +/** + * ValidUserInputRows are the types the user can submit as data rows. + * Note that number will be converted to SimpleDataPoint[], + * so the effective types we accept is more restrictive. + */ +type ValidUserInputRows = + | RowArrayAdapter + | (number | SupportedDataPointType)[]; /** * Metadata about previous levels. Used to quickly return to parents. @@ -265,7 +275,7 @@ export class c2m { * Index for the current group */ get _groupIndex() { - return this._visible_group_indices[this._visibleGroupIndex]; + return this._visible_group_indices.at(this._visibleGroupIndex); } /** @@ -281,7 +291,7 @@ export class c2m { private get _currentGroupType() { if (Array.isArray(this._type)) { // Example type: ["bar", "line"] - return this._type[this._visibleGroupIndex]; + return this._type.at(this._visibleGroupIndex); } else { // Example type: "bar" return this._type; @@ -292,7 +302,7 @@ export class c2m { * The current group's data */ private get _currentDataRow() { - return this._data[this._groupIndex]; + return this._data.at(this._groupIndex); } /** @@ -313,14 +323,14 @@ export class c2m { if (this._currentDataRow === null) { return null; } - return this._currentDataRow[this._pointIndex]; + return this._currentDataRow.at(this._pointIndex); } /** * Get the name of the current group */ private get _currentGroupName() { - return this._groups[this._groupIndex]; + return this._groups.at(this._groupIndex); } /** @@ -403,7 +413,7 @@ export class c2m { this._visibleGroupIndex++; this._playCurrent(); } - }, SPEEDS[this._speedRateIndex]); + }, SPEEDS.at(this._speedRateIndex)); this._playCurrent(); }, play_backward_category: () => { @@ -420,7 +430,7 @@ export class c2m { this._visibleGroupIndex--; this._playCurrent(); } - }, SPEEDS[this._speedRateIndex]) as NodeJS.Timeout; + }, SPEEDS.at(this._speedRateIndex)) as NodeJS.Timeout; this._playCurrent(); }, stop_play: () => { @@ -599,7 +609,7 @@ export class c2m { } this._sr.render( translate(this._language, "kbr-speed", { - rate_in_ms: SPEEDS[this._speedRateIndex] + rate_in_ms: SPEEDS.at(this._speedRateIndex) }) ); }, @@ -610,7 +620,7 @@ export class c2m { } this._sr.render( translate(this._language, "kbr-speed", { - rate_in_ms: SPEEDS[this._speedRateIndex] + rate_in_ms: SPEEDS.at(this._speedRateIndex) }) ); }, @@ -652,7 +662,7 @@ export class c2m { this._speedRateIndex = speedIndex; this._sr.render( translate(this._language, "kbr-speed", { - rate_in_ms: SPEEDS[this._speedRateIndex] + rate_in_ms: SPEEDS.at(this._speedRateIndex) }) ); } @@ -664,7 +674,7 @@ export class c2m { }, (hertzIndex: number) => { this._audioEngine?.playDataPoint( - this._options.hertzes[hertzIndex], + this._options.hertzes.at(hertzIndex), 0, NOTE_LENGTH ); @@ -1385,14 +1395,14 @@ export class c2m { (value, index) => index ); this._data = Object.values(userData).map((row) => - convertDataRow(row) + convertDataRow(row as ValidUserInputRows) ); return; } this._groups = [""]; this._visible_group_indices = [0]; - this._data = [convertDataRow(userData)]; + this._data = [convertDataRow(userData as ValidUserInputRows)]; } /** @@ -1711,7 +1721,7 @@ export class c2m { this._outlierIndex--; this._playCurrent(); } - }, SPEEDS[this._speedRateIndex]) as NodeJS.Timeout; + }, SPEEDS.at(this._speedRateIndex)) as NodeJS.Timeout; this._playCurrent(); } @@ -1736,7 +1746,7 @@ export class c2m { this._pointIndex--; this._playCurrent(); } - }, SPEEDS[this._speedRateIndex]) as NodeJS.Timeout; + }, SPEEDS.at(this._speedRateIndex)) as NodeJS.Timeout; this._playCurrent(); } @@ -1761,7 +1771,7 @@ export class c2m { this._outlierIndex++; this._playCurrent(); } - }, SPEEDS[this._speedRateIndex]); + }, SPEEDS.at(this._speedRateIndex)); this._playCurrent(); } @@ -1772,7 +1782,7 @@ export class c2m { const startIndex = this._pointIndex; const startX = this.getCurrent().point.x; const row = this._currentDataRow.slice(startIndex); - const totalTime = SPEEDS[this._speedRateIndex] * 10; + const totalTime = SPEEDS.at(this._speedRateIndex) * 10; const xMin = this._xAxis.minimum; const range = this._xAxis.maximum - xMin; const change = @@ -1807,7 +1817,7 @@ export class c2m { const startIndex = this._pointIndex; const startX = this.getCurrent().point.x; const row = this._currentDataRow.slice(0, startIndex + 1); - const totalTime = SPEEDS[this._speedRateIndex] * 10; + const totalTime = SPEEDS.at(this._speedRateIndex) * 10; const xMin = this._xAxis.minimum; const range = this._xAxis.maximum - xMin; const change = @@ -1857,7 +1867,7 @@ export class c2m { this._pointIndex++; this._playCurrent(); } - }, SPEEDS[this._speedRateIndex]); + }, SPEEDS.at(this._speedRateIndex)); this._playCurrent(); } @@ -2135,7 +2145,7 @@ export class c2m { NOTE_LENGTH ); }, - SPEEDS[this._speedRateIndex] * interval * index + SPEEDS.at(this._speedRateIndex) * interval * index ); }); } @@ -2170,8 +2180,9 @@ export class c2m { return; } - const { statIndex, availableStats } = - this._metadataByGroup[this._groupIndex]; + const { statIndex, availableStats } = this._metadataByGroup.at( + this._groupIndex + ); if (this._flagNewStat && availableStats.length === 0) { this._flagNewStat = false; } diff --git a/src/utils.ts b/src/utils.ts index 6091d555..070a6585 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -206,7 +206,7 @@ export const generatePointDescription = ( if (isBoxDataPoint(point) && outlierIndex !== null) { return translate(language, "point-outlier", { x: xFormat(point.x), - y: point.outlier[outlierIndex], + y: point.outlier.at(outlierIndex), index: outlierIndex + 1, count: point.outlier.length }); @@ -584,7 +584,7 @@ export const checkForNumberInput = ( data: SonifyTypes["data"] ) => { if (Array.isArray(data) && typeof data[0] === "number") { - metadataByGroup[0].inputType = "number"; + metadataByGroup.at(0).inputType = "number"; } else { let index = 0; for (const group in data) { From 88c0675e6fe9c4a9b0045b6927790d588ea60f1c Mon Sep 17 00:00:00 2001 From: Andrew Pikul Date: Sun, 28 Apr 2024 23:30:23 -0500 Subject: [PATCH 34/34] Adapt checkForNumberInput for ArrayAdapter --- src/utils.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index b64ae43c..a038f209 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -100,7 +100,6 @@ function calculateRowMinimum( ); } -// Question: Howcome the pre-adapter code didn't support Boxpoint for minimum/maximum? export const calculateAxisMinimum = ( data: ( | SupportedDataPointType[] @@ -114,7 +113,7 @@ export const calculateAxisMinimum = ( } const localMinimums: number[] = data - .map((row) => calculateRowMinimum(row, prop)[1]) + .map((row) => calculateRowMinimum(row, prop)[1]) // [0] is the index [1] is the value .filter((num) => !isNaN(num)); if (localMinimums.length === 0) { return NaN; @@ -579,22 +578,25 @@ export const prepChartElement = ( }; export const checkForNumberInput = ( - // TODO What is this lol metadataByGroup: groupedMetadata[], data: SonifyTypes["data"] ) => { - if (Array.isArray(data) && typeof data[0] === "number") { + if ( + (Array.isArray(data) || isRowArrayAdapter(data)) && + typeof data.at(0) === "number" + ) { metadataByGroup[0].inputType = "number"; } else { let index = 0; for (const group in data) { const row = (data as dataSet)[group]; - if ( - row !== null && - Array.isArray(row) && - detectDataPointType(row.at(0)) === "number" - ) { - metadataByGroup[index].inputType = "number"; + if (row !== null) { + if ( + (Array.isArray(row) || isRowArrayAdapter(row)) && + detectDataPointType(row.at(0)) === "number" + ) { + metadataByGroup[index].inputType = "number"; + } } index++; }