@platform-mesh/portal-server-lib 0.11.2 → 0.11.3

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.
@@ -11,6 +11,7 @@ import { Injectable } from '@nestjs/common';
11
11
  import { GraphQLClient } from 'graphql-request';
12
12
  import { PermissionsProxyService } from '../services/permissions/permissions-proxy.service.js';
13
13
  import { processContentConfigurationForAccountHierarchy } from '../utils/account-hierarchy-resolver.js';
14
+ import { normalizePortalWebComponentModules } from '../utils/portal-web-component-modules.js';
14
15
  import { contentConfigurationsQuery } from './contentconfigurations-query.js';
15
16
  import { welcomeNodeConfig } from './models/welcome-node-config.js';
16
17
  let ContentConfigurationServiceProvidersService = class ContentConfigurationServiceProvidersService {
@@ -57,6 +58,7 @@ let ContentConfigurationServiceProvidersService = class ContentConfigurationServ
57
58
  contentConfiguration.url = item.spec.remoteConfiguration?.url;
58
59
  }
59
60
  processContentConfigurationForAccountHierarchy(contentConfiguration, context);
61
+ normalizePortalWebComponentModules(contentConfiguration);
60
62
  return contentConfiguration;
61
63
  }
62
64
  catch (parseError) {
@@ -1 +1 @@
1
- {"version":3,"file":"content-configuration-service-providers.service.js","sourceRoot":"","sources":["../../../src/portal-options/service-providers/content-configuration-service-providers.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"content-configuration-service-providers.service.js","sourceRoot":"","sources":["../../../src/portal-options/service-providers/content-configuration-service-providers.service.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,7 @@ import { Injectable } from '@nestjs/common';
11
11
  import { KcpKubernetesService } from '../services/kcp-k8s.service.js';
12
12
  import { PermissionsProxyService } from '../services/permissions/permissions-proxy.service.js';
13
13
  import { processContentConfigurationForAccountHierarchy } from '../utils/account-hierarchy-resolver.js';
14
+ import { normalizePortalWebComponentModules } from '../utils/portal-web-component-modules.js';
14
15
  import { welcomeNodeConfig } from './models/welcome-node-config.js';
15
16
  let KubernetesServiceProvidersService = class KubernetesServiceProvidersService {
16
17
  kcpKubernetesService;
@@ -44,6 +45,7 @@ let KubernetesServiceProvidersService = class KubernetesServiceProvidersService
44
45
  contentConfiguration.url = item.spec.remoteConfiguration?.url;
45
46
  }
46
47
  processContentConfigurationForAccountHierarchy(contentConfiguration, context);
48
+ normalizePortalWebComponentModules(contentConfiguration);
47
49
  return contentConfiguration;
48
50
  });
49
51
  const accountPath = context?.accountPath ?? context?.['core_platform-mesh_io_account'] ?? '';
@@ -1 +1 @@
1
- {"version":3,"file":"kubernetes-service-providers.service.js","sourceRoot":"","sources":["../../../src/portal-options/service-providers/kubernetes-service-providers.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"kubernetes-service-providers.service.js","sourceRoot":"","sources":["../../../src/portal-options/service-providers/kubernetes-service-providers.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { ContentConfiguration } from '@openmfp/portal-server-lib';
2
+ export declare function normalizePortalWebComponentModules(contentConfiguration: ContentConfiguration): void;
@@ -0,0 +1,40 @@
1
+ const PORTAL_BUNDLE_PATH = '/assets/platform-mesh-portal-ui-wc.js';
2
+ function isPortalBundleUrl(value) {
3
+ if (typeof value !== 'string')
4
+ return false;
5
+ return (value === PORTAL_BUNDLE_PATH ||
6
+ value.startsWith(`${PORTAL_BUNDLE_PATH}#`) ||
7
+ value.startsWith(`${PORTAL_BUNDLE_PATH}?`));
8
+ }
9
+ function normalizeNode(node, defaults) {
10
+ if (!node || typeof node !== 'object')
11
+ return node;
12
+ const effective = { ...defaults, ...node };
13
+ const result = { ...node };
14
+ const webcomponent = effective.webcomponent;
15
+ if (webcomponent &&
16
+ typeof webcomponent === 'object' &&
17
+ webcomponent.selfRegistered === true &&
18
+ webcomponent.type === undefined &&
19
+ isPortalBundleUrl(effective.url ?? effective.viewUrl)) {
20
+ result.webcomponent = { ...webcomponent, type: 'module' };
21
+ }
22
+ if (Array.isArray(effective.children)) {
23
+ result.children = effective.children.map((child) => normalizeNode(child, defaults));
24
+ }
25
+ if (Array.isArray(effective.compound?.children)) {
26
+ result.compound = {
27
+ ...effective.compound,
28
+ children: effective.compound.children.map((child) => normalizeNode(child, undefined)),
29
+ };
30
+ }
31
+ return result;
32
+ }
33
+ export function normalizePortalWebComponentModules(contentConfiguration) {
34
+ const data = contentConfiguration?.luigiConfigFragment?.data;
35
+ if (!Array.isArray(data?.nodes))
36
+ return;
37
+ const defaults = data.nodeDefaults;
38
+ data.nodes = data.nodes.map((node) => normalizeNode(node, defaults));
39
+ }
40
+ //# sourceMappingURL=portal-web-component-modules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal-web-component-modules.js","sourceRoot":"","sources":["../../../src/portal-options/utils/portal-web-component-modules.ts"],"names":[],"mappings":"AAEA,MAAM,kBAAkB,GAAG,uCAAuC,CAAC;AAEnE,SAAS,iBAAiB,CAAC,KAAc;IACvC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAI5C,OAAO,CACL,KAAK,KAAK,kBAAkB;QAC5B,KAAK,CAAC,UAAU,CAAC,GAAG,kBAAkB,GAAG,CAAC;QAC1C,KAAK,CAAC,UAAU,CAAC,GAAG,kBAAkB,GAAG,CAAC,CAC3C,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CACpB,IAAe,EACf,QAA+B;IAE/B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAInD,MAAM,SAAS,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,IAAI,EAAE,CAAC;IAC3C,MAAM,MAAM,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;IAC3B,MAAM,YAAY,GAAG,SAAS,CAAC,YAAY,CAAC;IAC5C,IACE,YAAY;QACZ,OAAO,YAAY,KAAK,QAAQ;QAChC,YAAY,CAAC,cAAc,KAAK,IAAI;QACpC,YAAY,CAAC,IAAI,KAAK,SAAS;QAC/B,iBAAiB,CAAC,SAAS,CAAC,GAAG,IAAI,SAAS,CAAC,OAAO,CAAC,EACrD,CAAC;QACD,MAAM,CAAC,YAAY,GAAG,EAAE,GAAG,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC5D,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;QACtC,MAAM,CAAC,QAAQ,GAAG,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACjD,aAAa,CAAC,KAAK,EAAE,QAAQ,CAAC,CAC/B,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC;QAChD,MAAM,CAAC,QAAQ,GAAG;YAChB,GAAG,SAAS,CAAC,QAAQ;YAGrB,QAAQ,EAAE,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAgB,EAAE,EAAE,CAC7D,aAAa,CAAC,KAAK,EAAE,SAAS,CAAC,CAChC;SACF,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,kCAAkC,CAChD,oBAA0C;IAE1C,MAAM,IAAI,GAAG,oBAAoB,EAAE,mBAAmB,EAAE,IAAI,CAAC;IAC7D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;QAAE,OAAO;IAGxC,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAqC,CAAC;IAC5D,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;AACvE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platform-mesh/portal-server-lib",
3
- "version": "0.11.2",
3
+ "version": "0.11.3",
4
4
  "author": "Platform Mesh",
5
5
  "license": "Apache-2.0",
6
6
  "publishConfig": {
@@ -45,6 +45,59 @@ describe('ContentConfigurationServiceProvidersService', () => {
45
45
  ).rejects.toThrow('Token is required');
46
46
  });
47
47
 
48
+ it.each([{ entities: [] }, { entities: ['core_platform-mesh_io_account'] }])(
49
+ 'normalizes root and entity configuration through the GraphQL provider for %p',
50
+ async ({ entities }) => {
51
+ const config = {
52
+ name: 'accounts',
53
+ luigiConfigFragment: {
54
+ data: {
55
+ nodes: [
56
+ {
57
+ label: 'Accounts',
58
+ url: '/assets/platform-mesh-portal-ui-wc.js#generic-list-view',
59
+ webcomponent: { selfRegistered: true },
60
+ },
61
+ ],
62
+ },
63
+ },
64
+ };
65
+ mockClient.request.mockResolvedValue({
66
+ ui_platform_mesh_io: {
67
+ v1alpha1: {
68
+ ContentConfigurations: {
69
+ items: [
70
+ {
71
+ metadata: {
72
+ name: 'accounts',
73
+ labels: {
74
+ 'ui.platform-mesh.io/entity': entities[0] ?? 'main',
75
+ },
76
+ },
77
+ spec: {},
78
+ status: { configurationResult: JSON.stringify(config) },
79
+ },
80
+ ],
81
+ },
82
+ },
83
+ },
84
+ });
85
+
86
+ const result = await service.getServiceProviders(
87
+ 'token',
88
+ entities,
89
+ context,
90
+ );
91
+ expect(
92
+ result.rawServiceProviders[0].contentConfiguration[0].luigiConfigFragment
93
+ .data.nodes[0].webcomponent,
94
+ ).toEqual({
95
+ selfRegistered: true,
96
+ type: 'module',
97
+ });
98
+ },
99
+ );
100
+
48
101
  it('throws if context organization is missing', async () => {
49
102
  const badContext = { ...context, organization: undefined } as any;
50
103
  await expect(
@@ -8,6 +8,7 @@ import { GraphQLClient } from 'graphql-request';
8
8
  import { RequestContext } from '../pm-request-context-provider.js';
9
9
  import { PermissionsProxyService } from '../services/permissions/permissions-proxy.service.js';
10
10
  import { processContentConfigurationForAccountHierarchy } from '../utils/account-hierarchy-resolver.js';
11
+ import { normalizePortalWebComponentModules } from '../utils/portal-web-component-modules.js';
11
12
  import { contentConfigurationsQuery } from './contentconfigurations-query.js';
12
13
  import { ContentConfigurationQueryResponse } from './models/contentconfigurations.js';
13
14
  import { welcomeNodeConfig } from './models/welcome-node-config.js';
@@ -93,6 +94,7 @@ export class ContentConfigurationServiceProvidersService implements ServiceProvi
93
94
  contentConfiguration,
94
95
  context,
95
96
  );
97
+ normalizePortalWebComponentModules(contentConfiguration);
96
98
 
97
99
  return contentConfiguration;
98
100
  } catch (parseError) {
@@ -70,6 +70,72 @@ describe('KubernetesServiceProvidersService', () => {
70
70
  });
71
71
 
72
72
  describe('getServiceProviders', () => {
73
+ it.each([{ entities: [] }, { entities: ['core_platform-mesh_io_account'] }])(
74
+ 'normalizes built-in and provider bundle nodes for entity selection %p',
75
+ async ({ entities }) => {
76
+ const portalOrigin = 'https://portal.example.test';
77
+ const config = {
78
+ name: 'accounts',
79
+ luigiConfigFragment: {
80
+ data: {
81
+ nodes: [
82
+ {
83
+ label: 'Accounts',
84
+ pathSegment: 'accounts',
85
+ url: '/assets/platform-mesh-portal-ui-wc.js#generic-list-view',
86
+ webcomponent: { selfRegistered: true },
87
+ },
88
+ {
89
+ label: 'Provider detail',
90
+ url: '/assets/platform-mesh-portal-ui-wc.js#generic-detail-view',
91
+ webcomponent: { selfRegistered: true },
92
+ },
93
+ {
94
+ label: 'Absolute URL',
95
+ url: `${portalOrigin}/assets/platform-mesh-portal-ui-wc.js#generic-list-view`,
96
+ webcomponent: { selfRegistered: true },
97
+ },
98
+ {
99
+ label: 'Other view',
100
+ url: 'https://provider.example.test/index.html',
101
+ },
102
+ ],
103
+ },
104
+ },
105
+ };
106
+ kcpKubernetesServiceMock.listClusterCustomObjectInKcpVirtualWorkspace.mockResolvedValue(
107
+ {
108
+ items: [
109
+ { status: { configurationResult: JSON.stringify(config) }, spec: {} },
110
+ ],
111
+ },
112
+ );
113
+
114
+ const result = await service.getServiceProviders(
115
+ mockToken,
116
+ entities,
117
+ mockContext,
118
+ );
119
+ const normalized = result.rawServiceProviders[0].contentConfiguration[0];
120
+ expect(
121
+ normalized.luigiConfigFragment.data.nodes.map(
122
+ (node) => node.webcomponent,
123
+ ),
124
+ ).toEqual([
125
+ { selfRegistered: true, type: 'module' },
126
+ { selfRegistered: true, type: 'module' },
127
+ { selfRegistered: true },
128
+ undefined,
129
+ ]);
130
+ expect(permissionsProxyServiceMock.resolvePermissions).toHaveBeenCalledWith(
131
+ mockToken,
132
+ mockContext.organization,
133
+ '',
134
+ [normalized],
135
+ );
136
+ },
137
+ );
138
+
73
139
  it('should throw error when token is missing', async () => {
74
140
  await expect(
75
141
  service.getServiceProviders('', mockEntities, mockContext),
@@ -8,6 +8,7 @@ import { K8sRequestContext, K8sResourceDescriptor } from '../models/k8s.js';
8
8
  import { KcpKubernetesService } from '../services/kcp-k8s.service.js';
9
9
  import { PermissionsProxyService } from '../services/permissions/permissions-proxy.service.js';
10
10
  import { processContentConfigurationForAccountHierarchy } from '../utils/account-hierarchy-resolver.js';
11
+ import { normalizePortalWebComponentModules } from '../utils/portal-web-component-modules.js';
11
12
  import { welcomeNodeConfig } from './models/welcome-node-config.js';
12
13
 
13
14
  @Injectable()
@@ -62,6 +63,7 @@ export class KubernetesServiceProvidersService implements ServiceProviderService
62
63
  contentConfiguration,
63
64
  context,
64
65
  );
66
+ normalizePortalWebComponentModules(contentConfiguration);
65
67
 
66
68
  return contentConfiguration;
67
69
  });
@@ -0,0 +1,277 @@
1
+ import { ContentConfiguration, LuigiNode } from '@openmfp/portal-server-lib';
2
+ import { normalizePortalWebComponentModules } from './portal-web-component-modules.js';
3
+
4
+ const bundle = '/assets/platform-mesh-portal-ui-wc.js';
5
+ const origin = 'https://portal.example.test';
6
+ const node = (overrides: LuigiNode = {}): LuigiNode => ({
7
+ label: 'Accounts',
8
+ pathSegment: 'accounts',
9
+ url: `${bundle}#generic-list-view`,
10
+ webcomponent: { selfRegistered: true },
11
+ ...overrides,
12
+ });
13
+ const configuration = (
14
+ nodes: LuigiNode[],
15
+ defaults?: Partial<LuigiNode>,
16
+ ): ContentConfiguration => ({
17
+ name: 'accounts',
18
+ creationTimestamp: '',
19
+ luigiConfigFragment: { data: { nodes, nodeDefaults: defaults } },
20
+ });
21
+
22
+ describe('normalizePortalWebComponentModules', () => {
23
+ it('repairs the built-in Accounts list and its nested detail view', () => {
24
+ const config = configuration([
25
+ node({
26
+ entityType: 'main',
27
+ children: [
28
+ node({
29
+ pathSegment: ':accountId',
30
+ url: `${bundle}#generic-detail-view`,
31
+ }),
32
+ ],
33
+ }),
34
+ ]);
35
+ normalizePortalWebComponentModules(config);
36
+
37
+ const accounts = config.luigiConfigFragment.data.nodes[0];
38
+ expect(accounts.webcomponent).toEqual({
39
+ selfRegistered: true,
40
+ type: 'module',
41
+ });
42
+ expect(accounts.entityType).toBe('main');
43
+ expect((accounts.children as LuigiNode[])[0].webcomponent).toEqual({
44
+ selfRegistered: true,
45
+ type: 'module',
46
+ });
47
+ });
48
+
49
+ it('uses each child URL rather than inheriting an iframe parent URL', () => {
50
+ const config = configuration([
51
+ { url: 'https://provider.example.test/index.html', children: [node()] },
52
+ ]);
53
+ normalizePortalWebComponentModules(config);
54
+ const parent = config.luigiConfigFragment.data.nodes[0];
55
+ expect(parent.webcomponent).toBeUndefined();
56
+ expect((parent.children as LuigiNode[])[0].webcomponent).toEqual({
57
+ selfRegistered: true,
58
+ type: 'module',
59
+ });
60
+ });
61
+
62
+ it('materializes default web-component settings only for the matching node', () => {
63
+ const defaults = Object.freeze({
64
+ webcomponent: Object.freeze({ selfRegistered: true }),
65
+ });
66
+ const config = configuration(
67
+ [
68
+ { url: `${bundle}#generic-list-view` },
69
+ { url: 'https://provider.example.test/other.js#other-view' },
70
+ { url: `${bundle}#generic-list-view`, webcomponent: false },
71
+ ],
72
+ defaults,
73
+ );
74
+ normalizePortalWebComponentModules(config);
75
+
76
+ expect(
77
+ config.luigiConfigFragment.data.nodes.map((n) => n.webcomponent),
78
+ ).toEqual([{ selfRegistered: true, type: 'module' }, undefined, false]);
79
+ expect(defaults.webcomponent).toEqual({ selfRegistered: true });
80
+ });
81
+
82
+ it('matches the server shallow merge when a node overrides default type or URL', () => {
83
+ const config = configuration(
84
+ [
85
+ { url: 'https://provider.example.test/index.html' },
86
+ node(),
87
+ { pathSegment: 'inherited' },
88
+ ],
89
+ {
90
+ url: `${bundle}#generic-detail-view`,
91
+ webcomponent: { selfRegistered: true, type: 'classic' },
92
+ },
93
+ );
94
+ normalizePortalWebComponentModules(config);
95
+
96
+ expect(
97
+ config.luigiConfigFragment.data.nodes.map((n) => n.webcomponent),
98
+ ).toEqual([undefined, { selfRegistered: true, type: 'module' }, undefined]);
99
+ expect(config.luigiConfigFragment.data.nodeDefaults).toEqual({
100
+ url: `${bundle}#generic-detail-view`,
101
+ webcomponent: { selfRegistered: true, type: 'classic' },
102
+ });
103
+ });
104
+
105
+ it('applies global defaults to ordinary children but not to compound views', () => {
106
+ const config = configuration(
107
+ [
108
+ {
109
+ children: [{ url: `${bundle}#generic-list-view` }],
110
+ compound: {
111
+ children: [
112
+ { viewUrl: `${bundle}#generic-list-view` },
113
+ node({
114
+ url: undefined,
115
+ viewUrl: `${bundle}#generic-detail-view`,
116
+ }),
117
+ ],
118
+ },
119
+ },
120
+ ],
121
+ { webcomponent: { selfRegistered: true } },
122
+ );
123
+ normalizePortalWebComponentModules(config);
124
+
125
+ const parent = config.luigiConfigFragment.data.nodes[0];
126
+ expect((parent.children as LuigiNode[])[0].webcomponent).toEqual({
127
+ selfRegistered: true,
128
+ type: 'module',
129
+ });
130
+ expect(parent.compound.children[0].webcomponent).toBeUndefined();
131
+ expect(parent.compound.children[1].webcomponent).toEqual({
132
+ selfRegistered: true,
133
+ type: 'module',
134
+ });
135
+ });
136
+
137
+ it('copies inherited children without modifying shared defaults', () => {
138
+ const defaults = { children: [node({ children: [] })] };
139
+ const config = configuration(
140
+ [{ pathSegment: 'first' }, { pathSegment: 'second' }],
141
+ defaults,
142
+ );
143
+ normalizePortalWebComponentModules(config);
144
+ for (const parent of config.luigiConfigFragment.data.nodes) {
145
+ expect((parent.children as LuigiNode[])[0].webcomponent).toEqual({
146
+ selfRegistered: true,
147
+ type: 'module',
148
+ });
149
+ }
150
+ expect(defaults.children[0].webcomponent).toEqual({ selfRegistered: true });
151
+ });
152
+
153
+ it.each(['module', 'classic', '', null])(
154
+ 'preserves explicitly configured type %p',
155
+ (type) => {
156
+ const config = configuration([
157
+ node({ webcomponent: { selfRegistered: true, type } }),
158
+ ]);
159
+ normalizePortalWebComponentModules(config);
160
+ expect(config.luigiConfigFragment.data.nodes[0].webcomponent).toEqual({
161
+ selfRegistered: true,
162
+ type,
163
+ });
164
+ },
165
+ );
166
+
167
+ it.each([undefined, false, true, { selfRegistered: false }])(
168
+ 'preserves non-self-registered setting %p',
169
+ (webcomponent) => {
170
+ const config = configuration([node({ webcomponent })]);
171
+ normalizePortalWebComponentModules(config);
172
+ expect(config.luigiConfigFragment.data.nodes[0].webcomponent).toEqual(
173
+ webcomponent,
174
+ );
175
+ },
176
+ );
177
+
178
+ it.each([
179
+ bundle,
180
+ `${bundle}?v=2#generic-list-view`,
181
+ `${bundle}#generic-detail-view`,
182
+ ])('recognizes the exact portal-owned bundle %s', (url) => {
183
+ const config = configuration([node({ url })]);
184
+ normalizePortalWebComponentModules(config);
185
+ expect(config.luigiConfigFragment.data.nodes[0].webcomponent).toEqual({
186
+ selfRegistered: true,
187
+ type: 'module',
188
+ });
189
+ });
190
+
191
+ it.each([
192
+ undefined,
193
+ './assets/platform-mesh-portal-ui-wc.js#generic-list-view',
194
+ `${bundle}.other#generic-list-view`,
195
+ '/assets/openmfp-portal-ui-wc.js#generic-list-view',
196
+ `${origin}${bundle}#generic-list-view`,
197
+ `https://other.example.test${bundle}#generic-list-view`,
198
+ `http://portal.example.test${bundle}#generic-list-view`,
199
+ `https://portal.example.test:8443${bundle}#generic-list-view`,
200
+ `https://user@portal.example.test${bundle}#generic-list-view`,
201
+ `https://:password@portal.example.test${bundle}#generic-list-view`,
202
+ `//portal.example.test${bundle}#generic-list-view`,
203
+ 'not a URL',
204
+ ])('preserves an unrelated or unsafe URL %s', (url) => {
205
+ const config = configuration([node({ url })]);
206
+ normalizePortalWebComponentModules(config);
207
+ expect(config.luigiConfigFragment.data.nodes[0].webcomponent).toEqual({
208
+ selfRegistered: true,
209
+ });
210
+ });
211
+
212
+ it('does not use a remote provider URL as the portal origin', () => {
213
+ const config = configuration([
214
+ node({ url: `https://provider.example.test${bundle}#generic-list-view` }),
215
+ ]);
216
+ config.url = 'https://provider.example.test/content.json';
217
+ normalizePortalWebComponentModules(config);
218
+ expect(config.luigiConfigFragment.data.nodes[0].webcomponent).toEqual({
219
+ selfRegistered: true,
220
+ });
221
+ });
222
+
223
+ it('uses url before viewUrl and does not traverse arbitrary context data', () => {
224
+ const context = { resourceDefinition: { example: node() } };
225
+ const config = configuration([
226
+ node({
227
+ url: 'https://provider.example.test/index.html',
228
+ viewUrl: `${bundle}#generic-list-view`,
229
+ context,
230
+ }),
231
+ ]);
232
+ normalizePortalWebComponentModules(config);
233
+ expect(config.luigiConfigFragment.data.nodes[0].webcomponent).toEqual({
234
+ selfRegistered: true,
235
+ });
236
+ expect(context.resourceDefinition.example.webcomponent).toEqual({
237
+ selfRegistered: true,
238
+ });
239
+ });
240
+
241
+ it('preserves function children and supports repeated normalization', () => {
242
+ const children = () => [];
243
+ const config = configuration([node({ children })]);
244
+ normalizePortalWebComponentModules(config);
245
+ const normalized = structuredClone({
246
+ ...config.luigiConfigFragment.data.nodes[0],
247
+ children: undefined,
248
+ });
249
+ normalizePortalWebComponentModules(config);
250
+ expect(config.luigiConfigFragment.data.nodes[0].children).toBe(children);
251
+ expect({
252
+ ...config.luigiConfigFragment.data.nodes[0],
253
+ children: undefined,
254
+ }).toEqual(normalized);
255
+ });
256
+
257
+ it.each([
258
+ null,
259
+ {},
260
+ { luigiConfigFragment: {} },
261
+ { luigiConfigFragment: { data: { nodes: null } } },
262
+ ])('ignores absent node structures %p', (value) => {
263
+ expect(() =>
264
+ normalizePortalWebComponentModules(value as ContentConfiguration),
265
+ ).not.toThrow();
266
+ });
267
+
268
+ it('preserves malformed scalar nodes without reading their properties', () => {
269
+ const config = configuration([
270
+ null,
271
+ 'invalid',
272
+ 7,
273
+ ] as unknown as LuigiNode[]);
274
+ normalizePortalWebComponentModules(config);
275
+ expect(config.luigiConfigFragment.data.nodes).toEqual([null, 'invalid', 7]);
276
+ });
277
+ });
@@ -0,0 +1,66 @@
1
+ import { ContentConfiguration, LuigiNode } from '@openmfp/portal-server-lib';
2
+
3
+ const PORTAL_BUNDLE_PATH = '/assets/platform-mesh-portal-ui-wc.js';
4
+
5
+ function isPortalBundleUrl(value: unknown): boolean {
6
+ if (typeof value !== 'string') return false;
7
+ // These paths always resolve against the portal, including remote provider
8
+ // configurations. Do not infer ownership from a provider's configuration URL.
9
+ // Leave absolute URLs unchanged instead of inferring an origin from proxies.
10
+ return (
11
+ value === PORTAL_BUNDLE_PATH ||
12
+ value.startsWith(`${PORTAL_BUNDLE_PATH}#`) ||
13
+ value.startsWith(`${PORTAL_BUNDLE_PATH}?`)
14
+ );
15
+ }
16
+
17
+ function normalizeNode(
18
+ node: LuigiNode,
19
+ defaults: LuigiNode | undefined,
20
+ ): LuigiNode {
21
+ if (!node || typeof node !== 'object') return node;
22
+
23
+ // Match OpenMFP createNode's shallow default merge. Write the fallback onto
24
+ // this node, never onto shared defaults that also apply to unrelated views.
25
+ const effective = { ...defaults, ...node };
26
+ const result = { ...node };
27
+ const webcomponent = effective.webcomponent;
28
+ if (
29
+ webcomponent &&
30
+ typeof webcomponent === 'object' &&
31
+ webcomponent.selfRegistered === true &&
32
+ webcomponent.type === undefined &&
33
+ isPortalBundleUrl(effective.url ?? effective.viewUrl)
34
+ ) {
35
+ result.webcomponent = { ...webcomponent, type: 'module' };
36
+ }
37
+
38
+ if (Array.isArray(effective.children)) {
39
+ result.children = effective.children.map((child) =>
40
+ normalizeNode(child, defaults),
41
+ );
42
+ }
43
+ if (Array.isArray(effective.compound?.children)) {
44
+ result.compound = {
45
+ ...effective.compound,
46
+ // Compound children are already Luigi views: the server does not apply
47
+ // the content configuration's nodeDefaults to them.
48
+ children: effective.compound.children.map((child: LuigiNode) =>
49
+ normalizeNode(child, undefined),
50
+ ),
51
+ };
52
+ }
53
+
54
+ return result;
55
+ }
56
+
57
+ export function normalizePortalWebComponentModules(
58
+ contentConfiguration: ContentConfiguration,
59
+ ): void {
60
+ const data = contentConfiguration?.luigiConfigFragment?.data;
61
+ if (!Array.isArray(data?.nodes)) return;
62
+
63
+ // Runtime JSON can use Luigi node properties beyond LuigiNodeDefaults' type.
64
+ const defaults = data.nodeDefaults as LuigiNode | undefined;
65
+ data.nodes = data.nodes.map((node) => normalizeNode(node, defaults));
66
+ }