@adobe/uix-host-react 0.10.5 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/components/ExtensibleWrapper/ExtensibleWrapper.d.ts +33 -0
- package/dist/components/ExtensibleWrapper/ExtensibleWrapper.d.ts.map +1 -0
- package/dist/components/ExtensibleWrapper/ExtensionManagerProvider.d.ts +129 -0
- package/dist/components/ExtensibleWrapper/ExtensionManagerProvider.d.ts.map +1 -0
- package/dist/components/ExtensibleWrapper/ExtensionManagerProvider.test.d.ts +2 -0
- package/dist/components/ExtensibleWrapper/ExtensionManagerProvider.test.d.ts.map +1 -0
- package/dist/components/ExtensibleWrapper/UrlExtensionProvider.d.ts +37 -0
- package/dist/components/ExtensibleWrapper/UrlExtensionProvider.d.ts.map +1 -0
- package/dist/components/ExtensibleWrapper/UrlExtensionProvider.test.d.ts +2 -0
- package/dist/components/ExtensibleWrapper/UrlExtensionProvider.test.d.ts.map +1 -0
- package/dist/components/ExtensibleWrapper/index.d.ts +4 -0
- package/dist/components/ExtensibleWrapper/index.d.ts.map +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/index.js +251 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/ExtensibleWrapper/ExtensibleWrapper.tsx +133 -0
- package/src/components/ExtensibleWrapper/ExtensionManagerProvider.test.ts +214 -0
- package/src/components/ExtensibleWrapper/ExtensionManagerProvider.ts +324 -0
- package/src/components/ExtensibleWrapper/UrlExtensionProvider.test.ts +119 -0
- package/src/components/ExtensibleWrapper/UrlExtensionProvider.ts +93 -0
- package/src/components/ExtensibleWrapper/index.ts +15 -0
- package/src/components/index.ts +1 -0
@@ -0,0 +1,324 @@
|
|
1
|
+
/*************************************************************************
|
2
|
+
* ADOBE CONFIDENTIAL
|
3
|
+
* ___________________
|
4
|
+
*
|
5
|
+
* Copyright 2024 Adobe
|
6
|
+
* All Rights Reserved.
|
7
|
+
*
|
8
|
+
* NOTICE: All information contained herein is, and remains
|
9
|
+
* the property of Adobe and its suppliers, if any. The intellectual
|
10
|
+
* and technical concepts contained herein are proprietary to Adobe
|
11
|
+
* and its suppliers and are protected by all applicable intellectual
|
12
|
+
* property laws, including trade secret and copyright laws.
|
13
|
+
* Dissemination of this information or reproduction of this material
|
14
|
+
* is strictly forbidden unless prior written permission is obtained
|
15
|
+
* from Adobe.
|
16
|
+
**************************************************************************/
|
17
|
+
|
18
|
+
import {
|
19
|
+
createExtensionRegistryAsObjectsProvider,
|
20
|
+
ExtensionsProvider,
|
21
|
+
InstalledExtensions,
|
22
|
+
} from "@adobe/uix-host";
|
23
|
+
|
24
|
+
const EXTENSION_MANAGER_URL_PROD = "https://aemx-mngr.adobe.io";
|
25
|
+
const EXTENSION_MANAGER_URL_STAGE = "https://aemx-mngr-stage.adobe.io";
|
26
|
+
|
27
|
+
const APP_REGISTRY_URL_PROD = "https://appregistry.adobe.io";
|
28
|
+
const APP_REGISTRY_URL_STAGE = "https://appregistry-stage.adobe.io";
|
29
|
+
|
30
|
+
// Extension Manager stores information about extension points that a particular extension implements
|
31
|
+
// in the "extensionPoints" array of objects of the following "ExtensionPoint" type
|
32
|
+
// where "extensionPoint" is the name of the extension point, for example, "aem/assets/details/1"
|
33
|
+
// "url" is the extension url for the specified extension point
|
34
|
+
type ExtensionPoint = {
|
35
|
+
extensionPoint: string;
|
36
|
+
url: string;
|
37
|
+
};
|
38
|
+
export type ExtensionManagerExtension = {
|
39
|
+
id: string;
|
40
|
+
name: string;
|
41
|
+
title: string;
|
42
|
+
description: string;
|
43
|
+
status: string;
|
44
|
+
supportEmail: string;
|
45
|
+
extId: string;
|
46
|
+
disabled: boolean;
|
47
|
+
extensionPoints: ExtensionPoint[];
|
48
|
+
scope: Record<string, unknown>;
|
49
|
+
configuration?: Record<string, unknown>;
|
50
|
+
};
|
51
|
+
|
52
|
+
type AuthEMConfig = {
|
53
|
+
schema: "Bearer" | "Basic";
|
54
|
+
imsToken: string;
|
55
|
+
};
|
56
|
+
export interface ExtensionManagerConfig {
|
57
|
+
apiKey: string;
|
58
|
+
auth: AuthEMConfig;
|
59
|
+
service: string;
|
60
|
+
extensionPoint: string;
|
61
|
+
version: string;
|
62
|
+
imsOrg: string;
|
63
|
+
baseUrl: string;
|
64
|
+
scope?: Record<string, string>;
|
65
|
+
}
|
66
|
+
|
67
|
+
/** Authentication configuration, including IMS Org ID, access token, and API key */
|
68
|
+
export interface AuthConfig {
|
69
|
+
/** IMS Org ID */
|
70
|
+
imsOrg: string;
|
71
|
+
/** Access token for the user */
|
72
|
+
imsToken: string;
|
73
|
+
/** API key */
|
74
|
+
apiKey: string;
|
75
|
+
}
|
76
|
+
|
77
|
+
/** Discovery configuration, including environment and repo Id */
|
78
|
+
export interface DiscoveryConfig {
|
79
|
+
/** Environment level for backend Extension resolution services */
|
80
|
+
experienceShellEnvironment?: "prod" | "stage";
|
81
|
+
scope?: Record<string, string>;
|
82
|
+
}
|
83
|
+
|
84
|
+
/** Extension point ID */
|
85
|
+
export interface ExtensionPointId {
|
86
|
+
/** Service name */
|
87
|
+
service: string;
|
88
|
+
/** Extension point name */
|
89
|
+
name: string;
|
90
|
+
/** Extension point version */
|
91
|
+
version: string;
|
92
|
+
}
|
93
|
+
|
94
|
+
/**
|
95
|
+
* Sets up new ExtensionsProvider with authentication and discovery information needed to fetch the list of
|
96
|
+
* Extensions from AppRegistry and Extension Manager service, along with the query string portion of URL
|
97
|
+
* to extract the information about development Extensions
|
98
|
+
*/
|
99
|
+
export interface ExtensionsProviderConfig {
|
100
|
+
/** Discovery configuration */
|
101
|
+
discoveryConfig: DiscoveryConfig;
|
102
|
+
/** Authentication configuration */
|
103
|
+
authConfig: AuthConfig;
|
104
|
+
/** Extension point ID */
|
105
|
+
extensionPointId: ExtensionPointId;
|
106
|
+
providerConfig: ExtensionProviderConfig;
|
107
|
+
}
|
108
|
+
|
109
|
+
export interface ExtensionProviderConfig {
|
110
|
+
extensionManagerUrl?: string;
|
111
|
+
appRegistryUrl?: string;
|
112
|
+
disableExtensionManager?: boolean;
|
113
|
+
}
|
114
|
+
export const getExtensionRegistryBaseUrl = (
|
115
|
+
environment: "prod" | "stage" | undefined,
|
116
|
+
registry: string | null
|
117
|
+
): string =>
|
118
|
+
environment === "prod"
|
119
|
+
? APP_REGISTRY_URL_PROD
|
120
|
+
: registry ?? APP_REGISTRY_URL_STAGE;
|
121
|
+
|
122
|
+
export const getExtensionManagerBaseUrl = (
|
123
|
+
environment: "prod" | "stage" | undefined,
|
124
|
+
extensionManager: string | null
|
125
|
+
): string =>
|
126
|
+
environment === "prod"
|
127
|
+
? EXTENSION_MANAGER_URL_PROD
|
128
|
+
: extensionManager ?? EXTENSION_MANAGER_URL_STAGE;
|
129
|
+
|
130
|
+
/**
|
131
|
+
* Extracts programId and envId from the repo value
|
132
|
+
* @param repo - the repo value
|
133
|
+
* @returns object with programId and envId
|
134
|
+
* @ignore
|
135
|
+
*/
|
136
|
+
export function extractProgramIdEnvId(repo: string): {
|
137
|
+
programId: string;
|
138
|
+
envId: string;
|
139
|
+
} {
|
140
|
+
const regex: RegExp = /p(\d+)-e(\d+)/;
|
141
|
+
const match: RegExpMatchArray | null = regex.exec(repo);
|
142
|
+
if (!match) {
|
143
|
+
throw new Error("Error parsing a repo value");
|
144
|
+
}
|
145
|
+
|
146
|
+
return {
|
147
|
+
programId: match[1],
|
148
|
+
envId: match[2],
|
149
|
+
};
|
150
|
+
}
|
151
|
+
|
152
|
+
/**
|
153
|
+
* Builds the URL for fetching extensions from the Extension Manager service
|
154
|
+
* @param config - the Extension Manager configuration
|
155
|
+
* @returns the URL for fetching extensions
|
156
|
+
* @ignore
|
157
|
+
*/
|
158
|
+
export function buildExtensionManagerUrl(
|
159
|
+
config: ExtensionManagerConfig
|
160
|
+
): string {
|
161
|
+
const scope = config.scope
|
162
|
+
? Object.fromEntries(
|
163
|
+
Object.entries(config.scope).map(([k, v]) => [`scope.${k}`, v])
|
164
|
+
)
|
165
|
+
: {};
|
166
|
+
const extensionPoints: string = `${config.service}/${config.extensionPoint}/${config.version}`;
|
167
|
+
const queryParams = new URLSearchParams({
|
168
|
+
...scope,
|
169
|
+
extensionPoints,
|
170
|
+
});
|
171
|
+
|
172
|
+
return `${config.baseUrl}/v2/extensions?${queryParams.toString()}`;
|
173
|
+
}
|
174
|
+
|
175
|
+
/**
|
176
|
+
* @ignore
|
177
|
+
*/
|
178
|
+
export async function fetchExtensionsFromExtensionManager(
|
179
|
+
config: ExtensionManagerConfig
|
180
|
+
): Promise<ExtensionManagerExtension[]> {
|
181
|
+
const resp: Response = await fetch(buildExtensionManagerUrl(config), {
|
182
|
+
headers: {
|
183
|
+
Authorization: `Bearer ${config.auth.imsToken}`,
|
184
|
+
"x-api-key": config.apiKey,
|
185
|
+
"x-org-id": config.imsOrg,
|
186
|
+
},
|
187
|
+
});
|
188
|
+
|
189
|
+
if (resp.status !== 200) {
|
190
|
+
throw new Error(
|
191
|
+
`Extension Manager returned non-200 response (${
|
192
|
+
resp.status
|
193
|
+
}): ${await resp.text()}`
|
194
|
+
);
|
195
|
+
}
|
196
|
+
|
197
|
+
return resp.json();
|
198
|
+
}
|
199
|
+
|
200
|
+
/**
|
201
|
+
* Takes an array of extensions from the App Registry, an array of extensions from the Extension Manager, and
|
202
|
+
* merges them into a list of Extensions. If an extension is disabled in the Extension Manager, it is removed from
|
203
|
+
* the list.
|
204
|
+
* Extension list from the App Registry is used as a base.
|
205
|
+
* @ignore
|
206
|
+
*/
|
207
|
+
export function mergeExtensions(
|
208
|
+
appRegistryExtensions: InstalledExtensions,
|
209
|
+
extensionManagerExtensions: ExtensionManagerExtension[],
|
210
|
+
extensionPointId: ExtensionPointId
|
211
|
+
): InstalledExtensions {
|
212
|
+
const mergedExtensions: InstalledExtensions = Object.assign(
|
213
|
+
appRegistryExtensions,
|
214
|
+
{}
|
215
|
+
);
|
216
|
+
extensionManagerExtensions.forEach((extension: ExtensionManagerExtension) => {
|
217
|
+
if (extension.disabled) {
|
218
|
+
// remove disabled extensions
|
219
|
+
delete mergedExtensions[extension.name];
|
220
|
+
} else {
|
221
|
+
const extPoint: ExtensionPoint | undefined =
|
222
|
+
extension.extensionPoints.find(
|
223
|
+
(_extensionPoint: ExtensionPoint) =>
|
224
|
+
_extensionPoint.extensionPoint ===
|
225
|
+
`${extensionPointId.service}/${extensionPointId.name}/${extensionPointId.version}`
|
226
|
+
);
|
227
|
+
if (extPoint) {
|
228
|
+
// add a new extension record or replace the existing one by an extension record from Extension Manager
|
229
|
+
// extension points are useful for filtering out extensions
|
230
|
+
mergedExtensions[extension.name] = {
|
231
|
+
id: extension.name,
|
232
|
+
url: extPoint.url,
|
233
|
+
configuration: extension.configuration,
|
234
|
+
extensionPoints: extension.extensionPoints.map(
|
235
|
+
(point) => point.extensionPoint
|
236
|
+
),
|
237
|
+
};
|
238
|
+
} else {
|
239
|
+
//this should never happen because we query Extension Manager service for our specific extension point
|
240
|
+
console.warn(
|
241
|
+
`Extension point ${extensionPointId.service}/${extensionPointId.name}/${extensionPointId.version} not found for extension ${extension.name}`
|
242
|
+
);
|
243
|
+
}
|
244
|
+
}
|
245
|
+
});
|
246
|
+
|
247
|
+
return mergedExtensions;
|
248
|
+
}
|
249
|
+
|
250
|
+
async function getExtensionManagerExtensions(
|
251
|
+
discoveryConfig: DiscoveryConfig,
|
252
|
+
authConfig: AuthConfig,
|
253
|
+
providerConfig: ExtensionProviderConfig,
|
254
|
+
extensionPointId: ExtensionPointId
|
255
|
+
): Promise<InstalledExtensions> {
|
256
|
+
const config = {
|
257
|
+
apiKey: authConfig.apiKey,
|
258
|
+
auth: {
|
259
|
+
schema: "Bearer",
|
260
|
+
imsToken: authConfig.imsToken,
|
261
|
+
},
|
262
|
+
service: extensionPointId.service,
|
263
|
+
extensionPoint: extensionPointId.name,
|
264
|
+
version: extensionPointId.version,
|
265
|
+
imsOrg: authConfig.imsOrg,
|
266
|
+
scope: discoveryConfig.scope,
|
267
|
+
};
|
268
|
+
|
269
|
+
const appRegistryConfig = {
|
270
|
+
...config,
|
271
|
+
baseUrl: getExtensionRegistryBaseUrl(
|
272
|
+
discoveryConfig.experienceShellEnvironment,
|
273
|
+
providerConfig.appRegistryUrl
|
274
|
+
),
|
275
|
+
} as ExtensionManagerConfig;
|
276
|
+
const appRegistryExtensionsProvider: ExtensionsProvider =
|
277
|
+
createExtensionRegistryAsObjectsProvider(appRegistryConfig);
|
278
|
+
|
279
|
+
const extensionManagerConfiguration = {
|
280
|
+
...config,
|
281
|
+
baseUrl: getExtensionManagerBaseUrl(
|
282
|
+
discoveryConfig.experienceShellEnvironment,
|
283
|
+
providerConfig.extensionManagerUrl
|
284
|
+
),
|
285
|
+
} as ExtensionManagerConfig;
|
286
|
+
const [appRegistryExtensions, extensionManagerExtensions] = await Promise.all(
|
287
|
+
[
|
288
|
+
appRegistryExtensionsProvider(),
|
289
|
+
providerConfig.disableExtensionManager
|
290
|
+
? []
|
291
|
+
: fetchExtensionsFromExtensionManager(extensionManagerConfiguration),
|
292
|
+
]
|
293
|
+
);
|
294
|
+
|
295
|
+
if (providerConfig.disableExtensionManager) {
|
296
|
+
return appRegistryExtensions;
|
297
|
+
} else {
|
298
|
+
return mergeExtensions(
|
299
|
+
appRegistryExtensions,
|
300
|
+
extensionManagerExtensions,
|
301
|
+
extensionPointId
|
302
|
+
);
|
303
|
+
}
|
304
|
+
}
|
305
|
+
|
306
|
+
/**
|
307
|
+
* Creates an extension manager extension provider
|
308
|
+
* @ignore
|
309
|
+
*/
|
310
|
+
export function createExtensionManagerExtensionsProvider(
|
311
|
+
discoveryConfig: DiscoveryConfig,
|
312
|
+
authConfig: AuthConfig,
|
313
|
+
providerConfig: ExtensionProviderConfig,
|
314
|
+
extensionPointId: ExtensionPointId
|
315
|
+
): ExtensionsProvider {
|
316
|
+
return () => {
|
317
|
+
return getExtensionManagerExtensions(
|
318
|
+
discoveryConfig,
|
319
|
+
authConfig,
|
320
|
+
providerConfig,
|
321
|
+
extensionPointId
|
322
|
+
);
|
323
|
+
};
|
324
|
+
}
|
@@ -0,0 +1,119 @@
|
|
1
|
+
/*
|
2
|
+
Copyright 2022 Adobe. All rights reserved.
|
3
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
5
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
|
7
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
8
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
9
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
10
|
+
governing permissions and limitations under the License.
|
11
|
+
*/
|
12
|
+
|
13
|
+
import {
|
14
|
+
extractExtUrlParams,
|
15
|
+
generateExtensionId,
|
16
|
+
createUrlExtensionsProvider,
|
17
|
+
} from "./UrlExtensionProvider";
|
18
|
+
import { ExtensionPointId } from "./ExtensionManagerProvider";
|
19
|
+
describe("extractExtUrlParams", () => {
|
20
|
+
it("should return an empty object when no query string is provided", () => {
|
21
|
+
expect(extractExtUrlParams(undefined)).toEqual({});
|
22
|
+
});
|
23
|
+
|
24
|
+
it("should return an empty object when the query string does not contain any valid extension params", () => {
|
25
|
+
expect(extractExtUrlParams("foo=bar&baz=qux")).toEqual({});
|
26
|
+
});
|
27
|
+
|
28
|
+
it("should extract valid extension params", () => {
|
29
|
+
const queryString =
|
30
|
+
"ext=foo&ext.service1.name1.version1=http://example.com";
|
31
|
+
const expectedParams = {
|
32
|
+
ext: "foo",
|
33
|
+
"ext.service1.name1.version1": "http://example.com",
|
34
|
+
};
|
35
|
+
expect(extractExtUrlParams(queryString)).toEqual(expectedParams);
|
36
|
+
});
|
37
|
+
|
38
|
+
it('should only include params with the "ext" prefix', () => {
|
39
|
+
const queryString =
|
40
|
+
"ext=foo&other=bar&ext.service1.name1.version1=http://example.com";
|
41
|
+
const expectedParams = {
|
42
|
+
ext: "foo",
|
43
|
+
"ext.service1.name1.version1": "http://example.com",
|
44
|
+
};
|
45
|
+
expect(extractExtUrlParams(queryString)).toEqual(expectedParams);
|
46
|
+
});
|
47
|
+
});
|
48
|
+
|
49
|
+
describe("generateExtensionId", () => {
|
50
|
+
it("should replace non-word characters with underscores", () => {
|
51
|
+
const url = "http://example.com/some/path";
|
52
|
+
expect(generateExtensionId(url)).toBe("http___example_com_some_path");
|
53
|
+
});
|
54
|
+
|
55
|
+
it("should return the same ID when there are no non-word characters", () => {
|
56
|
+
const url = "extension_1";
|
57
|
+
expect(generateExtensionId(url)).toBe("extension_1");
|
58
|
+
});
|
59
|
+
});
|
60
|
+
|
61
|
+
describe("createUrlExtensionsProvider", () => {
|
62
|
+
const mockExtensionPointId: ExtensionPointId = {
|
63
|
+
service: "service1",
|
64
|
+
name: "name1",
|
65
|
+
version: "version1",
|
66
|
+
};
|
67
|
+
|
68
|
+
it("should return an ExtensionsProvider that provides installed extensions", async () => {
|
69
|
+
const queryString =
|
70
|
+
"ext=foo&ext.service1/name1/version1=http://example2.com";
|
71
|
+
const provider = createUrlExtensionsProvider(
|
72
|
+
mockExtensionPointId,
|
73
|
+
queryString
|
74
|
+
);
|
75
|
+
|
76
|
+
const extensions = await provider();
|
77
|
+
expect(Object.keys(extensions)).toHaveLength(2);
|
78
|
+
expect(extensions).toHaveProperty("foo");
|
79
|
+
expect(extensions).toHaveProperty("http___example2_com");
|
80
|
+
expect(extensions["http___example2_com"]).toHaveProperty(
|
81
|
+
"url",
|
82
|
+
"http://example2.com"
|
83
|
+
);
|
84
|
+
});
|
85
|
+
|
86
|
+
it("should return an empty object if no valid extensions are found in the query string", async () => {
|
87
|
+
const queryString = "foo=bar&baz=qux";
|
88
|
+
const provider = createUrlExtensionsProvider(
|
89
|
+
mockExtensionPointId,
|
90
|
+
queryString
|
91
|
+
);
|
92
|
+
|
93
|
+
const extensions = await provider();
|
94
|
+
expect(extensions).toEqual({});
|
95
|
+
});
|
96
|
+
|
97
|
+
it("should filter extensions by the correct extension point", async () => {
|
98
|
+
const queryString =
|
99
|
+
"ext.service1/name1/version1=http://example1.com&ext.service2/name2/version2=https://www.test.";
|
100
|
+
const provider = createUrlExtensionsProvider(
|
101
|
+
mockExtensionPointId,
|
102
|
+
queryString
|
103
|
+
);
|
104
|
+
|
105
|
+
const extensions = await provider();
|
106
|
+
expect(extensions).toHaveProperty("http___example1_com");
|
107
|
+
});
|
108
|
+
|
109
|
+
it("should return an empty object when the query string does not match the expected extension point", async () => {
|
110
|
+
const queryString = "ext.service2.name2.version2=http://example1.com";
|
111
|
+
const provider = createUrlExtensionsProvider(
|
112
|
+
mockExtensionPointId,
|
113
|
+
queryString
|
114
|
+
);
|
115
|
+
|
116
|
+
const extensions = await provider();
|
117
|
+
expect(extensions).toEqual({});
|
118
|
+
});
|
119
|
+
});
|
@@ -0,0 +1,93 @@
|
|
1
|
+
/*************************************************************************
|
2
|
+
* ADOBE CONFIDENTIAL
|
3
|
+
* ___________________
|
4
|
+
*
|
5
|
+
* Copyright 2024 Adobe
|
6
|
+
* All Rights Reserved.
|
7
|
+
*
|
8
|
+
* NOTICE: All information contained herein is, and remains
|
9
|
+
* the property of Adobe and its suppliers, if any. The intellectual
|
10
|
+
* and technical concepts contained herein are proprietary to Adobe
|
11
|
+
* and its suppliers and are protected by all applicable intellectual
|
12
|
+
* property laws, including trade secret and copyright laws.
|
13
|
+
* Dissemination of this information or reproduction of this material
|
14
|
+
* is strictly forbidden unless prior written permission is obtained
|
15
|
+
* from Adobe.
|
16
|
+
**************************************************************************/
|
17
|
+
import { ExtensionsProvider, InstalledExtensions } from "@adobe/uix-host";
|
18
|
+
import { Extension } from "@adobe/uix-core";
|
19
|
+
import { ExtensionPointId } from "./ExtensionManagerProvider";
|
20
|
+
|
21
|
+
const EXT_PARAM_PREFIX = "ext";
|
22
|
+
|
23
|
+
export interface ExtUrlParams {
|
24
|
+
[key: string]: string;
|
25
|
+
}
|
26
|
+
|
27
|
+
/**
|
28
|
+
* Extracts extension URLs from the query string
|
29
|
+
* @ignore
|
30
|
+
*/
|
31
|
+
export function extractExtUrlParams(
|
32
|
+
queryString: string | undefined
|
33
|
+
): ExtUrlParams {
|
34
|
+
if (!queryString) {
|
35
|
+
return {};
|
36
|
+
}
|
37
|
+
const params: URLSearchParams = new URLSearchParams(queryString);
|
38
|
+
return Array.from(params.entries()).reduce((extParams, [key, value]) => {
|
39
|
+
if (key === EXT_PARAM_PREFIX || key.startsWith(`${EXT_PARAM_PREFIX}.`)) {
|
40
|
+
extParams[key] = value;
|
41
|
+
}
|
42
|
+
return extParams;
|
43
|
+
}, {} as ExtUrlParams);
|
44
|
+
}
|
45
|
+
|
46
|
+
/**
|
47
|
+
* Generates an extension ID from the extension URL
|
48
|
+
* @ignore
|
49
|
+
*/
|
50
|
+
export function generateExtensionId(extensionUrl: string): string {
|
51
|
+
return extensionUrl.replace(/\W/g, "_");
|
52
|
+
}
|
53
|
+
|
54
|
+
/**
|
55
|
+
* Creates an ExtensionsProvider that provides extensions from the URL
|
56
|
+
* @ignore
|
57
|
+
*/
|
58
|
+
export function createUrlExtensionsProvider(
|
59
|
+
extensionPointId: ExtensionPointId,
|
60
|
+
queryString: string | undefined
|
61
|
+
): ExtensionsProvider {
|
62
|
+
const extUrlParams: ExtUrlParams = extractExtUrlParams(queryString);
|
63
|
+
|
64
|
+
const extensionUrls: string[] = Object.keys(extUrlParams)
|
65
|
+
.filter(
|
66
|
+
(extParam) =>
|
67
|
+
extParam === EXT_PARAM_PREFIX ||
|
68
|
+
extParam ===
|
69
|
+
`${EXT_PARAM_PREFIX}.${extensionPointId.service}/${extensionPointId.name}/${extensionPointId.version}`
|
70
|
+
)
|
71
|
+
.flatMap((extParam) => {
|
72
|
+
const paramValue = extUrlParams[extParam];
|
73
|
+
// If it's a single value, return it in an array. If it's already an array, return it as is.
|
74
|
+
return Array.isArray(paramValue) ? paramValue : [paramValue];
|
75
|
+
});
|
76
|
+
|
77
|
+
const installedExtensions: InstalledExtensions = extensionUrls
|
78
|
+
.map((extensionUrl: string) => {
|
79
|
+
return {
|
80
|
+
id: generateExtensionId(extensionUrl),
|
81
|
+
url: extensionUrl,
|
82
|
+
extensionPoints: [
|
83
|
+
`${extensionPointId.service}/${extensionPointId.name}/${extensionPointId.version}`,
|
84
|
+
],
|
85
|
+
} as Extension;
|
86
|
+
})
|
87
|
+
.reduce((acc: InstalledExtensions, extension: Extension) => {
|
88
|
+
acc[extension.id] = extension;
|
89
|
+
return acc;
|
90
|
+
}, {} as InstalledExtensions);
|
91
|
+
|
92
|
+
return async () => installedExtensions;
|
93
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
Copyright 2022 Adobe. All rights reserved.
|
3
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
5
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
|
7
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
8
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
9
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
10
|
+
governing permissions and limitations under the License.
|
11
|
+
*/
|
12
|
+
|
13
|
+
export * from "./ExtensibleWrapper";
|
14
|
+
export * from "./UrlExtensionProvider";
|
15
|
+
export * from "./ExtensionManagerProvider";
|
package/src/components/index.ts
CHANGED