@heswell/vuu-data-engine-local 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/91.js +53 -19
- package/README.md +26 -2
- package/index.js +4 -4
- package/package.json +1 -1
- package/src/core/table/TableContainer.d.ts +24 -2
- package/src/index.d.ts +1 -1
- package/src/nextFrame.d.ts +1 -0
package/91.js
CHANGED
|
@@ -85,24 +85,15 @@ const DEFAULT_RANGE_LIMITS = {
|
|
|
85
85
|
};
|
|
86
86
|
const tableKey = ({ module, table })=>`${module}:${table}`;
|
|
87
87
|
const createTable = (schema, data = [], dataMap = buildDataColumnMapFromSchema(schema), updateGenerator)=>new Table_Table(schema, data, dataMap, updateGenerator);
|
|
88
|
-
const createJoinTable = (joinTable, table1, table2, joinColumn,
|
|
89
|
-
const { schema
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
...schema1.columns,
|
|
93
|
-
...schema2.columns
|
|
94
|
-
].map((col)=>col.name).sort());
|
|
95
|
-
const combinedSchema = {
|
|
96
|
-
key: schema1.key,
|
|
97
|
-
table: joinTable,
|
|
98
|
-
rangeLimits: DEFAULT_RANGE_LIMITS,
|
|
99
|
-
columns: Array.from(combinedColumns).map((columnName)=>({
|
|
100
|
-
name: columnName,
|
|
101
|
-
serverDataType: getServerDataType(columnName, schema1, schema2)
|
|
102
|
-
}))
|
|
103
|
-
};
|
|
88
|
+
const createJoinTable = (joinTable, table1, table2, joinColumn, joinTypeOrOptions = "inner")=>{
|
|
89
|
+
const { joinType = "inner", columns, schema } = "string" == typeof joinTypeOrOptions ? {
|
|
90
|
+
joinType: joinTypeOrOptions
|
|
91
|
+
} : joinTypeOrOptions;
|
|
104
92
|
const engineJoinTable = new JoinTable({
|
|
105
|
-
schema:
|
|
93
|
+
schema: buildJoinSchema(joinTable, table1.schema, table2.schema, {
|
|
94
|
+
columns,
|
|
95
|
+
schema
|
|
96
|
+
}),
|
|
106
97
|
baseTable: table1.engineTable,
|
|
107
98
|
joinTable: table2.engineTable,
|
|
108
99
|
leftColumn: joinColumn,
|
|
@@ -111,6 +102,49 @@ const createJoinTable = (joinTable, table1, table2, joinColumn, joinType = "inne
|
|
|
111
102
|
});
|
|
112
103
|
return new Table_Table(engineJoinTable);
|
|
113
104
|
};
|
|
105
|
+
const buildJoinSchema = (joinTable, schema1, schema2, { columns, schema })=>{
|
|
106
|
+
const name = `${joinTable.module}:${joinTable.table}`;
|
|
107
|
+
const fail = (message)=>{
|
|
108
|
+
throw Error(`[createJoinTable] ${name}: ${message}`);
|
|
109
|
+
};
|
|
110
|
+
if (columns && schema) fail("pass either columns or schema, not both");
|
|
111
|
+
if (schema) {
|
|
112
|
+
const { module, table } = schema.table;
|
|
113
|
+
if (module !== joinTable.module || table !== joinTable.table) fail(`schema is for table ${module}:${table}`);
|
|
114
|
+
if (schema.key !== schema1.key) fail(`schema key ${schema.key} must be the key of ${schema1.table.table} (${schema1.key})`);
|
|
115
|
+
}
|
|
116
|
+
const sourceColumns = new Set([
|
|
117
|
+
...schema1.columns,
|
|
118
|
+
...schema2.columns
|
|
119
|
+
].map((col)=>col.name));
|
|
120
|
+
const columnNames = schema?.columns.map((col)=>col.name) ?? columns ?? Array.from(sourceColumns).sort();
|
|
121
|
+
const seen = new Set();
|
|
122
|
+
for (const columnName of columnNames){
|
|
123
|
+
if (!sourceColumns.has(columnName)) fail(`unknown column ${columnName}, not in ${schema1.table.table} or ${schema2.table.table}`);
|
|
124
|
+
if (seen.has(columnName)) fail(`duplicate column ${columnName}`);
|
|
125
|
+
seen.add(columnName);
|
|
126
|
+
}
|
|
127
|
+
if (!seen.has(schema1.key)) fail(`columns must include the key column ${schema1.key}`);
|
|
128
|
+
const joinColumns = columnNames.map((columnName)=>({
|
|
129
|
+
name: columnName,
|
|
130
|
+
serverDataType: getServerDataType(columnName, schema1, schema2)
|
|
131
|
+
}));
|
|
132
|
+
if (schema) {
|
|
133
|
+
schema.columns.forEach(({ name: columnName, serverDataType }, i)=>{
|
|
134
|
+
if (serverDataType !== joinColumns[i].serverDataType) fail(`column ${columnName} is declared ${serverDataType} but is ${joinColumns[i].serverDataType} in the source table`);
|
|
135
|
+
});
|
|
136
|
+
return {
|
|
137
|
+
...schema,
|
|
138
|
+
rangeLimits: schema.rangeLimits ?? DEFAULT_RANGE_LIMITS
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
key: schema1.key,
|
|
143
|
+
table: joinTable,
|
|
144
|
+
rangeLimits: DEFAULT_RANGE_LIMITS,
|
|
145
|
+
columns: joinColumns
|
|
146
|
+
};
|
|
147
|
+
};
|
|
114
148
|
class TableContainer {
|
|
115
149
|
#tables = new Map();
|
|
116
150
|
createTable = (schema, data = [], dataMap = buildDataColumnMapFromSchema(schema), updateGenerator)=>{
|
|
@@ -118,8 +152,8 @@ class TableContainer {
|
|
|
118
152
|
this.addTable(table);
|
|
119
153
|
return table;
|
|
120
154
|
};
|
|
121
|
-
createJoinTable(joinTable, table1, table2, joinColumn,
|
|
122
|
-
const newTable = createJoinTable(joinTable, this.getTable(table1), this.getTable(table2), joinColumn,
|
|
155
|
+
createJoinTable(joinTable, table1, table2, joinColumn, joinTypeOrOptions = "inner") {
|
|
156
|
+
const newTable = createJoinTable(joinTable, this.getTable(table1), this.getTable(table2), joinColumn, joinTypeOrOptions);
|
|
123
157
|
this.addTable(newTable);
|
|
124
158
|
return newTable;
|
|
125
159
|
}
|
package/README.md
CHANGED
|
@@ -33,8 +33,9 @@ a second set of React contexts). They are `@vuu-ui/vuu-utils` and `@vuu-ui/vuu-f
|
|
|
33
33
|
## Usage
|
|
34
34
|
|
|
35
35
|
Importing the package has no side effects: no modules, tables or timers are created
|
|
36
|
-
until you ask for them. The main entry needs no DOM, so it runs in plain Node or Bun
|
|
37
|
-
|
|
36
|
+
until you ask for them. The main entry needs no DOM, so it runs in plain Node or Bun with
|
|
37
|
+
no shims: it uses `requestAnimationFrame` when present and falls back to `setTimeout`
|
|
38
|
+
otherwise. The React bindings (`createDataSourceProvider`, `createLocalVuuServer`,
|
|
38
39
|
`LocalDataSourceProvider`) are in `@heswell/vuu-data-engine-local/react`, because
|
|
39
40
|
`@vuu-ui/core` needs a DOM (`ResizeObserver`) at import time.
|
|
40
41
|
|
|
@@ -151,6 +152,29 @@ changes. Pass `"leftOuter"` to keep every `table1` row. For a row with no match,
|
|
|
151
152
|
Sort places nulls first when ascending; relational filters (`<`, `>=`, …) never match
|
|
152
153
|
null; and aggregations skip nulls.
|
|
153
154
|
|
|
155
|
+
By default the join table has every column of both tables, in alphabetical order. To
|
|
156
|
+
control the columns, pass an options object as the fifth argument instead of a join type:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
// an ordered list of column names, possibly a subset (must include table1's key)
|
|
160
|
+
createJoinTable(joinTable, instruments, prices, "ric", {
|
|
161
|
+
joinType: "leftOuter",
|
|
162
|
+
columns: ["ric", "exchange", "bid", "ask"],
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// or a declared TableSchema for the join table, e.g. the schema vuu-server returns
|
|
166
|
+
createJoinTable(instrumentPricesSchema.table, instruments, prices, "ric", {
|
|
167
|
+
joinType: "leftOuter",
|
|
168
|
+
schema: instrumentPricesSchema,
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`columns` and `schema` are mutually exclusive, and `joinType` still defaults to `"inner"`.
|
|
173
|
+
Column types come from the source tables; a column in both tables takes its value from
|
|
174
|
+
`table1`. Unknown or duplicate columns, a missing key column, a declared `serverDataType`
|
|
175
|
+
that differs from the source column, or a `schema` whose `table` or `key` does not match
|
|
176
|
+
(`joinTable`, `table1`'s key) throw an error naming the join table and the problem.
|
|
177
|
+
|
|
154
178
|
### Ranges
|
|
155
179
|
|
|
156
180
|
`subscribe({ range })`, `dataSource.range = …` and `setRange` accept a vuu-utils `Range` or
|
package/index.js
CHANGED
|
@@ -147,12 +147,12 @@ class RowKeys {
|
|
|
147
147
|
return key;
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
|
-
const toRange = (range)=>"function" == typeof range.equals && void 0 !== range.withBuffer ? range : Range(range.from, range.to);
|
|
151
|
-
const { KEY: KEY } = metadataKeys;
|
|
152
150
|
const nextFrame = (callback)=>{
|
|
153
|
-
if ("function" == typeof globalThis.requestAnimationFrame) globalThis.requestAnimationFrame(callback);
|
|
151
|
+
if ("function" == typeof globalThis.requestAnimationFrame) globalThis.requestAnimationFrame(()=>callback());
|
|
154
152
|
else globalThis.setTimeout(callback, 0);
|
|
155
153
|
};
|
|
154
|
+
const toRange = (range)=>"function" == typeof range.equals && void 0 !== range.withBuffer ? range : Range(range.from, range.to);
|
|
155
|
+
const { KEY: KEY } = metadataKeys;
|
|
156
156
|
const buildTableSchema = (columns, keyColumn)=>({
|
|
157
157
|
columns: columns.map(toSchemaColumn),
|
|
158
158
|
key: keyColumn ?? columns[0].name,
|
|
@@ -1228,7 +1228,7 @@ class VuuModule {
|
|
|
1228
1228
|
handlePostSubscribeActivities = ({ clientViewportId, tableSchema })=>{
|
|
1229
1229
|
const visualLinks = this.getVisualLinks(tableSchema.table.table);
|
|
1230
1230
|
const { dataSource } = this.getSubscriptionByViewport(clientViewportId);
|
|
1231
|
-
|
|
1231
|
+
nextFrame(()=>{
|
|
1232
1232
|
dataSource.links = visualLinks;
|
|
1233
1233
|
});
|
|
1234
1234
|
if (this.visualLinks && dataSource.tableSchema) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heswell/vuu-data-engine-local",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "In-browser Vuu server (VuuModule, tables, EngineDataSource) built on @heswell/vuu-table and @heswell/vuu-viewport",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -9,13 +9,35 @@ import type { UpdateGenerator } from "../../rowUpdates";
|
|
|
9
9
|
* whichever VuuModule returns it from `tables`.
|
|
10
10
|
*/
|
|
11
11
|
export declare const createTable: (schema: TableSchema, data?: Array<Array<VuuRowDataItemType | bigint>>, dataMap?: ColumnMap, updateGenerator?: UpdateGenerator) => Table;
|
|
12
|
+
export interface CreateJoinTableOptions {
|
|
13
|
+
/** Defaults to "inner". */
|
|
14
|
+
joinType?: JoinType;
|
|
15
|
+
/**
|
|
16
|
+
* Ordered column names of the join table, possibly a subset of the columns
|
|
17
|
+
* of the two source tables. Must include the key column of table1. Types
|
|
18
|
+
* are taken from the source tables. Defaults to every column of both
|
|
19
|
+
* tables, in alphabetical order.
|
|
20
|
+
*/
|
|
21
|
+
columns?: readonly string[];
|
|
22
|
+
/**
|
|
23
|
+
* Declared schema of the join table, e.g. the schema a Vuu server would
|
|
24
|
+
* return in TABLE_META_RESP. Its column order is preserved; every column
|
|
25
|
+
* must exist in one of the source tables, with the same serverDataType, and
|
|
26
|
+
* its key must be the key of table1. Cannot be combined with `columns`.
|
|
27
|
+
*/
|
|
28
|
+
schema?: TableSchema;
|
|
29
|
+
}
|
|
12
30
|
/**
|
|
13
31
|
* Create a materialized join of two tables, backed by an engine JoinTable,
|
|
14
32
|
* maintained incrementally as either source changes. `joinType` defaults to
|
|
15
33
|
* "inner" (only rows with a match in table2). With "leftOuter" every table1
|
|
16
34
|
* row is present and unmatched table2 columns are null, as in vuu-server.
|
|
35
|
+
*
|
|
36
|
+
* The fifth argument is either a JoinType or a CreateJoinTableOptions, which
|
|
37
|
+
* can also control the columns (and their order) of the join table. Where a
|
|
38
|
+
* column exists in both tables, the value comes from table1.
|
|
17
39
|
*/
|
|
18
|
-
export declare const createJoinTable: (joinTable: VuuTable, table1: Table, table2: Table, joinColumn: string,
|
|
40
|
+
export declare const createJoinTable: (joinTable: VuuTable, table1: Table, table2: Table, joinColumn: string, joinTypeOrOptions?: JoinType | CreateJoinTableOptions) => Table;
|
|
19
41
|
/**
|
|
20
42
|
* Tables, keyed by `{module, table}`. Each LocalEngine owns one, populated
|
|
21
43
|
* from the tables of its registered modules. Tables created through
|
|
@@ -28,7 +50,7 @@ export declare class TableContainer {
|
|
|
28
50
|
* Create a join of two tables already in this container. Pass full
|
|
29
51
|
* `{module, table}` references; a bare name is accepted only if unique.
|
|
30
52
|
*/
|
|
31
|
-
createJoinTable(joinTable: VuuTable, table1: VuuTable | string, table2: VuuTable | string, joinColumn: string,
|
|
53
|
+
createJoinTable(joinTable: VuuTable, table1: VuuTable | string, table2: VuuTable | string, joinColumn: string, joinTypeOrOptions?: JoinType | CreateJoinTableOptions): Table;
|
|
32
54
|
/**
|
|
33
55
|
* Add a table, keyed by `as` if given, otherwise by its schema's
|
|
34
56
|
* `{module, table}`.
|
package/src/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export * from "./core/module/VuuModule";
|
|
|
4
4
|
export * from "./core/module/edit-hooks";
|
|
5
5
|
export { type AnyVuuModule, createLocalEngine, ensureVuuModule, getDefaultEngine, LocalEngine, type LocalEngineOptions, resetDefaultEngine, } from "./core/engine/LocalEngine";
|
|
6
6
|
export * from "./core/user/CurrentUser";
|
|
7
|
-
export { createJoinTable, createTable, TableContainer, } from "./core/table/TableContainer";
|
|
7
|
+
export { type CreateJoinTableOptions, createJoinTable, createTable, TableContainer, } from "./core/table/TableContainer";
|
|
8
8
|
export { tableContainer } from "./core/table/defaultTableContainer";
|
|
9
9
|
export { registerDemoModules, type DemoModules } from "./demo-modules";
|
|
10
10
|
export { BaseUpdateGenerator } from "./UpdateGenerator";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const nextFrame: (callback: () => void) => void;
|