@heswell/vuu-portal 2.2.0 → 2.2.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.
- package/195.js +68 -8
- package/README.md +1 -1
- package/config/default-modules.yaml +4 -0
- package/package.json +1 -1
- package/src/modules/ModuleDiscovery/ModuleStore.d.ts +2 -36
package/195.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { MODULE_ADMIN_RPC, executeModuleAdminRpc, managedModuleColumnValues, managedModulePermissionValues, splitNotificationTopics, validateModuleConfig } from "@heswell/module-admin";
|
|
2
|
+
import { ConfigFactory, EditSessionRpcHandler, ModuleFactory, NotificationModule, NullProvider, PermissionFilter, Provider, TableDef, VUU_DEFAULT_COLUMNS, ViewPortDef, createConfiguredAuthProviders, createVuuServerApplication, getLogger } from "@heswell/vuu-server";
|
|
3
3
|
import node_fs from "node:fs";
|
|
4
4
|
import node_path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -31,7 +31,8 @@ function readModules(table) {
|
|
|
31
31
|
mfComponent: stringValue(table, row, "mfComponent"),
|
|
32
32
|
mfScope: stringValue(table, row, "mfScope"),
|
|
33
33
|
mfUrl: stringValue(table, row, "mfUrl"),
|
|
34
|
-
...navIcon(table, row)
|
|
34
|
+
...navIcon(table, row),
|
|
35
|
+
...navMetadata(table, row)
|
|
35
36
|
};
|
|
36
37
|
});
|
|
37
38
|
}
|
|
@@ -41,6 +42,23 @@ function navIcon(table, row) {
|
|
|
41
42
|
navIconUrl
|
|
42
43
|
} : {};
|
|
43
44
|
}
|
|
45
|
+
function navMetadata(table, row) {
|
|
46
|
+
const metadata = {};
|
|
47
|
+
const navOrder = optionalValue(table, row, "navOrder");
|
|
48
|
+
if ("number" == typeof navOrder && Number.isFinite(navOrder)) metadata.navOrder = navOrder;
|
|
49
|
+
const navBadge = optionalValue(table, row, "navBadge");
|
|
50
|
+
if ("string" == typeof navBadge && navBadge) metadata.navBadge = navBadge;
|
|
51
|
+
const topics = optionalValue(table, row, "notificationTopics");
|
|
52
|
+
if ("string" == typeof topics) {
|
|
53
|
+
const notificationTopics = splitNotificationTopics(topics);
|
|
54
|
+
if (notificationTopics.length) metadata.notificationTopics = notificationTopics;
|
|
55
|
+
}
|
|
56
|
+
return metadata;
|
|
57
|
+
}
|
|
58
|
+
function optionalValue(table, row, column) {
|
|
59
|
+
const index = table.tableDef.columns.findIndex(({ name })=>name === column);
|
|
60
|
+
return -1 === index ? void 0 : row[index];
|
|
61
|
+
}
|
|
44
62
|
function readModulePermissions(table) {
|
|
45
63
|
return table.rows.map((row)=>({
|
|
46
64
|
moduleId: numberValue(table, row, "module_id"),
|
|
@@ -70,7 +88,13 @@ function selectModules(modules, modulePermissions, authorizations) {
|
|
|
70
88
|
...latestByName.values()
|
|
71
89
|
];
|
|
72
90
|
const permittedIds = new Set(permittedModules.map(({ id })=>id));
|
|
73
|
-
return permittedModules.filter(({ parentModuleId })=>0 === parentModuleId || permittedIds.has(parentModuleId)).map(({ parentModuleId: _parentModuleId, ...module })=>module).sort(
|
|
91
|
+
return permittedModules.filter(({ parentModuleId })=>0 === parentModuleId || permittedIds.has(parentModuleId)).map(({ parentModuleId: _parentModuleId, ...module })=>module).sort(compareModules);
|
|
92
|
+
}
|
|
93
|
+
function compareModules(left, right) {
|
|
94
|
+
const leftOrder = left.navOrder ?? 1 / 0;
|
|
95
|
+
const rightOrder = right.navOrder ?? 1 / 0;
|
|
96
|
+
if (leftOrder !== rightOrder) return leftOrder < rightOrder ? -1 : 1;
|
|
97
|
+
return left.name.localeCompare(right.name);
|
|
74
98
|
}
|
|
75
99
|
function accessRoleForModule(module, moduleRoles, permittedModuleRoles) {
|
|
76
100
|
const ownRole = permittedModuleRoles.get(module.id);
|
|
@@ -152,6 +176,18 @@ const modulesTable = TableDef({
|
|
|
152
176
|
name: "navIconUrl",
|
|
153
177
|
dataType: "string"
|
|
154
178
|
},
|
|
179
|
+
{
|
|
180
|
+
name: "navOrder",
|
|
181
|
+
dataType: "double"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: "navBadge",
|
|
185
|
+
dataType: "string"
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: "notificationTopics",
|
|
189
|
+
dataType: "string"
|
|
190
|
+
},
|
|
155
191
|
...VUU_DEFAULT_COLUMNS
|
|
156
192
|
],
|
|
157
193
|
keyField: "id",
|
|
@@ -332,9 +368,12 @@ function readModulesFile(filePath) {
|
|
|
332
368
|
return parsed.modules;
|
|
333
369
|
}
|
|
334
370
|
function cloneModules(modules) {
|
|
335
|
-
return modules.map((module)=>(
|
|
336
|
-
|
|
337
|
-
|
|
371
|
+
return modules.map((module)=>Object.fromEntries(Object.entries(module).filter(([, value])=>void 0 !== value).map(([key, value])=>[
|
|
372
|
+
key,
|
|
373
|
+
Array.isArray(value) ? [
|
|
374
|
+
...value
|
|
375
|
+
] : value
|
|
376
|
+
])));
|
|
338
377
|
}
|
|
339
378
|
const managedModuleKeys = [
|
|
340
379
|
"id",
|
|
@@ -354,6 +393,11 @@ const managedModuleKeys = [
|
|
|
354
393
|
"navIconUrl",
|
|
355
394
|
"accessRole"
|
|
356
395
|
];
|
|
396
|
+
const optionalModuleKeys = [
|
|
397
|
+
"navOrder",
|
|
398
|
+
"navBadge",
|
|
399
|
+
"notificationTopics"
|
|
400
|
+
];
|
|
357
401
|
const retiredModuleKeys = [
|
|
358
402
|
"vuuConnectionId",
|
|
359
403
|
"vuuWebsocketUrl",
|
|
@@ -361,7 +405,7 @@ const retiredModuleKeys = [
|
|
|
361
405
|
];
|
|
362
406
|
function parseManagedModule(value, index) {
|
|
363
407
|
if (!isRecord(value)) throw new Error(`Modules file entry ${index} must be an object`);
|
|
364
|
-
for (const key of Object.keys(value))if (!managedModuleKeys.includes(key) && !retiredModuleKeys.includes(key)) throw new Error(`Modules file entry ${index} contains unknown field '${key}'`);
|
|
408
|
+
for (const key of Object.keys(value))if (!managedModuleKeys.includes(key) && !optionalModuleKeys.includes(key) && !retiredModuleKeys.includes(key)) throw new Error(`Modules file entry ${index} contains unknown field '${key}'`);
|
|
365
409
|
const module = {};
|
|
366
410
|
for (const key of managedModuleKeys){
|
|
367
411
|
const fieldValue = value[key];
|
|
@@ -377,6 +421,18 @@ function parseManagedModule(value, index) {
|
|
|
377
421
|
if ("number" === expected && !Number.isInteger(fieldValue)) throw new Error(`Modules file entry ${index} field '${key}' must be an integer`);
|
|
378
422
|
module[key] = fieldValue;
|
|
379
423
|
}
|
|
424
|
+
for (const key of optionalModuleKeys){
|
|
425
|
+
const fieldValue = value[key];
|
|
426
|
+
if (null == fieldValue) continue;
|
|
427
|
+
const valid = "navOrder" === key ? "number" == typeof fieldValue : "navBadge" === key ? "string" == typeof fieldValue : Array.isArray(fieldValue) && fieldValue.every((topic)=>"string" == typeof topic);
|
|
428
|
+
if (!valid) {
|
|
429
|
+
const expected = "navOrder" === key ? "a number" : "navBadge" === key ? "a string" : "a list of strings";
|
|
430
|
+
throw new Error(`Modules file entry ${index} field '${key}' must be ${expected}`);
|
|
431
|
+
}
|
|
432
|
+
module[key] = Array.isArray(fieldValue) ? [
|
|
433
|
+
...fieldValue
|
|
434
|
+
] : fieldValue;
|
|
435
|
+
}
|
|
380
436
|
return module;
|
|
381
437
|
}
|
|
382
438
|
function assertValidManagedModules(modules, filePath) {
|
|
@@ -498,6 +554,10 @@ function createPortalApplication({ config, configFile } = {}) {
|
|
|
498
554
|
moduleRegistry: createModuleRegistry(tableContainer, user)
|
|
499
555
|
}),
|
|
500
556
|
modules: [
|
|
557
|
+
NotificationModule((table)=>new NullProvider(table), (viewport)=>PermissionFilter("audience", new Set([
|
|
558
|
+
"all",
|
|
559
|
+
viewport.user.name
|
|
560
|
+
])), (table)=>ViewPortDef["default"](table.tableDef.columns)),
|
|
501
561
|
ModuleDiscoveryModule(loadModuleState(portalConfig), {
|
|
502
562
|
adminRole: loadModuleAdminRole(portalConfig)
|
|
503
563
|
})
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @heswell/vuu-portal
|
|
2
2
|
|
|
3
3
|
Vuu portal server: a [`@heswell/vuu-server`](../vuu-server) application that
|
|
4
|
-
hosts the `MODULE_DISCOVERY`
|
|
4
|
+
hosts the `MODULE_DISCOVERY` and `NOTIFICATIONS` modules and returns each user's module registry in
|
|
5
5
|
the login response. Requires the [Bun](https://bun.sh) runtime to run the
|
|
6
6
|
server. The library exports can be imported under Node.js too.
|
|
7
7
|
|
package/package.json
CHANGED
|
@@ -6,48 +6,14 @@ export interface ModuleStore {
|
|
|
6
6
|
export declare class InMemoryModuleStore implements ModuleStore {
|
|
7
7
|
private modules;
|
|
8
8
|
constructor(modules?: readonly ManagedModule[]);
|
|
9
|
-
load():
|
|
10
|
-
parentModuleId: number;
|
|
11
|
-
name: string;
|
|
12
|
-
title: string;
|
|
13
|
-
description: string;
|
|
14
|
-
enabled: boolean;
|
|
15
|
-
location: string;
|
|
16
|
-
path: string;
|
|
17
|
-
mfComponent: string;
|
|
18
|
-
mfScope: string;
|
|
19
|
-
mfUrl: string;
|
|
20
|
-
navIconUrl: string;
|
|
21
|
-
accessRole: string;
|
|
22
|
-
id: number;
|
|
23
|
-
version: number;
|
|
24
|
-
created: number;
|
|
25
|
-
updated: number;
|
|
26
|
-
}[];
|
|
9
|
+
load(): ManagedModule[];
|
|
27
10
|
save(modules: readonly ManagedModule[]): void;
|
|
28
11
|
}
|
|
29
12
|
export declare class ModuleState {
|
|
30
13
|
#private;
|
|
31
14
|
private readonly store;
|
|
32
15
|
constructor(store: ModuleStore, modules: readonly ManagedModule[]);
|
|
33
|
-
get modules():
|
|
34
|
-
parentModuleId: number;
|
|
35
|
-
name: string;
|
|
36
|
-
title: string;
|
|
37
|
-
description: string;
|
|
38
|
-
enabled: boolean;
|
|
39
|
-
location: string;
|
|
40
|
-
path: string;
|
|
41
|
-
mfComponent: string;
|
|
42
|
-
mfScope: string;
|
|
43
|
-
mfUrl: string;
|
|
44
|
-
navIconUrl: string;
|
|
45
|
-
accessRole: string;
|
|
46
|
-
id: number;
|
|
47
|
-
version: number;
|
|
48
|
-
created: number;
|
|
49
|
-
updated: number;
|
|
50
|
-
}[];
|
|
16
|
+
get modules(): ManagedModule[];
|
|
51
17
|
replaceAndSave(modules: readonly ManagedModule[]): void;
|
|
52
18
|
}
|
|
53
19
|
export declare class YamlModuleStore implements ModuleStore {
|