@vuu-ui/vuu-data-test 4.0.1 → 5.0.0-alpha.1

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.
Files changed (109) hide show
  1. package/README.md +46 -0
  2. package/package.json +12 -10
  3. package/src/Table.js +8 -5
  4. package/src/basket/BasketModule.js +105 -40
  5. package/src/basket/basket-schemas.js +16 -0
  6. package/src/basket/reference-data/constituent-data.js +2 -0
  7. package/src/basket/reference-data/constituents.js +7 -75
  8. package/src/basket/reference-data/prices.js +10 -4
  9. package/src/core/filter/PermissionFilter.js +60 -0
  10. package/src/core/filter/PermissionFilteredTable.js +59 -0
  11. package/src/core/module/ModuleContainer.js +9 -1
  12. package/src/core/module/VuuModule.js +54 -6
  13. package/src/core/table/TableContainer.js +53 -10
  14. package/src/core/user/CurrentUser.js +13 -0
  15. package/src/index.js +7 -0
  16. package/src/local-datasource-provider/LocalDatasourceProvider.js +67 -43
  17. package/src/module-admin/ModuleAdminModule.js +124 -0
  18. package/src/module-admin/index.js +3 -0
  19. package/src/module-admin/module-admin-schemas.js +95 -0
  20. package/src/module-admin/snapshot-projection.js +29 -0
  21. package/src/notifications/NotificationModule.js +131 -0
  22. package/src/notifications/NotificationsSchema.js +52 -0
  23. package/src/notifications/index.js +6 -0
  24. package/src/notifications/simulated/DismissNotificationRpcHandler.js +36 -0
  25. package/src/notifications/simulated/SimulatedNotificationsModule.js +18 -0
  26. package/src/notifications/simulated/SimulatedNotificationsProvider.js +151 -0
  27. package/src/notifications/simulated/notificationsModule.js +3 -0
  28. package/src/schemas.js +8 -2
  29. package/src/user-admin/UserAdminModule.js +567 -0
  30. package/src/user-admin/index.js +3 -0
  31. package/src/user-admin/initialSnapshot.js +461 -0
  32. package/src/user-admin/snapshot-projection.js +151 -0
  33. package/types/basket/BasketModule.d.ts +2 -0
  34. package/types/basket/reference-data/constituents.d.ts +3 -3
  35. package/types/basket/reference-data/prices.d.ts +2 -1
  36. package/types/core/module/ModuleContainer.d.ts +6 -3
  37. package/types/core/module/VuuModule.d.ts +22 -0
  38. package/types/core/table/TableContainer.d.ts +6 -6
  39. package/types/index.d.ts +7 -0
  40. package/types/local-datasource-provider/LocalDatasourceProvider.d.ts +19 -3
  41. package/types/schemas.d.ts +5 -3
  42. package/types/vuu-row-generator.d.ts +1 -1
  43. package/src/basket/reference-data/ftse100.js +0 -686
  44. package/src/basket/reference-data/hsi.js +0 -1047
  45. package/src/basket/reference-data/nasdaq100.js +0 -812
  46. package/src/basket/reference-data/sp500.js +0 -315
  47. package/types/basket/reference-data/ftse100.d.ts +0 -2
  48. package/types/basket/reference-data/hsi.d.ts +0 -2
  49. package/types/basket/reference-data/nasdaq100.d.ts +0 -2
  50. package/types/basket/reference-data/sp500.d.ts +0 -2
  51. package/types/src/ArrayProxy.d.ts +0 -8
  52. package/types/src/RuntimeVisualLink.d.ts +0 -9
  53. package/types/src/Table.d.ts +0 -34
  54. package/types/src/TickingArrayDataSource.d.ts +0 -44
  55. package/types/src/UpdateGenerator.d.ts +0 -18
  56. package/types/src/basket/BasketModule.d.ts +0 -55
  57. package/types/src/basket/basket-schemas.d.ts +0 -9
  58. package/types/src/basket/index.d.ts +0 -2
  59. package/types/src/basket/reference-data/constituents.d.ts +0 -3
  60. package/types/src/basket/reference-data/ftse100.d.ts +0 -2
  61. package/types/src/basket/reference-data/hsi.d.ts +0 -2
  62. package/types/src/basket/reference-data/nasdaq100.d.ts +0 -2
  63. package/types/src/basket/reference-data/prices.d.ts +0 -24
  64. package/types/src/basket/reference-data/sp500.d.ts +0 -2
  65. package/types/src/core/module/ModuleContainer.d.ts +0 -11
  66. package/types/src/core/module/VuuModule.d.ts +0 -81
  67. package/types/src/core/table/TableContainer.d.ts +0 -16
  68. package/types/src/data-utils.d.ts +0 -7
  69. package/types/src/default-column-definitions.d.ts +0 -2
  70. package/types/src/index.d.ts +0 -12
  71. package/types/src/local-datasource-provider/LocalDatasourceProvider.d.ts +0 -4
  72. package/types/src/makeSuggestions.d.ts +0 -3
  73. package/types/src/rowUpdates.d.ts +0 -15
  74. package/types/src/schemas.d.ts +0 -8
  75. package/types/src/session-table-utils.d.ts +0 -6
  76. package/types/src/simul/SimulModule.d.ts +0 -24
  77. package/types/src/simul/index.d.ts +0 -2
  78. package/types/src/simul/reference-data/accounts.d.ts +0 -1
  79. package/types/src/simul/reference-data/algos.d.ts +0 -1
  80. package/types/src/simul/reference-data/currencies.d.ts +0 -1
  81. package/types/src/simul/reference-data/index.d.ts +0 -5
  82. package/types/src/simul/reference-data/instrument-prices.d.ts +0 -60
  83. package/types/src/simul/reference-data/instruments-extended.d.ts +0 -1
  84. package/types/src/simul/reference-data/instruments.d.ts +0 -35
  85. package/types/src/simul/reference-data/isin-generator/convert-string-to-base-ten-number.d.ts +0 -1
  86. package/types/src/simul/reference-data/isin-generator/fixtures/alphabet.d.ts +0 -1
  87. package/types/src/simul/reference-data/isin-generator/fixtures/alphanumeric-values.d.ts +0 -1
  88. package/types/src/simul/reference-data/isin-generator/fixtures/base-ten-numbers.d.ts +0 -1
  89. package/types/src/simul/reference-data/isin-generator/generate-cusip-check-code.d.ts +0 -1
  90. package/types/src/simul/reference-data/isin-generator/generate-cusip-without-check-code.d.ts +0 -1
  91. package/types/src/simul/reference-data/isin-generator/generate-cusip.d.ts +0 -1
  92. package/types/src/simul/reference-data/isin-generator/generate-random-alphanumeric.d.ts +0 -1
  93. package/types/src/simul/reference-data/isin-generator/generate-random-base-ten-number.d.ts +0 -1
  94. package/types/src/simul/reference-data/isin-generator/generate-random-string-of-alphanumeric-chars.d.ts +0 -1
  95. package/types/src/simul/reference-data/isin-generator/generate-random-string-of-base-ten-chars.d.ts +0 -1
  96. package/types/src/simul/reference-data/isin-generator/index.d.ts +0 -1
  97. package/types/src/simul/reference-data/isin-generator/is-odd.d.ts +0 -1
  98. package/types/src/simul/reference-data/locations.d.ts +0 -4
  99. package/types/src/simul/reference-data/lotsizes.d.ts +0 -1
  100. package/types/src/simul/reference-data/orderStatus.d.ts +0 -1
  101. package/types/src/simul/reference-data/orders.d.ts +0 -36
  102. package/types/src/simul/reference-data/parent-child-orders.d.ts +0 -4
  103. package/types/src/simul/reference-data/priceStrategies.d.ts +0 -1
  104. package/types/src/simul/reference-data/prices.d.ts +0 -34
  105. package/types/src/simul/reference-data/sides.d.ts +0 -1
  106. package/types/src/simul/simul-schemas.d.ts +0 -9
  107. package/types/src/test/TestModule.d.ts +0 -16
  108. package/types/src/test/index.d.ts +0 -1
  109. package/types/src/vuu-row-generator.d.ts +0 -17
