@vuu-ui/vuu-data-test 4.0.0-alpha.2 → 4.0.0-alpha.21

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 (53) hide show
  1. package/README.md +8 -0
  2. package/package.json +11 -9
  3. package/src/Table.js +5 -5
  4. package/src/TickingArrayDataSource.js +7 -2
  5. package/src/core/filter/PermissionFilter.js +60 -0
  6. package/src/core/filter/PermissionFilteredTable.js +59 -0
  7. package/src/core/module/ModuleContainer.js +8 -0
  8. package/src/core/module/VuuModule.js +57 -7
  9. package/src/core/table/TableContainer.js +1 -1
  10. package/src/core/user/CurrentUser.js +13 -0
  11. package/src/index.js +7 -0
  12. package/src/local-datasource-provider/LocalDatasourceProvider.js +64 -44
  13. package/src/module-admin/ModuleAdminModule.js +124 -0
  14. package/src/module-admin/index.js +3 -0
  15. package/src/module-admin/module-admin-schemas.js +95 -0
  16. package/src/module-admin/snapshot-projection.js +29 -0
  17. package/src/notifications/NotificationModule.js +131 -0
  18. package/src/notifications/NotificationsSchema.js +52 -0
  19. package/src/notifications/index.js +6 -0
  20. package/src/notifications/simulated/DismissNotificationRpcHandler.js +36 -0
  21. package/src/notifications/simulated/SimulatedNotificationsModule.js +18 -0
  22. package/src/notifications/simulated/SimulatedNotificationsProvider.js +151 -0
  23. package/src/notifications/simulated/notificationsModule.js +3 -0
  24. package/src/schemas.js +8 -2
  25. package/src/user-admin/UserAdminModule.js +567 -0
  26. package/src/user-admin/index.js +3 -0
  27. package/src/user-admin/initialSnapshot.js +461 -0
  28. package/src/user-admin/snapshot-projection.js +151 -0
  29. package/types/TickingArrayDataSource.d.ts +1 -1
  30. package/types/core/filter/PermissionFilter.d.ts +41 -0
  31. package/types/core/filter/PermissionFilteredTable.d.ts +24 -0
  32. package/types/core/module/ModuleContainer.d.ts +6 -3
  33. package/types/core/module/VuuModule.d.ts +22 -0
  34. package/types/core/table/TableContainer.d.ts +5 -5
  35. package/types/core/user/CurrentUser.d.ts +14 -0
  36. package/types/index.d.ts +7 -0
  37. package/types/local-datasource-provider/LocalDatasourceProvider.d.ts +18 -2
  38. package/types/module-admin/ModuleAdminModule.d.ts +32 -0
  39. package/types/module-admin/index.d.ts +4 -0
  40. package/types/module-admin/module-admin-schemas.d.ts +4 -0
  41. package/types/module-admin/snapshot-projection.d.ts +13 -0
  42. package/types/notifications/NotificationModule.d.ts +60 -0
  43. package/types/notifications/NotificationsSchema.d.ts +20 -0
  44. package/types/notifications/index.d.ts +6 -0
  45. package/types/notifications/simulated/DismissNotificationRpcHandler.d.ts +8 -0
  46. package/types/notifications/simulated/SimulatedNotificationsModule.d.ts +9 -0
  47. package/types/notifications/simulated/SimulatedNotificationsProvider.d.ts +21 -0
  48. package/types/notifications/simulated/notificationsModule.d.ts +5 -0
  49. package/types/schemas.d.ts +5 -3
  50. package/types/user-admin/UserAdminModule.d.ts +54 -0
  51. package/types/user-admin/index.d.ts +3 -0
  52. package/types/user-admin/initialSnapshot.d.ts +2 -0
  53. package/types/user-admin/snapshot-projection.d.ts +9 -0