@@ -1,76 +1,8 @@
1
- import ftse100 from "./ftse100.js";
2
- import nasdaq100 from "./nasdaq100.js";
3
- import sp500 from "./sp500.js";
4
- import hsi from "./hsi.js";
5
- const basketConstituentData = [];
6
- for (const row of ftse100){
7
- const [ric, name, lastTrade, change, volume] = row;
8
- const basketId = ".FTSE100";
9
- const side = "BUY";
10
- const weighting = 1;
11
- basketConstituentData.push([
12
- basketId,
13
- change,
14
- name,
15
- lastTrade,
16
- ric,
17
- `${ric}-${basketId}`,
18
- side,
19
- volume,
20
- weighting
21
- ]);
22
- }
23
- for (const row of hsi){
24
- const [name, ric, lastTrade, change, , volume] = row;
25
- const basketId = ".HSI";
26
- const side = "BUY";
27
- const weighting = 1;
28
- basketConstituentData.push([
29
- basketId,
30
- change,
31
- name,
32
- lastTrade,
33
- ric,
34
- `${ric}-${basketId}`,
35
- side,
36
- volume,
37
- weighting
38
- ]);
39
- }
40
- for (const row of nasdaq100){
41
- const [name, ric, weighting, lastTrade, change] = row;
42
- const basketId = ".NASDAQ100";
43
- const side = "BUY";
44
- const volume = 1000;
45
- basketConstituentData.push([
46
- basketId,
47
- change,
48
- name,
49
- lastTrade,
50
- ric,
51
- `${ric}-${basketId}`,
52
- side,
53
- volume,
54
- weighting
55
- ]);
56
- }
57
- for (const row of sp500){
58
- const [name, ric, weighting, , change] = row;
59
- const basketId = ".SP500";
60
- const side = "BUY";
61
- const volume = 1000;
62
- const lastTrade = 0;
63
- basketConstituentData.push([
64
- basketId,
65
- change,
66
- name,
67
- lastTrade,
68
- ric,
69
- `${ric}-${basketId}`,
70
- side,
71
- volume,
72
- weighting
73
- ]);
74
- }
75
- var constituents = basketConstituentData;
1
+ import constituent_data from "./constituent-data.js";
2
+ import { schemas } from "../basket-schemas.js";
3
+ const basketConstituentData = constituent_data.map((constituent)=>{
4
+ const values = constituent;
5
+ return schemas.basketConstituent.columns.map(({ name })=>values[name]);
6
+ });
7
+ const constituents = basketConstituentData;
76
8
  export default constituents;
@@ -1,9 +1,8 @@
1
- import { buildDataColumnMap } from "../../Table.js";
1
+ import { Table, buildDataColumnMap } from "../../Table.js";
2
2
  import { BaseUpdateGenerator } from "../../UpdateGenerator.js";
3
3
  import { schemas } from "../../simul/simul-schemas.js";
4
4
  import constituents from "./constituents.js";
5
5
  import { initBidAsk, nextRandomDouble, random } from "../../data-utils.js";
6
- import TableContainer from "../../core/table/TableContainer.js";
7
6
  const { bid: prices_bid, bidSize: prices_bidSize, ask: prices_ask, askSize: prices_askSize, last: prices_last } = buildDataColumnMap(schemas, "prices");
8
7
  const pricesUpdateGenerator = new BaseUpdateGenerator({
9
8
  bid: prices_bid,
@@ -13,9 +12,12 @@ const pricesUpdateGenerator = new BaseUpdateGenerator({
13
12
  last: prices_last
14
13
  });
15
14
  const prices = [];
15
+ const seen = new Set();
16
16
  for (const [, , , lastTrade, ric] of constituents){
17
+ if (seen.has(String(ric))) continue;
17
18
  const priceSeed = parseFloat(String(lastTrade));
18
19
  if (!isNaN(priceSeed)) {
20
+ seen.add(String(ric));
19
21
  const [bid, ask] = initBidAsk(5, nextRandomDouble);
20
22
  const askSize = random(1000, 3000);
21
23
  const bidSize = random(1000, 3000);
@@ -38,6 +40,10 @@ for (const [, , , lastTrade, ric] of constituents){
38
40
  ]);
39
41
  }
40
42
  }
41
- const pricesTable = TableContainer.createTable(schemas.prices, prices, buildDataColumnMap(schemas, "prices"), pricesUpdateGenerator);
42
- var reference_data_prices = pricesTable;
43
+ const pricesTable = new Table(schemas.prices, prices, buildDataColumnMap(schemas, "prices"), pricesUpdateGenerator);
44
+ pricesUpdateGenerator.setRange({
45
+ from: 0,
46
+ to: prices.length
47
+ });
48
+ const reference_data_prices = pricesTable;
43
49
  export default reference_data_prices;
@@ -0,0 +1,60 @@
1
+ const allowAll = ()=>true;
2
+ const denyAll = ()=>false;
3
+ const AllowAllPermissionFilter = {
4
+ createPredicate: ()=>allowAll
5
+ };
6
+ const DenyAllPermissionFilter = {
7
+ createPredicate: ()=>denyAll
8
+ };
9
+ class RowPermissionFilter {
10
+ rowPredicate;
11
+ constructor(rowPredicate){
12
+ this.rowPredicate = rowPredicate;
13
+ }
14
+ createPredicate(columnMap) {
15
+ let currentRow = [];
16
+ const rowData = {
17
+ get: (columnName)=>{
18
+ const idx = columnMap[columnName];
19
+ return void 0 === idx ? void 0 : currentRow[idx];
20
+ }
21
+ };
22
+ return (row)=>{
23
+ currentRow = row;
24
+ return this.rowPredicate(rowData);
25
+ };
26
+ }
27
+ }
28
+ class ContainsPermissionFilter {
29
+ columnName;
30
+ allowedValues;
31
+ constructor(columnName, allowedValues){
32
+ this.columnName = columnName;
33
+ this.allowedValues = allowedValues;
34
+ }
35
+ createPredicate(columnMap) {
36
+ const idx = columnMap[this.columnName];
37
+ if (void 0 === idx || 0 === this.allowedValues.size) return denyAll;
38
+ const { allowedValues } = this;
39
+ return (row)=>{
40
+ const value = row[idx];
41
+ return null != value && allowedValues.has(String(value));
42
+ };
43
+ }
44
+ }
45
+ class PermissionFilterChain {
46
+ filters;
47
+ constructor(filters){
48
+ this.filters = filters;
49
+ }
50
+ createPredicate(columnMap) {
51
+ const predicates = this.filters.map((f)=>f.createPredicate(columnMap));
52
+ return (row)=>predicates.every((predicate)=>predicate(row));
53
+ }
54
+ }
55
+ function PermissionFilter(arg, allowedValues) {
56
+ if ("function" == typeof arg) return new RowPermissionFilter(arg);
57
+ if ("string" == typeof arg) return new ContainsPermissionFilter(arg, allowedValues ?? new Set());
58
+ return new PermissionFilterChain(Array.from(arg));
59
+ }
60
+ export { AllowAllPermissionFilter, DenyAllPermissionFilter, PermissionFilter };
@@ -0,0 +1,59 @@
1
+ import { Table } from "../../Table.js";
2
+ class PermissionFilteredTable extends Table {
3
+ #attached = false;
4
+ #indexOfKey;
5
+ #predicate;
6
+ #source;
7
+ constructor(source, predicate){
8
+ super(source.schema, source.data.filter(predicate), source.map);
9
+ this.#indexOfKey = source.map[source.schema.key];
10
+ this.#predicate = predicate;
11
+ this.#source = source;
12
+ this.attach();
13
+ }
14
+ get source() {
15
+ return this.#source;
16
+ }
17
+ attach() {
18
+ if (!this.#attached) {
19
+ this.#source.on("insert", this.handleSourceInsert);
20
+ this.#source.on("update", this.handleSourceUpdate);
21
+ this.#source.on("delete", this.handleSourceDelete);
22
+ this.#attached = true;
23
+ }
24
+ }
25
+ dispose() {
26
+ if (this.#attached) {
27
+ this.#source.removeListener("insert", this.handleSourceInsert);
28
+ this.#source.removeListener("update", this.handleSourceUpdate);
29
+ this.#source.removeListener("delete", this.handleSourceDelete);
30
+ this.#attached = false;
31
+ }
32
+ }
33
+ contains(key) {
34
+ return void 0 !== this.findByKey(key);
35
+ }
36
+ handleSourceInsert = (row)=>{
37
+ if (this.#predicate(row)) this.insert(row);
38
+ };
39
+ handleSourceUpdate = (row)=>{
40
+ const key = row[this.#indexOfKey];
41
+ const isMember = this.contains(key);
42
+ const isPermitted = this.#predicate(row);
43
+ if (isMember && isPermitted) this.replaceRow(row);
44
+ else if (isMember) this.delete(key);
45
+ else if (isPermitted) this.insert(row);
46
+ };
47
+ handleSourceDelete = (key)=>{
48
+ if (this.contains(key)) this.delete(key);
49
+ };
50
+ replaceRow(row) {
51
+ const key = row[this.#indexOfKey];
52
+ const rowIndex = this.data.findIndex((r)=>r[this.#indexOfKey] === key);
53
+ if (-1 !== rowIndex) {
54
+ this.data[rowIndex] = row;
55
+ this.emit("update", row);
56
+ }
57
+ }
58
+ }
59
+ export { PermissionFilteredTable };
@@ -8,6 +8,9 @@ class ModuleContainer {
8
8
  register(module) {
9
9
  this.#modules.set(module.name, module);
10
10
  }
11
+ has(name) {
12
+ return this.#modules.has(name);
13
+ }
11
14
  get(name) {
12
15
  const module = this.#modules.get(name);
13
16
  if (module) return module;
@@ -17,5 +20,10 @@ class ModuleContainer {
17
20
  return this.#modules.keys();
18
21
  }
19
22
  }
20
- var module_ModuleContainer = ModuleContainer.instance;
23
+ const module_ModuleContainer = ModuleContainer.instance;
24
+ const ensureVuuModule = (module)=>{
25
+ if (!ModuleContainer.instance.has(module.name)) ModuleContainer.instance.register(module);
26
+ return module;
27
+ };
21
28
  export default module_ModuleContainer;
29
+ export { ensureVuuModule };
@@ -4,6 +4,10 @@ import { TickingArrayDataSource } from "../../TickingArrayDataSource.js";
4
4
  import { RuntimeVisualLink } from "../../RuntimeVisualLink.js";
5
5
  import ModuleContainer from "./ModuleContainer.js";
6
6
  import { sessionTableRow, sessionTableSchema } from "../../session-table-utils.js";
7
+ import { AllowAllPermissionFilter } from "../filter/PermissionFilter.js";
8
+ import { PermissionFilteredTable } from "../filter/PermissionFilteredTable.js";
9
+ import { getCurrentUser } from "../user/CurrentUser.js";
10
+ import TableContainer from "../table/TableContainer.js";
7
11
  const assertUpdateIsValid = (schema, column, _data)=>{
8
12
  const col = schema.columns.find((col)=>col.name === column);
9
13
  if (col) ;
@@ -22,6 +26,12 @@ class VuuModule {
22
26
  this.#sessionTableMessageColumn = sessionTableMessageColumn;
23
27
  ModuleContainer.register(this);
24
28
  }
29
+ get includeDefaultServices() {
30
+ return true;
31
+ }
32
+ async beforeSessionSave(_sourceTable, _sessionTable) {}
33
+ async afterSessionSave(_sourceTable, _sessionTable) {}
34
+ permissionFunctions;
25
35
  getTableSchema(tableName) {
26
36
  return this.schemas[tableName] ?? this.#sessionTableMap[tableName]?.schema;
27
37
  }
@@ -121,9 +131,17 @@ class VuuModule {
121
131
  return void 0 === linksForTable ? void 0 : this.getLinks(this.#subscriptionMap, linksForTable);
122
132
  };
123
133
  createDataSource = (tableName, viewport, config)=>{
134
+ this.beforeCreateDataSource?.(tableName);
124
135
  const columnDescriptors = this.getColumnDescriptors(tableName);
125
- const table = this.tables[tableName];
126
136
  const sessionTable = this.#sessionTableMap[tableName];
137
+ const user = getCurrentUser();
138
+ const permissionFunction = sessionTable ? void 0 : this.permissionFunctions?.[tableName];
139
+ const viewportId = permissionFunction ? viewport ?? uuid() : viewport;
140
+ const permissionFilteredTable = permissionFunction ? this.createPermissionFilteredTable(this.tables[tableName], permissionFunction, {
141
+ id: viewportId,
142
+ user
143
+ }) : void 0;
144
+ const table = permissionFilteredTable ?? this.tables[tableName];
127
145
  const vuuModule = this;
128
146
  const dataSource = new TickingArrayDataSource({
129
147
  ...config,
@@ -134,18 +152,24 @@ class VuuModule {
134
152
  menu: this.menus?.[tableName],
135
153
  rpcServices: this.getServices(tableName),
136
154
  rpcMenuServices: this.getMenuServices(tableName),
137
- viewport,
155
+ viewport: viewportId,
138
156
  visualLinkService: this.visualLinkService,
139
157
  vuuModule
140
158
  });
141
159
  dataSource.on("subscribed", this.handlePostSubscribeActivities);
142
160
  dataSource.on("unsubscribed", this.unregisterViewport);
161
+ if (permissionFilteredTable) {
162
+ dataSource.on("subscribed", ()=>permissionFilteredTable.attach());
163
+ dataSource.on("unsubscribed", ()=>permissionFilteredTable.dispose());
164
+ }
143
165
  const existingSubscriptions = this.#subscriptionMap.get(tableName);
144
166
  const subscription = {
145
167
  viewportId: dataSource.viewport,
146
168
  dataSource,
147
169
  sessionTableName: sessionTable ? tableName : void 0,
148
- sourceTableName: this.#sessionSourceTableMap[tableName]
170
+ sourceTableName: this.#sessionSourceTableMap[tableName],
171
+ table: table || sessionTable,
172
+ user
149
173
  };
150
174
  if (existingSubscriptions) existingSubscriptions.push(subscription);
151
175
  else this.#subscriptionMap.set(tableName, [
@@ -153,10 +177,16 @@ class VuuModule {
153
177
  ]);
154
178
  return dataSource;
155
179
  };
180
+ createPermissionFilteredTable(table, permissionFunction, viewport) {
181
+ const permissionFilter = permissionFunction(viewport, TableContainer);
182
+ if (permissionFilter === AllowAllPermissionFilter) return;
183
+ return new PermissionFilteredTable(table, permissionFilter.createPredicate(table.map));
184
+ }
156
185
  getServices(tableName) {
157
186
  const tableServices = this.services?.[tableName];
158
- if (Array.isArray(tableServices)) return this.#moduleServices.concat(tableServices);
159
- return this.#moduleServices;
187
+ const defaultServices = this.includeDefaultServices ? this.#moduleServices : [];
188
+ if (Array.isArray(tableServices)) return defaultServices.concat(tableServices);
189
+ return defaultServices;
160
190
  }
161
191
  getMenuServices(tableName) {
162
192
  const tableServices = this.menuServices?.[tableName];
@@ -265,7 +295,9 @@ class VuuModule {
265
295
  sessionTable.delete(key);
266
296
  return {
267
297
  type: "SUCCESS_RESULT",
268
- data: void 0
298
+ data: {
299
+ wasInsertedRow: true
300
+ }
269
301
  };
270
302
  }
271
303
  if ("deleteRow" === action || "editCell" === action) {
@@ -499,6 +531,14 @@ class VuuModule {
499
531
  data: void 0
500
532
  };
501
533
  {
534
+ try {
535
+ await this.beforeSessionSave(sourceTable, sessionTable);
536
+ } catch (error) {
537
+ return {
538
+ errorMessage: error instanceof Error ? error.message : "Unable to prepare session changes",
539
+ type: "ERROR_RESULT"
540
+ };
541
+ }
502
542
  let rejectedCount = 0;
503
543
  const vuuMsgIdx = sessionTable.map[this.#sessionTableMessageColumn];
504
544
  const actionIdx = sessionTable.map.vuuAction;
@@ -533,6 +573,14 @@ class VuuModule {
533
573
  errorMessage: "stale update",
534
574
  type: "ERROR_RESULT"
535
575
  };
576
+ try {
577
+ await this.afterSessionSave(sourceTable, sessionTable);
578
+ } catch (error) {
579
+ return {
580
+ errorMessage: error instanceof Error ? error.message : "Unable to finalize session changes",
581
+ type: "ERROR_RESULT"
582
+ };
583
+ }
536
584
  return {
537
585
  type: "SUCCESS_RESULT",
538
586
  data: void 0
@@ -10,14 +10,14 @@ class TableContainer {
10
10
  return TableContainer.#instance;
11
11
  }
12
12
  #tables = new Map();
13
- createTable = (schema, data, dataMap, updateGenerator)=>{
13
+ createTable = (schema, data = [], dataMap = buildDataColumnMapFromSchema(schema), updateGenerator)=>{
14
14
  const table = new Table(schema, data, dataMap, updateGenerator);
15
15
  this.addTable(table);
16
16
  return table;
17
17
  };
18
- createJoinTable(joinTable, { table: t1 }, { table: t2 }, joinColumn) {
19
- const table1 = this.getTable(t1);
20
- const table2 = this.getTable(t2);
18
+ createJoinTable(joinTable, source1, source2, joinColumn) {
19
+ const table1 = source1 instanceof Table ? source1 : this.getTable(source1.table);
20
+ const table2 = source2 instanceof Table ? source2 : this.getTable(source2.table);
21
21
  const { map: m1, schema: schema1 } = table1;
22
22
  const { map: m2, schema: schema2 } = table2;
23
23
  const k1 = m1[joinColumn];
@@ -27,7 +27,7 @@ class TableContainer {
27
27
  ...schema2.columns
28
28
  ].map((col)=>col.name).sort());
29
29
  const combinedSchema = {
30
- key: joinColumn,
30
+ key: schema1.key,
31
31
  table: joinTable,
32
32
  rangeLimits: DEFAULT_RANGE_LIMITS,
33
33
  columns: Array.from(combinedColumns).map((columnName)=>({
@@ -53,6 +53,18 @@ class TableContainer {
53
53
  }
54
54
  }
55
55
  const newTable = new Table(combinedSchema, data, combinedColumnMap);
56
+ const joinedKeys = new Map();
57
+ const indexJoinedRow = (row)=>{
58
+ const value = String(row[combinedColumnMap[joinColumn]]);
59
+ const key = String(row[combinedColumnMap[schema1.key]]);
60
+ let keys = joinedKeys.get(value);
61
+ if (!keys) {
62
+ keys = new Set();
63
+ joinedKeys.set(value, keys);
64
+ }
65
+ keys.add(key);
66
+ };
67
+ data.forEach(indexJoinedRow);
56
68
  table1.on("insert", (row)=>{
57
69
  const row2 = table2.findByKey(String(row[k1]));
58
70
  if (row2) {
@@ -65,18 +77,49 @@ class TableContainer {
65
77
  const value = row2[m2[column.name]];
66
78
  out[combinedColumnMap[column.name]] = value;
67
79
  }
80
+ indexJoinedRow(out);
68
81
  newTable.insert(out);
69
82
  }
70
83
  });
71
- table2.on("update", (row)=>{
72
- const keyValue = row[k2];
73
- const targetRow = newTable.findByKey(keyValue);
84
+ table1.on("update", (row)=>{
85
+ const key = String(row[m1[schema1.key]]);
86
+ const targetRow = newTable.findByKey(key);
74
87
  if (targetRow) {
75
88
  const updatedRow = targetRow.slice();
76
- for (const { name } of table2.schema.columns)if (row[m2[name]] !== updatedRow[combinedColumnMap[name]]) updatedRow[combinedColumnMap[name]] = row[m2[name]];
89
+ for (const { name } of schema1.columns)updatedRow[combinedColumnMap[name]] = row[m1[name]];
90
+ newTable.updateRow(updatedRow);
91
+ }
92
+ });
93
+ table1.on("delete", (key)=>{
94
+ const row = newTable.findByKey(key);
95
+ if (row) {
96
+ const value = String(row[combinedColumnMap[joinColumn]]);
97
+ const keys = joinedKeys.get(value);
98
+ keys?.delete(key);
99
+ if (keys?.size === 0) joinedKeys.delete(value);
100
+ newTable.delete(key);
101
+ }
102
+ });
103
+ table2.on("update", (row)=>{
104
+ const keyValue = String(row[k2]);
105
+ for (const key of joinedKeys.get(keyValue) ?? []){
106
+ const targetRow = newTable.findByKey(key);
107
+ if (!targetRow) continue;
108
+ const updatedRow = targetRow.slice();
109
+ for (const { name } of table2.schema.columns)if (!(name in m1)) {
110
+ if (row[m2[name]] !== updatedRow[combinedColumnMap[name]]) updatedRow[combinedColumnMap[name]] = row[m2[name]];
111
+ }
77
112
  newTable.updateRow(updatedRow);
78
113
  }
79
114
  });
115
+ newTable.update = (key, columnName, value)=>{
116
+ const row = newTable.findByKey(key);
117
+ if (!row) throw Error(`[TableContainer] join row ${key} not found`);
118
+ if (columnName === schema1.key || columnName === joinColumn) throw Error(`[TableContainer] cannot edit join key ${columnName}`);
119
+ if (columnName in m1) table1.update(key, columnName, value);
120
+ else if (columnName in m2) table2.update(String(row[combinedColumnMap[schema2.key]]), columnName, value);
121
+ else throw Error(`[TableContainer] unknown join column ${columnName}`);
122
+ };
80
123
  this.addTable(newTable);
81
124
  return newTable;
82
125
  }
@@ -89,7 +132,7 @@ class TableContainer {
89
132
  throw Error(`[TableContainer] no table ${tableName}`);
90
133
  }
91
134
  }
92
- var table_TableContainer = TableContainer.instance;
135
+ const table_TableContainer = TableContainer.instance;
93
136
  const getServerDataType = (columnName, { columns: cols1, table: t1 }, { columns: cols2, table: t2 })=>{
94
137
  const col1 = cols1.find((col)=>col.name === columnName);
95
138
  const col2 = cols2.find((col)=>col.name === columnName);
@@ -0,0 +1,13 @@
1
+ const DEFAULT_USER = {
2
+ name: "user",
3
+ authorizations: []
4
+ };
5
+ let currentUser = DEFAULT_USER;
6
+ const getCurrentUser = ()=>currentUser;
7
+ const setCurrentUser = (user)=>{
8
+ currentUser = void 0 === user ? DEFAULT_USER : "string" == typeof user ? {
9
+ name: user,
10
+ authorizations: []
11
+ } : user;
12
+ };
13
+ export { getCurrentUser, setCurrentUser };
package/src/index.js CHANGED
@@ -1,12 +1,19 @@
1
1
  export * from "./ArrayProxy.js";
2
+ export * from "./core/filter/PermissionFilter.js";
3
+ export * from "./core/filter/PermissionFilteredTable.js";
2
4
  export * from "./core/module/VuuModule.js";
5
+ export * from "./core/user/CurrentUser.js";
3
6
  export * from "./local-datasource-provider/LocalDatasourceProvider.js";
4
7
  export * from "./makeSuggestions.js";
8
+ export * from "./notifications/index.js";
5
9
  export * from "./schemas.js";
6
10
  export * from "./simul/index.js";
7
11
  export * from "./basket/index.js";
12
+ export * from "./module-admin/index.js";
13
+ export * from "./user-admin/index.js";
8
14
  export * from "./test/index.js";
9
15
  export * from "./TickingArrayDataSource.js";
10
16
  export * from "./vuu-row-generator.js";
17
+ export { ensureVuuModule } from "./core/module/ModuleContainer.js";
11
18
  export { default as tableContainer } from "./core/table/TableContainer.js";
12
19
  export { Table, buildDataColumnMap, buildDataColumnMapFromSchema } from "./Table.js";
@@ -1,46 +1,70 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- import { DataProvider } from "@vuu-ui/vuu-utils";
3
- import ModuleContainer from "../core/module/ModuleContainer.js";
4
- import TableContainer from "../core/table/TableContainer.js";
5
- const serverAPI = {
6
- getTableList: async ()=>{
7
- const tables = ModuleContainer.moduleNames.reduce((tableList, moduleName)=>{
8
- const moduleTables = ModuleContainer.get(moduleName).getTableList();
9
- moduleTables.forEach((tableName)=>{
10
- const table = TableContainer.getTable(tableName);
11
- tableList.push(table.schema.table);
12
- });
13
- return tableList;
14
- }, []);
15
- return {
16
- tables
17
- };
18
- },
19
- getTableSchema: async ({ module, table })=>ModuleContainer.get(module).getTableSchema(table),
20
- rpcCall: async ()=>{
21
- throw Error("RpcCall no longer supported on LocalDataSourceProvider ServerAPI");
2
+ import { DataProvider } from "@vuu-ui/core";
3
+ import { DataProvider as vuu_utils_DataProvider } from "@vuu-ui/vuu-utils";
4
+ import ModuleContainer, { ensureVuuModule } from "../core/module/ModuleContainer.js";
5
+ const createLocalDataSourceProvider = (modules)=>{
6
+ const ownModules = modules ? new Map(modules.map((module)=>[
7
+ module.name,
8
+ module
9
+ ])) : void 0;
10
+ const getModule = (moduleName)=>{
11
+ if (ownModules) {
12
+ const module = ownModules.get(moduleName);
13
+ if (module) return module;
14
+ throw Error(`[LocalDataSourceProvider] module ${moduleName} is not available on this server`);
15
+ }
16
+ return ModuleContainer.get(moduleName);
17
+ };
18
+ const serverAPI = {
19
+ getTableList: async ()=>{
20
+ const tables = [];
21
+ for (const moduleName of ownModules?.keys() ?? ModuleContainer.moduleNames){
22
+ const module = getModule(moduleName);
23
+ for (const tableName of module.getTableList())tables.push(module.getTableSchema(tableName).table);
24
+ }
25
+ return {
26
+ tables
27
+ };
28
+ },
29
+ getTableSchema: async ({ module, table })=>getModule(module).getTableSchema(table),
30
+ rpcCall: async ()=>{
31
+ throw Error("RpcCall no longer supported on LocalDataSourceProvider ServerAPI");
32
+ }
33
+ };
34
+ const getServerAPI = async ()=>serverAPI;
35
+ class VuuDataSource {
36
+ constructor({ aggregations, baseFilterSpec, columns, filterSpec, groupBy, session, sort, table, viewport, visualLink }){
37
+ const config = {
38
+ aggregations,
39
+ baseFilterSpec,
40
+ columns,
41
+ filterSpec,
42
+ groupBy,
43
+ session,
44
+ sort,
45
+ visualLink
46
+ };
47
+ const module = getModule(table.module);
48
+ return module.createDataSource(table.table, viewport, config);
49
+ }
22
50
  }
51
+ const DataSourceProvider = ({ children })=>/*#__PURE__*/ jsx(vuu_utils_DataProvider, {
52
+ VuuDataSource: VuuDataSource,
53
+ getServerAPI: getServerAPI,
54
+ children: /*#__PURE__*/ jsx(DataProvider, {
55
+ VuuDataSource: VuuDataSource,
56
+ getServerAPI: getServerAPI,
57
+ children: children
58
+ })
59
+ });
60
+ return DataSourceProvider;
23
61
  };
24
- const getServerAPI = async ()=>serverAPI;
25
- class VuuDataSource {
26
- constructor({ aggregations, baseFilterSpec, columns, filterSpec, groupBy, session, sort, table, viewport, visualLink }){
27
- const config = {
28
- aggregations,
29
- baseFilterSpec,
30
- columns,
31
- filterSpec,
32
- groupBy,
33
- session,
34
- sort,
35
- visualLink
36
- };
37
- const module = ModuleContainer.get(table.module);
38
- return module.createDataSource(table.table, viewport, config);
39
- }
40
- }
41
- const LocalDataSourceProvider = ({ children })=>/*#__PURE__*/ jsx(DataProvider, {
42
- VuuDataSource: VuuDataSource,
43
- getServerAPI: getServerAPI,
44
- children: children
45
- });
46
- export { LocalDataSourceProvider };
62
+ const LocalDataSourceProvider = createLocalDataSourceProvider();
63
+ const createLocalVuuServer = ({ connectionId, modules })=>{
64
+ modules.forEach(ensureVuuModule);
65
+ return {
66
+ connectionId,
67
+ DataSourceProvider: createLocalDataSourceProvider(modules)
68
+ };
69
+ };
70
+ export { LocalDataSourceProvider, createLocalVuuServer };