@@ -0,0 +1,124 @@
1
+ import { DEFAULT_MODULE_DEFINITIONS, MODULE_ADMIN_RPC, MODULE_NAV_ICONS, executeModuleAdminRpc, toManagedModules } from "@heswell/module-admin/contracts";
2
+ import { VuuModule } from "../core/module/VuuModule.js";
3
+ import TableContainer from "../core/table/TableContainer.js";
4
+ import { buildDataColumnMapFromSchema } from "../Table.js";
5
+ import { MODULE_ADMIN_MODULE_NAME, MODULE_ADMIN_TABLE_SCHEMAS } from "./module-admin-schemas.js";
6
+ import { reconcileModuleAdminTables } from "./snapshot-projection.js";
7
+ const createTable = (tableName)=>{
8
+ const schema = MODULE_ADMIN_TABLE_SCHEMAS[tableName];
9
+ return TableContainer.createTable(schema, [], buildDataColumnMapFromSchema(schema));
10
+ };
11
+ const createTables = ()=>({
12
+ modulePermissions: createTable("modulePermissions"),
13
+ modules: createTable("modules")
14
+ });
15
+ const INITIAL_TIMESTAMP = 1710000000000;
16
+ const INITIAL_ACCESS_ROLES = [
17
+ {
18
+ moduleName: "moduleAdmin",
19
+ role: "module-admin-access"
20
+ },
21
+ {
22
+ moduleName: "userAdmin",
23
+ role: "user-admin-access"
24
+ },
25
+ {
26
+ moduleName: "basket-trading",
27
+ role: "basket-trading-access"
28
+ },
29
+ {
30
+ moduleName: "vuu-table-browser",
31
+ role: "vuu-table-browser-access"
32
+ }
33
+ ];
34
+ const EXAMPLE_MODULES = [
35
+ {
36
+ id: 6,
37
+ parentModuleId: 0,
38
+ name: "order-blotter",
39
+ title: "Order blotter",
40
+ description: "Monitor parent and child orders across all desks.",
41
+ version: 3,
42
+ enabled: false,
43
+ location: "/Trading/Orders",
44
+ path: "/trading/orders",
45
+ mfComponent: "OrderBlotter",
46
+ mfScope: "orderBlotter",
47
+ mfUrl: "http://localhost:5009",
48
+ navIconUrl: MODULE_NAV_ICONS.tables,
49
+ vuuConnectionId: "",
50
+ vuuWebsocketUrl: "",
51
+ vuuRestUrl: "",
52
+ accessRole: "",
53
+ created: INITIAL_TIMESTAMP,
54
+ updated: INITIAL_TIMESTAMP
55
+ }
56
+ ];
57
+ const MODULE_ADMIN_INITIAL_SNAPSHOT = {
58
+ modules: [
59
+ ...toManagedModules(DEFAULT_MODULE_DEFINITIONS, INITIAL_ACCESS_ROLES, INITIAL_TIMESTAMP),
60
+ ...EXAMPLE_MODULES
61
+ ]
62
+ };
63
+ class ModuleAdminModule extends VuuModule {
64
+ now;
65
+ #modules;
66
+ #tables;
67
+ constructor(snapshot = MODULE_ADMIN_INITIAL_SNAPSHOT, now = Date.now){
68
+ super(MODULE_ADMIN_MODULE_NAME), this.now = now;
69
+ this.#modules = snapshot.modules;
70
+ this.#tables = createTables();
71
+ reconcileModuleAdminTables(snapshot, this.#tables);
72
+ }
73
+ get modules() {
74
+ return this.#modules;
75
+ }
76
+ get menus() {
77
+ return {
78
+ modulePermissions: void 0,
79
+ modules: void 0
80
+ };
81
+ }
82
+ get includeDefaultServices() {
83
+ return false;
84
+ }
85
+ get menuServices() {}
86
+ get schemas() {
87
+ return MODULE_ADMIN_TABLE_SCHEMAS;
88
+ }
89
+ service = (rpcName)=>async (request)=>{
90
+ try {
91
+ if ("RPC_REQUEST" !== request.type || request.rpcName !== rpcName) throw new Error(`Expected ${rpcName} RPC request`);
92
+ const { modules, result } = executeModuleAdminRpc(this.#modules, rpcName, request.params, this.now);
93
+ this.#modules = modules;
94
+ reconcileModuleAdminTables({
95
+ modules
96
+ }, this.#tables);
97
+ return {
98
+ data: result,
99
+ type: "SUCCESS_RESULT"
100
+ };
101
+ } catch (error) {
102
+ return {
103
+ errorMessage: error instanceof Error ? error.message : "Module admin request failed",
104
+ type: "ERROR_RESULT"
105
+ };
106
+ }
107
+ };
108
+ get services() {
109
+ const services = Object.keys(MODULE_ADMIN_RPC).map((rpcName)=>({
110
+ rpcName,
111
+ service: this.service(rpcName)
112
+ }));
113
+ return {
114
+ modulePermissions: services,
115
+ modules: services
116
+ };
117
+ }
118
+ get tables() {
119
+ return this.#tables;
120
+ }
121
+ get visualLinks() {}
122
+ }
123
+ const moduleAdminModule = new ModuleAdminModule();
124
+ export { MODULE_ADMIN_INITIAL_SNAPSHOT, ModuleAdminModule, moduleAdminModule };
@@ -0,0 +1,3 @@
1
+ export { MODULE_ADMIN_INITIAL_SNAPSHOT, ModuleAdminModule, moduleAdminModule } from "./ModuleAdminModule.js";
2
+ export { MODULE_ADMIN_MODULE_NAME, MODULE_ADMIN_TABLE_SCHEMAS } from "./module-admin-schemas.js";
3
+ export { projectModuleAdminSnapshot, reconcileModuleAdminTables } from "./snapshot-projection.js";
@@ -0,0 +1,95 @@
1
+ import { VUU_DEFAULT_COLUMNS } from "../default-column-definitions.js";
2
+ const DEFAULT_RANGE_LIMITS = {
3
+ maxRangeEnd: 1000000,
4
+ maxRangeWidth: 1000
5
+ };
6
+ const MODULE_ADMIN_MODULE_NAME = "MODULE_DISCOVERY";
7
+ const MODULE_ADMIN_TABLE_SCHEMAS = {
8
+ modules: {
9
+ columns: [
10
+ {
11
+ name: "id",
12
+ serverDataType: "int"
13
+ },
14
+ {
15
+ name: "parentModuleId",
16
+ serverDataType: "int"
17
+ },
18
+ {
19
+ name: "name",
20
+ serverDataType: "string"
21
+ },
22
+ {
23
+ name: "title",
24
+ serverDataType: "string"
25
+ },
26
+ {
27
+ name: "description",
28
+ serverDataType: "string"
29
+ },
30
+ {
31
+ name: "version",
32
+ serverDataType: "int"
33
+ },
34
+ {
35
+ name: "enabled",
36
+ serverDataType: "boolean"
37
+ },
38
+ {
39
+ name: "location",
40
+ serverDataType: "string"
41
+ },
42
+ {
43
+ name: "path",
44
+ serverDataType: "string"
45
+ },
46
+ {
47
+ name: "mfComponent",
48
+ serverDataType: "string"
49
+ },
50
+ {
51
+ name: "mfScope",
52
+ serverDataType: "string"
53
+ },
54
+ {
55
+ name: "mfUrl",
56
+ serverDataType: "string"
57
+ },
58
+ {
59
+ name: "navIconUrl",
60
+ serverDataType: "string"
61
+ },
62
+ ...VUU_DEFAULT_COLUMNS
63
+ ],
64
+ key: "id",
65
+ rangeLimits: DEFAULT_RANGE_LIMITS,
66
+ table: {
67
+ module: MODULE_ADMIN_MODULE_NAME,
68
+ table: "modules"
69
+ }
70
+ },
71
+ modulePermissions: {
72
+ columns: [
73
+ {
74
+ name: "id",
75
+ serverDataType: "int"
76
+ },
77
+ {
78
+ name: "module_id",
79
+ serverDataType: "int"
80
+ },
81
+ {
82
+ name: "role",
83
+ serverDataType: "string"
84
+ },
85
+ ...VUU_DEFAULT_COLUMNS
86
+ ],
87
+ key: "id",
88
+ rangeLimits: DEFAULT_RANGE_LIMITS,
89
+ table: {
90
+ module: MODULE_ADMIN_MODULE_NAME,
91
+ table: "modulePermissions"
92
+ }
93
+ }
94
+ };
95
+ export { MODULE_ADMIN_MODULE_NAME, MODULE_ADMIN_TABLE_SCHEMAS };
@@ -0,0 +1,29 @@
1
+ import { managedModuleColumnValues, managedModulePermissionValues } from "@heswell/module-admin/contracts";
2
+ const rowsEqual = (left, right)=>left.length === right.length && left.every((value, index)=>value === right[index]);
3
+ const toRow = (table, values)=>table.schema.columns.map(({ name })=>values[name] ?? "");
4
+ const projectModuleAdminSnapshot = (snapshot, tables)=>({
5
+ modulePermissions: managedModulePermissionValues(snapshot.modules).map((values)=>toRow(tables.modulePermissions, values)),
6
+ modules: snapshot.modules.map((module)=>toRow(tables.modules, managedModuleColumnValues(module)))
7
+ });
8
+ const reconcileModuleAdminTables = (snapshot, tables)=>{
9
+ const projected = projectModuleAdminSnapshot(snapshot, tables);
10
+ for (const tableName of Object.keys(tables)){
11
+ const table = tables[tableName];
12
+ const desiredRows = projected[tableName];
13
+ const keyIndex = table.map[table.schema.key];
14
+ const desiredKeys = new Set(desiredRows.map((row)=>String(row[keyIndex])));
15
+ for (const existingRow of [
16
+ ...table.data
17
+ ]){
18
+ const key = String(existingRow[keyIndex]);
19
+ if (!desiredKeys.has(key)) table.delete(key);
20
+ }
21
+ for (const row of desiredRows){
22
+ const key = String(row[keyIndex]);
23
+ const existingRow = table.findByKey(key);
24
+ if (void 0 === existingRow) table.insert(row);
25
+ else if (!rowsEqual(existingRow, row)) table.updateRow(row);
26
+ }
27
+ }
28
+ };
29
+ export { projectModuleAdminSnapshot, reconcileModuleAdminTables };
@@ -0,0 +1,131 @@
1
+ import { VuuModule } from "../core/module/VuuModule.js";
2
+ import TableContainer from "../core/table/TableContainer.js";
3
+ import { buildDataColumnMapFromSchema } from "../Table.js";
4
+ import { NotificationsSchema } from "./NotificationsSchema.js";
5
+ const NAME = "NOTIFICATIONS";
6
+ const TABLE_NAME = "notifications";
7
+ const DEFAULT_RANGE_LIMITS = {
8
+ maxRangeEnd: 1000000,
9
+ maxRangeWidth: 1000
10
+ };
11
+ const VUU_DEFAULT_COLUMNS = [
12
+ {
13
+ name: "vuuCreatedTimestamp",
14
+ serverDataType: "epochtimestamp"
15
+ },
16
+ {
17
+ name: "vuuUpdatedTimestamp",
18
+ serverDataType: "epochtimestamp"
19
+ },
20
+ {
21
+ name: "vuuMsg",
22
+ serverDataType: "string"
23
+ }
24
+ ];
25
+ class NotificationModule extends VuuModule {
26
+ static NAME = NAME;
27
+ static TABLE_NAME = TABLE_NAME;
28
+ #autoStarted = false;
29
+ #expiryTimers = new Map();
30
+ #provider;
31
+ #running = false;
32
+ #schemas;
33
+ #services;
34
+ #tables;
35
+ constructor(providerFactory, permissionFunction, serviceFactory, ...additionalColumns){
36
+ super(NAME);
37
+ const schema = {
38
+ columns: [
39
+ ...NotificationsSchema.allFrom(...additionalColumns),
40
+ ...VUU_DEFAULT_COLUMNS
41
+ ],
42
+ key: "id",
43
+ rangeLimits: DEFAULT_RANGE_LIMITS,
44
+ table: {
45
+ module: NAME,
46
+ table: TABLE_NAME
47
+ }
48
+ };
49
+ const table = TableContainer.createTable(schema, [], buildDataColumnMapFromSchema(schema));
50
+ this.#schemas = {
51
+ [TABLE_NAME]: schema
52
+ };
53
+ this.#tables = {
54
+ [TABLE_NAME]: table
55
+ };
56
+ this.permissionFunctions = {
57
+ [TABLE_NAME]: permissionFunction
58
+ };
59
+ this.#provider = providerFactory(table);
60
+ this.#services = {
61
+ [TABLE_NAME]: serviceFactory(table, this)
62
+ };
63
+ }
64
+ beforeCreateDataSource() {
65
+ if (!this.#autoStarted) {
66
+ this.#autoStarted = true;
67
+ this.start();
68
+ }
69
+ }
70
+ publish(notification = {}) {
71
+ const table = this.#tables[TABLE_NAME];
72
+ const { map, schema } = table;
73
+ const id = notification.id ?? crypto.randomUUID();
74
+ const values = {
75
+ type: "toast",
76
+ title: "Notification",
77
+ message: "",
78
+ level: "INFO",
79
+ audience: "all",
80
+ ...notification,
81
+ id
82
+ };
83
+ const row = schema.columns.map(({ serverDataType })=>"string" === serverDataType ? "" : "boolean" === serverDataType ? false : 0);
84
+ for (const [name, value] of Object.entries(values)){
85
+ const idx = map[name];
86
+ if (void 0 !== idx && void 0 !== value) row[idx] = value;
87
+ }
88
+ const existingRow = table.findByKey(id);
89
+ if (existingRow) {
90
+ row[map.vuuCreatedTimestamp] = existingRow[map.vuuCreatedTimestamp];
91
+ table.updateRow(row);
92
+ } else table.insert(row);
93
+ clearTimeout(this.#expiryTimers.get(id));
94
+ this.#expiryTimers.delete(id);
95
+ const { expiryTime } = values;
96
+ if ("number" == typeof expiryTime && expiryTime > 0) this.#expiryTimers.set(id, setTimeout(()=>{
97
+ this.#expiryTimers.delete(id);
98
+ if (table.findByKey(id)) table.delete(id);
99
+ }, Math.max(0, expiryTime - Date.now())));
100
+ return id;
101
+ }
102
+ get isRunning() {
103
+ return this.#running;
104
+ }
105
+ start() {
106
+ if (!this.#running) {
107
+ this.#running = true;
108
+ this.#provider.start();
109
+ }
110
+ }
111
+ stop() {
112
+ this.#autoStarted = true;
113
+ if (this.#running) {
114
+ this.#running = false;
115
+ this.#provider.stop();
116
+ }
117
+ }
118
+ get menus() {}
119
+ get menuServices() {}
120
+ get schemas() {
121
+ return this.#schemas;
122
+ }
123
+ get services() {
124
+ return this.#services;
125
+ }
126
+ get tables() {
127
+ return this.#tables;
128
+ }
129
+ get visualLinks() {}
130
+ }
131
+ export { NotificationModule };
@@ -0,0 +1,52 @@
1
+ const VuuColumnDataTypes = {
2
+ boolean: "boolean",
3
+ char: "char",
4
+ double: "double",
5
+ epochtimestamp: "epochtimestamp",
6
+ epochtimestampnano: "epochtimestampnano",
7
+ int: "int",
8
+ long: "long",
9
+ string: "string"
10
+ };
11
+ const toColumn = (columnDefinition)=>{
12
+ if ("string" == typeof columnDefinition) {
13
+ const [name, dataType = ""] = columnDefinition.split(":");
14
+ const serverDataType = VuuColumnDataTypes[dataType.toLowerCase()];
15
+ if (name && serverDataType) return {
16
+ name,
17
+ serverDataType
18
+ };
19
+ throw Error(`[NotificationsSchema] invalid column definition '${columnDefinition}'`);
20
+ }
21
+ return columnDefinition;
22
+ };
23
+ const Id = "id:String";
24
+ const Type = "type:String";
25
+ const ExpiryTime = "expiryTime:EpochTimestamp";
26
+ const Title = "title:String";
27
+ const Message = "message:String";
28
+ const Level = "level:String";
29
+ const Audience = "audience:String";
30
+ const genericColumns = [
31
+ Id,
32
+ Type,
33
+ ExpiryTime,
34
+ Title,
35
+ Message,
36
+ Level,
37
+ Audience
38
+ ];
39
+ const NotificationsSchema = {
40
+ Id: Id,
41
+ Type: Type,
42
+ ExpiryTime: ExpiryTime,
43
+ Title: Title,
44
+ Message: Message,
45
+ Level: Level,
46
+ Audience: Audience,
47
+ allFrom: (...additionalColumns)=>[
48
+ ...genericColumns,
49
+ ...additionalColumns
50
+ ].map(toColumn)
51
+ };
52
+ export { NotificationsSchema, toColumn };
@@ -0,0 +1,6 @@
1
+ export * from "./NotificationModule.js";
2
+ export * from "./NotificationsSchema.js";
3
+ export * from "./simulated/DismissNotificationRpcHandler.js";
4
+ export * from "./simulated/SimulatedNotificationsModule.js";
5
+ export * from "./simulated/SimulatedNotificationsProvider.js";
6
+ export * from "./simulated/notificationsModule.js";
@@ -0,0 +1,36 @@
1
+ const errorResult = (errorMessage)=>({
2
+ type: "ERROR_RESULT",
3
+ errorMessage
4
+ });
5
+ const DismissNotificationRpcHandler = (table, module)=>{
6
+ const dismissNotification = async (rpcRequest)=>{
7
+ if ("VIEWPORT_CONTEXT" !== rpcRequest.context.type) return errorResult("dismissNotification requires a viewport context");
8
+ const { dataSource, table: viewportTable, user } = module.getSubscriptionByViewport(rpcRequest.context.viewPortId);
9
+ const selectedRowIds = dataSource.getSelectedRowIds();
10
+ const selection = selectedRowIds.includes("*") ? viewportTable.data.map((row)=>row[viewportTable.map[viewportTable.schema.key]]) : selectedRowIds;
11
+ const { map } = table;
12
+ for (const rowKey of selection){
13
+ const row = table.findByKey(rowKey);
14
+ if (row) {
15
+ const updatedRow = row.slice();
16
+ updatedRow[map.status] = "dismissed";
17
+ updatedRow[map.dismissedBy] = user.name;
18
+ table.updateRow(updatedRow);
19
+ }
20
+ }
21
+ return {
22
+ type: "SUCCESS_RESULT",
23
+ data: {
24
+ success: true,
25
+ dismissedCount: selection.length
26
+ }
27
+ };
28
+ };
29
+ return [
30
+ {
31
+ rpcName: "dismissNotification",
32
+ service: dismissNotification
33
+ }
34
+ ];
35
+ };
36
+ export { DismissNotificationRpcHandler };
@@ -0,0 +1,18 @@
1
+ import { PermissionFilter } from "../../core/filter/PermissionFilter.js";
2
+ import { NotificationModule } from "../NotificationModule.js";
3
+ import { DismissNotificationRpcHandler } from "./DismissNotificationRpcHandler.js";
4
+ import { SimulatedNotificationsProvider } from "./SimulatedNotificationsProvider.js";
5
+ const noSimulation = {
6
+ start: ()=>void 0,
7
+ stop: ()=>void 0
8
+ };
9
+ const SimulatedNotificationsModule = ({ simulate = true } = {})=>new NotificationModule((table)=>simulate ? new SimulatedNotificationsProvider(table) : noSimulation, (viewport)=>{
10
+ const currentUser = viewport.user.name;
11
+ return PermissionFilter("audience", new Set([
12
+ "all",
13
+ currentUser,
14
+ "admin",
15
+ "trader"
16
+ ]));
17
+ }, DismissNotificationRpcHandler, "source:String", "priority:Int", "status:String", "dismissedBy:String");
18
+ export { SimulatedNotificationsModule };
@@ -0,0 +1,151 @@
1
+ const sampleTemplates = [
2
+ [
3
+ "toast",
4
+ "Order Execution",
5
+ "Order #%04d filled %d shares of AAPL at $%d.%02d",
6
+ "INFO",
7
+ 12000,
8
+ "OMS",
9
+ 2,
10
+ "trader"
11
+ ],
12
+ [
13
+ "toast",
14
+ "Risk Limit Alert",
15
+ "Account ACC-%04d exceeded intraday VaR limit by %d%%",
16
+ "WARNING",
17
+ 15000,
18
+ "RISK",
19
+ 5,
20
+ "risk_manager"
21
+ ],
22
+ [
23
+ "toast",
24
+ "Feed Synchronization",
25
+ "Connected to pricing feed NYC-%02d with latency %dms",
26
+ "INFO",
27
+ 10000,
28
+ "PRICING",
29
+ 1,
30
+ "admin"
31
+ ],
32
+ [
33
+ "toast",
34
+ "Connection Warning",
35
+ "Intermittent packet loss detected on gateway LDN-%02d",
36
+ "WARNING",
37
+ 14000,
38
+ "GATEWAY",
39
+ 3,
40
+ "admin"
41
+ ],
42
+ [
43
+ "toast",
44
+ "Order Rejection",
45
+ "Order #%04d rejected by exchange: Insufficient margin",
46
+ "ERROR",
47
+ 18000,
48
+ "OMS",
49
+ 10,
50
+ "all"
51
+ ],
52
+ [
53
+ "banner",
54
+ "System Maintenance",
55
+ "Scheduled system maintenance will begin in %d minutes",
56
+ "WARNING",
57
+ 45000,
58
+ "SYSTEM",
59
+ 5,
60
+ "all"
61
+ ],
62
+ [
63
+ "banner",
64
+ "Market Status",
65
+ "US Equity markets are now OPEN. Trading session #%d active",
66
+ "INFO",
67
+ 60000,
68
+ "SYSTEM",
69
+ 1,
70
+ "all"
71
+ ]
72
+ ];
73
+ const MAX_ACTIVE_NOTIFICATIONS = 8;
74
+ const INITIAL_NOTIFICATIONS = 3;
75
+ const CYCLE_TIME_MS = 4000;
76
+ const randomInt = (bound)=>Math.floor(Math.random() * bound);
77
+ const formatMessage = (format, ...args)=>{
78
+ let argIndex = 0;
79
+ return format.replace(/%(0(\d+))?d|%%/g, (match, _, width)=>{
80
+ if ("%%" === match) return "%";
81
+ const value = String(args[argIndex++]);
82
+ return width ? value.padStart(Number(width), "0") : value;
83
+ });
84
+ };
85
+ class SimulatedNotificationsProvider {
86
+ #activeNotifications = new Map();
87
+ #table;
88
+ #timer;
89
+ constructor(table){
90
+ this.#table = table;
91
+ }
92
+ start() {
93
+ if (void 0 === this.#timer) {
94
+ for(let i = 0; i < INITIAL_NOTIFICATIONS; i++)this.generateRandomNotification();
95
+ this.#timer = setInterval(this.runOnce, CYCLE_TIME_MS);
96
+ }
97
+ }
98
+ stop() {
99
+ if (void 0 !== this.#timer) {
100
+ clearInterval(this.#timer);
101
+ this.#timer = void 0;
102
+ }
103
+ }
104
+ runOnce = ()=>{
105
+ try {
106
+ const now = Date.now();
107
+ for (const [id, expiryTime] of Array.from(this.#activeNotifications))if (expiryTime < now) {
108
+ this.#table.delete(id);
109
+ this.#activeNotifications.delete(id);
110
+ }
111
+ if (this.#activeNotifications.size < MAX_ACTIVE_NOTIFICATIONS) this.generateRandomNotification();
112
+ } catch (e) {
113
+ console.error("Error occurred in SimulatedNotificationsProvider runOnce", e);
114
+ }
115
+ };
116
+ generateRandomNotification() {
117
+ const id = crypto.randomUUID();
118
+ const now = Date.now();
119
+ const [type, title, messageFormat, level, duration, source, priority, audience] = sampleTemplates[randomInt(sampleTemplates.length)];
120
+ const expiryTime = now + duration;
121
+ const message = messageFormat.includes("%") ? formatMessage(messageFormat, randomInt(9000) + 1000, 10 * randomInt(90) + 100, randomInt(200) + 50, randomInt(99)) : messageFormat;
122
+ const rowData = {
123
+ id,
124
+ type,
125
+ expiryTime,
126
+ title,
127
+ message,
128
+ level,
129
+ audience,
130
+ source,
131
+ priority,
132
+ status: "",
133
+ dismissedBy: "",
134
+ vuuCreatedTimestamp: now,
135
+ vuuUpdatedTimestamp: now,
136
+ vuuMsg: ""
137
+ };
138
+ this.#table.insert(this.toDataRow(rowData));
139
+ this.#activeNotifications.set(id, expiryTime);
140
+ }
141
+ toDataRow(rowData) {
142
+ const { map, schema } = this.#table;
143
+ const row = Array(schema.columns.length);
144
+ for (const [name, value] of Object.entries(rowData)){
145
+ const idx = map[name];
146
+ if (void 0 !== idx) row[idx] = value;
147
+ }
148
+ return row;
149
+ }
150
+ }
151
+ export { SimulatedNotificationsProvider, formatMessage };
@@ -0,0 +1,3 @@
1
+ import { SimulatedNotificationsModule } from "./SimulatedNotificationsModule.js";
2
+ const notificationsModule = SimulatedNotificationsModule();
3
+ export { notificationsModule };
package/src/schemas.js CHANGED
@@ -1,15 +1,21 @@
1
1
  import { schemas } from "./basket/basket-schemas.js";
2
2
  import { schemas as simul_schemas_js_schemas } from "./simul/simul-schemas.js";
3
3
  import { testModule } from "./test/TestModule.js";
4
+ import { USER_ADMIN_TABLE_SCHEMAS } from "@heswell/user-admin/contracts";
5
+ import { MODULE_ADMIN_TABLE_SCHEMAS } from "./module-admin/index.js";
4
6
  const schemas_schemas = {
5
7
  ...schemas,
6
8
  ...simul_schemas_js_schemas,
7
- ...testModule.schemas
9
+ ...testModule.schemas,
10
+ ...USER_ADMIN_TABLE_SCHEMAS,
11
+ ...MODULE_ADMIN_TABLE_SCHEMAS
8
12
  };
9
13
  const allSchemas = {
10
14
  ...schemas,
11
15
  ...simul_schemas_js_schemas,
12
- ...testModule.schemas
16
+ ...testModule.schemas,
17
+ ...USER_ADMIN_TABLE_SCHEMAS,
18
+ ...MODULE_ADMIN_TABLE_SCHEMAS
13
19
  };
14
20
  const getAllSchemas = ()=>schemas_schemas;
15
21
  const getSchema = (tableName)=>{