@adobe/uix-host-react 1.1.5 → 1.1.7

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.
@@ -1 +1 @@
1
- {"version":3,"file":"Extensible.d.ts","sourceRoot":"","sources":["../../src/components/Extensible.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,KAAK,EACV,mBAAmB,EACnB,kBAAkB,EAClB,UAAU,EACV,WAAW,EACX,mBAAmB,EACpB,MAAM,iBAAiB,CAAC;AAIzB,cAAc;AACd,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC;IACnE;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,kBAAkB,EAAE,kBAAkB,CAAC;IACvC;;OAEG;IACH,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B;;OAEG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,sBAAsB,CAAC,EAAE,CACvB,UAAU,EAAE,mBAAmB,KAC5B,mBAAmB,CAAC;CAC1B;AAoDD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,QAAQ,EACR,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,KAAK,EACL,aAAa,EACb,sBAAsB,GACvB,EAAE,iBAAiB,CAAC,eAAe,CAAC,qBAwFpC;AACD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"Extensible.d.ts","sourceRoot":"","sources":["../../src/components/Extensible.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,KAAK,EACV,mBAAmB,EACnB,kBAAkB,EAClB,UAAU,EACV,WAAW,EACX,mBAAmB,EACpB,MAAM,iBAAiB,CAAC;AAIzB,cAAc;AACd,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC;IACnE;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,kBAAkB,EAAE,kBAAkB,CAAC;IACvC;;OAEG;IACH,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B;;OAEG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,sBAAsB,CAAC,EAAE,CACvB,UAAU,EAAE,mBAAmB,KAC5B,mBAAmB,CAAC;CAC1B;AAoDD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,QAAQ,EACR,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,KAAK,EACL,aAAa,EACb,sBAAsB,GACvB,EAAE,iBAAiB,CAAC,eAAe,CAAC,qBAoHpC;AACD,eAAe,UAAU,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Extensible.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Extensible.test.d.ts","sourceRoot":"","sources":["../../src/components/Extensible.test.tsx"],"names":[],"mappings":""}
package/dist/index.js CHANGED
@@ -60,7 +60,11 @@ function Extensible({
60
60
  const [extensionListFetched, setExtensionListFetched] = React.useState(false);
61
61
  const prevSharedContext = React.useRef(JSON.stringify(sharedContext));
62
62
  React.useEffect(() => {
63
+ let cancelled = false;
63
64
  extensionsProvider().then((loaded) => {
65
+ if (cancelled) {
66
+ return;
67
+ }
64
68
  setExtensions((prev) => {
65
69
  let newExtensions = loaded;
66
70
  if (extensionsListCallback && loaded) {
@@ -74,12 +78,29 @@ function Extensible({
74
78
  }
75
79
  });
76
80
  }).catch((e) => {
77
- console.error("Fetching list of extensions failed!", e);
81
+ if (!cancelled) {
82
+ console.error("Fetching list of extensions failed!", e);
83
+ }
78
84
  }).finally(() => {
79
- setExtensionListFetched(true);
85
+ if (!cancelled) {
86
+ setExtensionListFetched(true);
87
+ }
80
88
  });
89
+ return () => {
90
+ cancelled = true;
91
+ };
81
92
  }, [extensionsProvider, extensionsListCallback]);
82
93
  const [host, setHost] = React.useState();
94
+ const hostRef = React.useRef();
95
+ React.useEffect(() => {
96
+ return () => {
97
+ if (hostRef.current) {
98
+ hostRef.current.unload().catch(() => {
99
+ });
100
+ hostRef.current = void 0;
101
+ }
102
+ };
103
+ }, []);
83
104
  React.useEffect(() => {
84
105
  function logError(msg) {
85
106
  return (e) => {
@@ -98,12 +119,17 @@ function Extensible({
98
119
  prevSharedContext.current = JSON.stringify(sharedContext);
99
120
  }
100
121
  if (!host || sharedContextChanged) {
122
+ if (hostRef.current) {
123
+ hostRef.current.unload().catch(() => {
124
+ });
125
+ }
101
126
  const newHost = new uixHost.Host({
102
127
  debug,
103
128
  hostName,
104
129
  runtimeContainer,
105
130
  sharedContext
106
131
  });
132
+ hostRef.current = newHost;
107
133
  setHost(newHost);
108
134
  loadExtensions(newHost);
109
135
  } else {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/components/Extensible.tsx","../src/extension-context.ts","../src/components/GuestUIFrame.tsx","../src/hooks/useHost.ts","../src/components/ExtensibleComponentBoundary.tsx","../src/components/ExtensibleWrapper/ExtensibleWrapper.tsx","../src/components/ExtensibleWrapper/ExtensionManagerProvider.ts","../src/components/ExtensibleWrapper/UrlExtensionProvider.ts","../src/hooks/useExtensions.ts","../src/hooks/useExtensionListFetched.ts"],"names":["React","useEffect","useRef","Host","createContext","useContext","useMemo","useState"],"mappings":"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","sourcesContent":["/*\nCopyright 2022 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nexport * from \"@adobe/uix-host\";\nexport * from \"./components/index.js\";\nexport * from \"./hooks/index.js\";\nexport * from \"./extension-context\";\n","/*\nCopyright 2022 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nimport React, { useState, useEffect, useRef } from \"react\";\nimport type { PropsWithChildren } from \"react\";\nimport type {\n InstalledExtensions,\n ExtensionsProvider,\n HostConfig,\n PortOptions,\n SharedContextValues,\n} from \"@adobe/uix-host\";\nimport { Host } from \"@adobe/uix-host\";\nimport { ExtensionContext } from \"../extension-context.js\";\n\n/** @public */\nexport interface ExtensibleProps extends Omit<HostConfig, \"hostName\"> {\n /**\n * Unique name for identifying this extensible app. May be used as metadata in\n * extension registry in the future.\n */\n appName?: string;\n /**\n * Function which returns a promise for the full list of extensions.\n */\n extensionsProvider: ExtensionsProvider;\n /**\n * {@inheritDoc HostConfig.guestOptions}\n */\n guestOptions?: PortOptions;\n /**\n * {@inheritDoc HostConfig.sharedContext}\n */\n sharedContext?: SharedContextValues;\n extensionsListCallback?: (\n extensions: InstalledExtensions\n ) => InstalledExtensions;\n}\n\nfunction areExtensionsDifferent(\n set1: InstalledExtensions,\n set2: InstalledExtensions\n) {\n const ids1 = Object.keys(set1).sort();\n const ids2 = Object.keys(set2).sort();\n\n if (ids1.length !== ids2.length) {\n return true;\n }\n\n let isDifferent = false;\n\n ids1.forEach((id) => {\n if (isDifferent) {\n return;\n }\n\n const ext1 = set1[id];\n const ext2 = set2[id];\n\n if (typeof ext1 !== typeof ext2) {\n isDifferent = true;\n return;\n }\n\n if (typeof ext1 === \"string\" && typeof ext2 === \"string\") {\n if (ext1 !== ext2) {\n isDifferent = true;\n return;\n }\n }\n\n if (typeof ext1 === \"object\" && typeof ext2 === \"object\") {\n if (\n ext1.url !== ext2.url ||\n JSON.stringify(ext1.extensionPoints) !==\n JSON.stringify(ext2.extensionPoints) ||\n JSON.stringify(ext1.configuration) !==\n JSON.stringify(ext2.configuration)\n ) {\n isDifferent = true;\n return;\n }\n }\n });\n\n return isDifferent;\n}\n\n/**\n * Declares an extensible area in an app, and provides host and extension\n * objects to all descendents. The {@link useExtensions} hook can only be called\n * in a descendent of this component.\n *\n * @remarks\n * For many apps, there will be only one Extensible provider, fairly high in the\n * component tree. It is a context provider component that may be bundled with\n * other context providers in your app architecture.\n *\n * Each Extensible element creates one {@link @adobe/uix-host#Host} object and\n * one call to an extensions provider. If multiple Extensible elements are used,\n * this may cause redundant calls. Such a design should be carefully considered.\n *\n * @public\n */\nexport function Extensible({\n appName,\n children,\n extensionsProvider,\n guestOptions,\n runtimeContainer,\n debug,\n sharedContext,\n extensionsListCallback,\n}: PropsWithChildren<ExtensibleProps>) {\n const hostName = appName || window.location.host || \"mainframe\";\n\n const [extensions, setExtensions] = useState<InstalledExtensions>({});\n const [extensionListFetched, setExtensionListFetched] =\n useState<boolean>(false);\n const prevSharedContext = useRef(JSON.stringify(sharedContext));\n useEffect(() => {\n extensionsProvider()\n .then((loaded: InstalledExtensions) => {\n setExtensions((prev) => {\n let newExtensions = loaded;\n\n if (extensionsListCallback && loaded) {\n newExtensions = extensionsListCallback(newExtensions);\n }\n\n const shouldUpdate = areExtensionsDifferent(prev, newExtensions);\n\n if (shouldUpdate) {\n return newExtensions;\n } else {\n return prev;\n }\n });\n })\n .catch((e: Error | unknown) => {\n console.error(\"Fetching list of extensions failed!\", e);\n })\n .finally(() => {\n setExtensionListFetched(true);\n });\n }, [extensionsProvider, extensionsListCallback]);\n\n const [host, setHost] = useState<Host>();\n useEffect(() => {\n function logError(msg: string) {\n return (e: Error | unknown) => {\n const error = e instanceof Error ? e : new Error(String(e));\n console.error(msg, error, extensions, guestOptions);\n };\n }\n\n if (!extensions || !Object.keys(extensions).length) {\n return;\n }\n\n const loadExtensions = (hostInstance: Host) => {\n hostInstance\n .load(extensions, guestOptions)\n .catch(logError(\"Load of extensions failed!\"));\n };\n\n const sharedContextChanged =\n prevSharedContext.current !== JSON.stringify(sharedContext);\n\n if (sharedContextChanged) {\n prevSharedContext.current = JSON.stringify(sharedContext);\n }\n if (!host || sharedContextChanged) {\n const newHost = new Host({\n debug,\n hostName,\n runtimeContainer,\n sharedContext,\n });\n setHost(newHost);\n loadExtensions(newHost);\n } else {\n loadExtensions(host);\n }\n }, [debug, hostName, runtimeContainer, extensions]);\n\n // skip render before host is initialized\n if (!host) {\n return <>{children}</>;\n }\n\n return (\n <ExtensionContext.Provider\n value={{\n host: host,\n extensionListFetched: extensionListFetched,\n }}\n >\n {children}\n </ExtensionContext.Provider>\n );\n}\nexport default Extensible;\n","/*\nCopyright 2022 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\n/**\n * @hidden\n */\nimport { createContext } from \"react\";\nimport { Host } from \"@adobe/uix-host\";\n\n/**\n * Context container with Host object and extensions load status.\n *\n * @internal\n */\nexport type ExtensibilityContext = {\n host: Host;\n extensionListFetched: boolean;\n};\n\n/**\n * @internal\n */\nexport const ExtensionContext = createContext<ExtensibilityContext>(\n {} as unknown as ExtensibilityContext\n);\n","/*\nCopyright 2022 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nimport { CrossRealmObject, UIFrameRect, VirtualApi } from \"@adobe/uix-core\";\nimport React, { useEffect, useRef } from \"react\";\nimport type { IframeHTMLAttributes } from \"react\";\nimport { useHost } from \"../hooks/useHost.js\";\nimport type { AttrTokens, SandboxToken } from \"@adobe/uix-host\";\nimport { makeSandboxAttrs, requiredIframeProps } from \"@adobe/uix-host\";\n\n/**\n * @internal\n */\ntype ReactIframeProps = IframeHTMLAttributes<HTMLIFrameElement>;\n\n/**\n * @public\n */\ntype FrameProps = Omit<ReactIframeProps, \"sandbox\">;\n\n/** @public */\nexport interface GuestUIProps extends FrameProps {\n guestId: string;\n /**\n * Receives the Penpal context when the frame is mounted.\n */\n onConnect?: () => void;\n /**\n * Called when the frame disconnects and unmounts.\n */\n onDisconnect?: () => void;\n /**\n * Called when the connection process throws an exception\n */\n onConnectionError?: (error: Error) => void;\n /**\n * Called when the document in the iframe changes size.\n */\n onResize?: (dimensions: UIFrameRect) => void;\n /**\n * Additional sandbox attributes GuestUIFrame might need.\n */\n sandbox?: AttrTokens<SandboxToken>;\n /**\n * Optional custom URL or path.\n */\n src: string;\n /**\n * Host methods to provide only to the guest inside all iframes.\n */\n methods?: VirtualApi;\n /**\n * Host methods to provide only to the guest inside this iframe.\n */\n privateMethods?: VirtualApi;\n}\n\nconst defaultIFrameProps: FrameProps = {\n width: \"100%\",\n height: \"100%\",\n style: {\n display: \"block\",\n border: \"none\",\n },\n};\n\nconst defaultSandbox = \"allow-scripts allow-forms allow-same-origin\";\n\n/**\n * An iframe that attaches to a running GuestServer, to display visible UI pages\n * delivered by the Extension server.\n * @public\n */\nexport const GuestUIFrame = ({\n guestId,\n src = \"\",\n onConnect,\n onDisconnect,\n onConnectionError,\n onResize,\n methods,\n privateMethods,\n sandbox = \"\",\n style,\n ...customIFrameProps\n}: GuestUIProps) => {\n const ref = useRef<HTMLIFrameElement>();\n const { host } = useHost();\n if (!host) {\n return null;\n }\n const guest = host.guests.get(guestId);\n const frameUrl = new URL(src, guest.url.href);\n\n useEffect(() => {\n if (ref.current) {\n let mounted = true;\n let connection: CrossRealmObject<VirtualApi>;\n const connectionFrame = ref.current;\n if (methods) {\n guest.provide(methods);\n }\n const connecting = guest.attachUI(connectionFrame, privateMethods);\n connecting\n .then((c) => {\n connection = c;\n if (!mounted) {\n c.tunnel.destroy();\n } else if (onConnect) {\n onConnect();\n }\n })\n .catch((error: Error) => {\n if (mounted && !connection && connectionFrame === ref.current) {\n const frameError = new Error(\n `GuestUIFrame connection failed: ${\n (error && error.stack) || error\n }`\n );\n Object.assign(frameError, {\n original: error,\n ref,\n guest,\n host,\n });\n if (onConnectionError) onConnectionError(frameError);\n }\n });\n return () => {\n mounted = false;\n if (connection) {\n connection.tunnel.destroy();\n }\n };\n }\n }, [guest.id]);\n\n useEffect(() => {\n if (ref.current && onResize) {\n const currentFrame = ref.current;\n return guest.addEventListener(\n \"guestresize\",\n ({ detail: { guestPort, iframe, dimensions } }) => {\n if (guestPort.id === guest.id && iframe === currentFrame) {\n onResize(dimensions);\n }\n }\n );\n }\n }, [ref.current, guest.id, onResize]);\n\n return (\n <iframe\n {...defaultIFrameProps}\n ref={ref}\n src={frameUrl.href}\n name={`uix-guest-${guest.id}`}\n sandbox={\n sandbox\n ? makeSandboxAttrs(defaultSandbox, sandbox).join(\" \")\n : defaultSandbox\n }\n style={\n style\n ? { ...style, ...defaultIFrameProps.style }\n : defaultIFrameProps.style\n }\n {...customIFrameProps}\n {...requiredIframeProps}\n />\n );\n};\nexport default GuestUIFrame;\n","/*\nCopyright 2022 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nimport { useContext } from \"react\";\nimport { Host } from \"@adobe/uix-host\";\nimport { ExtensionContext, ExtensibilityContext } from \"../extension-context\";\n\n/**\n * @public\n */\nexport class OutsideOfExtensionContextError extends Error {\n outsideOfExtensionContext: boolean;\n constructor(msg: string) {\n super(msg);\n this.outsideOfExtensionContext = true;\n Object.setPrototypeOf(this, OutsideOfExtensionContextError.prototype);\n }\n}\n\n/** @public */\ntype UseHostResponse =\n | { host: undefined; error: Error }\n | { host: Host; error: undefined };\n\n/**\n * Retrieve the {@link @adobe/uix-host#Host} object hosting all extensions inside the current parent provider.\n *\n * @remarks Returns a `{ host, error }` tuple, not the host object directly.\n * @beta\n */\nexport function useHost(): UseHostResponse {\n const extensionsInfo = useContext<ExtensibilityContext>(ExtensionContext);\n\n if (!(extensionsInfo.host instanceof Host)) {\n const error = new OutsideOfExtensionContextError(\n \"Attempt to use extensions outside of ExtensionContext. Wrap extensible part of application with Extensible component.\"\n );\n return {\n host: undefined,\n error,\n };\n }\n return { error: undefined, host: extensionsInfo.host };\n}\n","/*\nCopyright 2022 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\n/**\n * @hidden\n */\nimport React, { createContext } from \"react\";\nimport type { PropsWithChildren } from \"react\";\nimport { ExtensionRegistryEndpointRegistration } from \"@adobe/uix-host\";\n\n/**\n * @internal\n */\nexport const ExtensibleComponentBoundaryContext = createContext<\n ExtensionRegistryEndpointRegistration[]\n>(null as ExtensionRegistryEndpointRegistration[]);\n\n/** @public */\nexport type ExtensibleComponentProps = PropsWithChildren<{\n extensionPoints: ExtensionRegistryEndpointRegistration[];\n}>;\n\n/**\n * Wrapper that adds an extension point context to subcomponent tree.\n *\n * @public\n */\nexport const ExtensibleComponentBoundary = ({\n extensionPoints,\n children,\n}: ExtensibleComponentProps) => {\n return (\n <ExtensibleComponentBoundaryContext.Provider value={extensionPoints}>\n {children}\n </ExtensibleComponentBoundaryContext.Provider>\n );\n};\n","/*\n * Copyright 2023 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\nimport type { PropsWithChildren, ReactElement } from \"react\";\nimport React, { useMemo } from \"react\";\nimport { Extensible } from \"../Extensible\";\nimport {\n combineExtensionsFromProviders,\n InstalledExtensions,\n mutedProvider,\n type ExtensionsProvider,\n type HostConfig,\n type PortOptions,\n type SharedContextValues,\n} from \"@adobe/uix-host\";\nimport {\n ExtensionPointId,\n ExtensionProviderConfig,\n createExtensionManagerExtensionsProvider,\n} from \"./ExtensionManagerProvider\";\nimport { createUrlExtensionsProvider } from \"./UrlExtensionProvider\";\n\n/** @public */\nexport interface ExtensibleDefaultProps extends Omit<HostConfig, \"hostName\"> {\n /**\n * Unique name for identifying this extensible app. May be used as metadata in\n * extension registry in the future.\n */\n appName?: string;\n /**\n * {@inheritDoc HostConfig.guestOptions}\n */\n guestOptions?: PortOptions;\n /**\n * {@inheritDoc HostConfig.sharedContext}\n */\n sharedContext?: SharedContextValues;\n queryString?: string;\n service: string;\n extensionPoint: string;\n version: string;\n disableExtensionManager?: boolean;\n authConfig: {\n imsToken: string;\n imsOrg: string;\n apiKey: string;\n };\n scope?: Record<string, any>;\n experienceShellEnvironment?: \"prod\" | \"stage\";\n extensionsListCallback?: (\n extensions: InstalledExtensions\n ) => InstalledExtensions;\n}\n\nexport const ExtensibleWrapper = ({\n appName,\n children,\n guestOptions,\n runtimeContainer,\n debug,\n sharedContext,\n experienceShellEnvironment,\n queryString,\n service,\n extensionPoint,\n version,\n disableExtensionManager,\n authConfig,\n scope,\n extensionsListCallback,\n}: PropsWithChildren<ExtensibleDefaultProps>): ReactElement => {\n const defaultExtensionsProvider = useMemo(() => {\n const extensionPointId: ExtensionPointId = {\n service,\n name: extensionPoint,\n version,\n };\n const params: URLSearchParams = new URLSearchParams(queryString ?? \"\");\n const extensionManagerUrlOverride = params.get(\"em\");\n const appRegistryUrlOverride = params.get(\"er\");\n const urlExtensionsProvider: ExtensionsProvider =\n createUrlExtensionsProvider(extensionPointId, queryString);\n const providerConfig: ExtensionProviderConfig = {\n disableExtensionManager,\n };\n\n if (extensionManagerUrlOverride) {\n providerConfig.extensionManagerUrl = extensionManagerUrlOverride;\n }\n\n if (appRegistryUrlOverride) {\n providerConfig.appRegistryUrl = appRegistryUrlOverride;\n }\n\n const extensionManagerExtensionsProvider: ExtensionsProvider =\n createExtensionManagerExtensionsProvider(\n {\n experienceShellEnvironment,\n scope,\n },\n authConfig,\n providerConfig,\n extensionPointId\n );\n\n const extenstions = combineExtensionsFromProviders(\n urlExtensionsProvider,\n mutedProvider(extensionManagerExtensionsProvider)\n );\n\n return extenstions;\n }, [\n experienceShellEnvironment,\n queryString,\n service,\n extensionPoint,\n version,\n disableExtensionManager,\n authConfig,\n scope,\n ]);\n return (\n <Extensible\n extensionsProvider={defaultExtensionsProvider}\n appName={appName}\n runtimeContainer={runtimeContainer}\n debug={debug}\n guestOptions={guestOptions}\n sharedContext={sharedContext}\n extensionsListCallback={extensionsListCallback}\n >\n {children}\n </Extensible>\n );\n};\n\nexport default ExtensibleWrapper;\n","/*************************************************************************\n * ADOBE CONFIDENTIAL\n * ___________________\n *\n * Copyright 2024 Adobe\n * All Rights Reserved.\n *\n * NOTICE: All information contained herein is, and remains\n * the property of Adobe and its suppliers, if any. The intellectual\n * and technical concepts contained herein are proprietary to Adobe\n * and its suppliers and are protected by all applicable intellectual\n * property laws, including trade secret and copyright laws.\n * Dissemination of this information or reproduction of this material\n * is strictly forbidden unless prior written permission is obtained\n * from Adobe.\n **************************************************************************/\n\nimport {\n createExtensionRegistryAsObjectsProvider,\n ExtensionsProvider,\n InstalledExtensions,\n} from \"@adobe/uix-host\";\n\nconst EXTENSION_MANAGER_URL_PROD = \"https://aemx-mngr.adobe.io\";\nconst EXTENSION_MANAGER_URL_STAGE = \"https://aemx-mngr-stage.adobe.io\";\n\nconst APP_REGISTRY_URL_PROD = \"https://appregistry.adobe.io\";\nconst APP_REGISTRY_URL_STAGE = \"https://appregistry-stage.adobe.io\";\n\n// Extension Manager stores information about extension points that a particular extension implements\n// in the \"extensionPoints\" array of objects of the following \"ExtensionPoint\" type\n// where \"extensionPoint\" is the name of the extension point, for example, \"aem/assets/details/1\"\n// \"url\" is the extension url for the specified extension point\ntype ExtensionPoint = {\n extensionPoint: string;\n url: string;\n};\nexport type ExtensionManagerExtension = {\n id: string;\n name: string;\n title: string;\n description: string;\n status: string;\n supportEmail: string;\n extId: string;\n disabled: boolean;\n extensionPoints: ExtensionPoint[];\n scope: Record<string, unknown>;\n configuration?: Record<string, unknown>;\n};\n\ntype AuthEMConfig = {\n schema: \"Bearer\" | \"Basic\";\n imsToken: string;\n};\nexport interface ExtensionManagerConfig {\n apiKey: string;\n auth: AuthEMConfig;\n service: string;\n extensionPoint: string;\n version: string;\n imsOrg: string;\n baseUrl: string;\n scope?: Record<string, string>;\n}\n\n/** Authentication configuration, including IMS Org ID, access token, and API key */\nexport interface AuthConfig {\n /** IMS Org ID */\n imsOrg: string;\n /** Access token for the user */\n imsToken: string;\n /** API key */\n apiKey: string;\n}\n\n/** Discovery configuration, including environment and repo Id */\nexport interface DiscoveryConfig {\n /** Environment level for backend Extension resolution services */\n experienceShellEnvironment?: \"prod\" | \"stage\";\n scope?: Record<string, string>;\n}\n\n/** Extension point ID */\nexport interface ExtensionPointId {\n /** Service name */\n service: string;\n /** Extension point name */\n name: string;\n /** Extension point version */\n version: string;\n}\n\n/**\n * Sets up new ExtensionsProvider with authentication and discovery information needed to fetch the list of\n * Extensions from AppRegistry and Extension Manager service, along with the query string portion of URL\n * to extract the information about development Extensions\n */\nexport interface ExtensionsProviderConfig {\n /** Discovery configuration */\n discoveryConfig: DiscoveryConfig;\n /** Authentication configuration */\n authConfig: AuthConfig;\n /** Extension point ID */\n extensionPointId: ExtensionPointId;\n providerConfig: ExtensionProviderConfig;\n}\n\nexport interface ExtensionProviderConfig {\n extensionManagerUrl?: string;\n appRegistryUrl?: string;\n disableExtensionManager?: boolean;\n}\nexport const getExtensionRegistryBaseUrl = (\n environment: \"prod\" | \"stage\" | undefined,\n registry: string | null\n): string =>\n environment === \"prod\"\n ? APP_REGISTRY_URL_PROD\n : registry ?? APP_REGISTRY_URL_STAGE;\n\nexport const getExtensionManagerBaseUrl = (\n environment: \"prod\" | \"stage\" | undefined,\n extensionManager: string | null\n): string =>\n environment === \"prod\"\n ? EXTENSION_MANAGER_URL_PROD\n : extensionManager ?? EXTENSION_MANAGER_URL_STAGE;\n\n/**\n * Extracts programId and envId from the repo value\n * @param repo - the repo value\n * @returns object with programId and envId\n * @ignore\n */\nexport function extractProgramIdEnvId(repo: string): {\n programId: string;\n envId: string;\n} {\n const regex: RegExp = /p(\\d+)-e(\\d+)/;\n const match: RegExpMatchArray | null = regex.exec(repo);\n if (!match) {\n throw new Error(\"Error parsing a repo value\");\n }\n\n return {\n programId: match[1],\n envId: match[2],\n };\n}\n\n/**\n * Builds the URL for fetching extensions from the Extension Manager service\n * @param config - the Extension Manager configuration\n * @returns the URL for fetching extensions\n * @ignore\n */\nexport function buildExtensionManagerUrl(\n config: ExtensionManagerConfig\n): string {\n const scope = config.scope\n ? Object.fromEntries(\n Object.entries(config.scope).map(([k, v]) => [`scope.${k}`, v])\n )\n : {};\n const extensionPoints: string = `${config.service}/${config.extensionPoint}/${config.version}`;\n const queryParams = new URLSearchParams({\n ...scope,\n extensionPoints,\n });\n\n return `${config.baseUrl}/v2/extensions?${queryParams.toString()}`;\n}\n\n/**\n * @ignore\n */\nexport async function fetchExtensionsFromExtensionManager(\n config: ExtensionManagerConfig\n): Promise<ExtensionManagerExtension[]> {\n const resp: Response = await fetch(buildExtensionManagerUrl(config), {\n headers: {\n Authorization: `Bearer ${config.auth.imsToken}`,\n \"x-api-key\": config.apiKey,\n \"x-org-id\": config.imsOrg,\n },\n });\n\n if (resp.status !== 200) {\n throw new Error(\n `Extension Manager returned non-200 response (${\n resp.status\n }): ${await resp.text()}`\n );\n }\n\n return resp.json();\n}\n\n/**\n * Takes an array of extensions from the App Registry, an array of extensions from the Extension Manager, and\n * merges them into a list of Extensions. If an extension is disabled in the Extension Manager, it is removed from\n * the list.\n * Extension list from the App Registry is used as a base.\n * @ignore\n */\nexport function mergeExtensions(\n appRegistryExtensions: InstalledExtensions,\n extensionManagerExtensions: ExtensionManagerExtension[],\n extensionPointId: ExtensionPointId\n): InstalledExtensions {\n const mergedExtensions: InstalledExtensions = Object.assign(\n appRegistryExtensions,\n {}\n );\n extensionManagerExtensions.forEach((extension: ExtensionManagerExtension) => {\n if (extension.disabled) {\n // remove disabled extensions\n delete mergedExtensions[extension.name];\n } else {\n const extPoint: ExtensionPoint | undefined =\n extension.extensionPoints.find(\n (_extensionPoint: ExtensionPoint) =>\n _extensionPoint.extensionPoint ===\n `${extensionPointId.service}/${extensionPointId.name}/${extensionPointId.version}`\n );\n if (extPoint) {\n // add a new extension record or replace the existing one by an extension record from Extension Manager\n // extension points are useful for filtering out extensions\n mergedExtensions[extension.name] = {\n id: extension.name,\n url: extPoint.url,\n configuration: extension.configuration,\n extensionPoints: extension.extensionPoints.map(\n (point) => point.extensionPoint\n ),\n };\n } else {\n //this should never happen because we query Extension Manager service for our specific extension point\n console.warn(\n `Extension point ${extensionPointId.service}/${extensionPointId.name}/${extensionPointId.version} not found for extension ${extension.name}`\n );\n }\n }\n });\n\n return mergedExtensions;\n}\n\nasync function getExtensionManagerExtensions(\n discoveryConfig: DiscoveryConfig,\n authConfig: AuthConfig,\n providerConfig: ExtensionProviderConfig,\n extensionPointId: ExtensionPointId\n): Promise<InstalledExtensions> {\n const config = {\n apiKey: authConfig.apiKey,\n auth: {\n schema: \"Bearer\",\n imsToken: authConfig.imsToken,\n },\n service: extensionPointId.service,\n extensionPoint: extensionPointId.name,\n version: extensionPointId.version,\n imsOrg: authConfig.imsOrg,\n scope: discoveryConfig.scope,\n };\n\n const appRegistryConfig = {\n ...config,\n baseUrl: getExtensionRegistryBaseUrl(\n discoveryConfig.experienceShellEnvironment,\n providerConfig.appRegistryUrl\n ),\n } as ExtensionManagerConfig;\n const appRegistryExtensionsProvider: ExtensionsProvider =\n createExtensionRegistryAsObjectsProvider(appRegistryConfig);\n\n const extensionManagerConfiguration = {\n ...config,\n baseUrl: getExtensionManagerBaseUrl(\n discoveryConfig.experienceShellEnvironment,\n providerConfig.extensionManagerUrl\n ),\n } as ExtensionManagerConfig;\n const [appRegistryExtensions, extensionManagerExtensions] = await Promise.all(\n [\n appRegistryExtensionsProvider(),\n providerConfig.disableExtensionManager\n ? []\n : fetchExtensionsFromExtensionManager(extensionManagerConfiguration),\n ]\n );\n\n if (providerConfig.disableExtensionManager) {\n return appRegistryExtensions;\n } else {\n return mergeExtensions(\n appRegistryExtensions,\n extensionManagerExtensions,\n extensionPointId\n );\n }\n}\n\n/**\n * Creates an extension manager extension provider\n * @ignore\n */\nexport function createExtensionManagerExtensionsProvider(\n discoveryConfig: DiscoveryConfig,\n authConfig: AuthConfig,\n providerConfig: ExtensionProviderConfig,\n extensionPointId: ExtensionPointId\n): ExtensionsProvider {\n return () => {\n return getExtensionManagerExtensions(\n discoveryConfig,\n authConfig,\n providerConfig,\n extensionPointId\n );\n };\n}\n","/*************************************************************************\n * ADOBE CONFIDENTIAL\n * ___________________\n *\n * Copyright 2024 Adobe\n * All Rights Reserved.\n *\n * NOTICE: All information contained herein is, and remains\n * the property of Adobe and its suppliers, if any. The intellectual\n * and technical concepts contained herein are proprietary to Adobe\n * and its suppliers and are protected by all applicable intellectual\n * property laws, including trade secret and copyright laws.\n * Dissemination of this information or reproduction of this material\n * is strictly forbidden unless prior written permission is obtained\n * from Adobe.\n **************************************************************************/\nimport { ExtensionsProvider, InstalledExtensions } from \"@adobe/uix-host\";\nimport { Extension } from \"@adobe/uix-core\";\nimport { ExtensionPointId } from \"./ExtensionManagerProvider\";\nconst EXT_PARAM_PREFIX = \"ext\";\n\nexport interface ExtUrlParams {\n [key: string]: string;\n}\n\n/**\n * Validates if a URL is safe and only allows HTTP/HTTPS protocols\n * @param url - The URL string to validate\n * @returns true if the URL is valid and uses HTTP/HTTPS protocol, false otherwise\n */\nexport function isValidHttpUrl(url: string): boolean {\n try {\n const parsedUrl = new URL(url);\n\n return parsedUrl.protocol === \"http:\" || parsedUrl.protocol === \"https:\";\n } catch {\n return false;\n }\n}\n\n/**\n * Extracts extension URLs from the query string\n * @ignore\n */\nexport function extractExtUrlParams(\n queryString: string | undefined\n): ExtUrlParams {\n if (!queryString) {\n return {};\n }\n const params: URLSearchParams = new URLSearchParams(queryString);\n return Array.from(params.entries()).reduce((extParams, [key, value]) => {\n if (key === EXT_PARAM_PREFIX || key.startsWith(`${EXT_PARAM_PREFIX}.`)) {\n extParams[key] = value;\n }\n return extParams;\n }, {} as ExtUrlParams);\n}\n\n/**\n * Generates an extension ID from the extension URL\n * @ignore\n */\nexport function generateExtensionId(extensionUrl: string): string {\n return extensionUrl.replace(/\\W/g, \"_\");\n}\n\n/**\n * Creates an ExtensionsProvider that provides extensions from the URL\n * @ignore\n */\nexport function createUrlExtensionsProvider(\n extensionPointId: ExtensionPointId,\n queryString: string | undefined\n): ExtensionsProvider {\n const extUrlParams: ExtUrlParams = extractExtUrlParams(queryString);\n\n const extensionUrls: string[] = Object.keys(extUrlParams)\n .filter(\n (extParam) =>\n extParam === EXT_PARAM_PREFIX ||\n extParam ===\n `${EXT_PARAM_PREFIX}.${extensionPointId.service}/${extensionPointId.name}/${extensionPointId.version}`\n )\n .flatMap((extParam) => {\n const paramValue = extUrlParams[extParam];\n // If it's a single value, return it in an array. If it's already an array, return it as is.\n if (Array.isArray(paramValue)) {\n return paramValue.filter((param) => isValidHttpUrl(param));\n } else {\n return isValidHttpUrl(paramValue) ? [paramValue] : [];\n }\n });\n\n const installedExtensions: InstalledExtensions = extensionUrls\n .map((extensionUrl: string) => {\n return {\n id: generateExtensionId(extensionUrl),\n url: extensionUrl,\n extensionPoints: [\n `${extensionPointId.service}/${extensionPointId.name}/${extensionPointId.version}`,\n ],\n } as Extension;\n })\n .reduce((acc: InstalledExtensions, extension: Extension) => {\n acc[extension.id] = extension;\n return acc;\n }, {} as InstalledExtensions);\n\n return async () => installedExtensions;\n}\n","/*\nCopyright 2022 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nimport { useCallback, useContext, useEffect, useMemo, useState } from \"react\";\nimport type {\n GuestConnection,\n GuestApis,\n RemoteGuestApis,\n RemoteGuestApiNS,\n VirtualApi,\n} from \"@adobe/uix-core\";\n\nimport { Host, HostEvents } from \"@adobe/uix-host\";\nimport type { CapabilitySpec, Port } from \"@adobe/uix-host\";\nimport { useHost } from \"./useHost\";\nimport { ExtensibleComponentBoundaryContext } from \"../components/ExtensibleComponentBoundary\";\nimport { ExtensionRegistryEndpointRegistration } from \"@adobe/uix-host\";\n\n/**\n * @internal\n */\nexport interface TypedGuestConnection<T extends GuestApis>\n extends GuestConnection {\n id: GuestConnection[\"id\"];\n apis: RemoteGuestApis<T>;\n}\n\n/** @public */\nexport interface UseExtensionsConfig<\n Incoming extends GuestApis,\n Outgoing extends VirtualApi\n> {\n /**\n * A {@link @adobe/uix-host#CapabilitySpec} describing the namespaced methods\n * extensions must implement to be used by this component.\n *\n * @remarks\n * This declaration is used to filter the extensions that will be\n * returned; if they don't implement those methods, they will be filtered out.\n */\n requires?: CapabilitySpec<Required<Incoming>>;\n /**\n * A namespaced object of methods which extensions will be able to call.\n *\n * @remarks This is the counterpart of `requires`; in `requires`, the you\n * describes methods the extension must implement that your host code will\n * call, and in `provides`, you implement host methods that extensions will be\n * able to call.\n *\n * Most cases for host-side methods will use the state of the component. This\n * can cause unexpected bugs in React if the config callback is run on every\n * render. **useExtensions caches the config callback by default!**\n * So remember to pass a deps array, so that the config callback re-runs under\n * the right conditions.\n */\n provides?: Outgoing;\n /**\n * Sets when re-render is triggered on extension load.\n *\n * @remarks\n * Set to `each` to trigger a component re-render every time an individual\n * extension loads, which may result in multiple UI updates. Set to `all` to\n * wait until all extensions have loaded to re-render the component.\n * @defaultValue \"each\"\n */\n updateOn?: \"each\" | \"all\";\n}\n\n/** @public */\nexport interface UseExtensionsResult<T extends GuestApis> {\n /**\n * A list of loaded guests which implement the methods specified in\n * `requires`, represented as {@link @adobe/uix-host#Port} objects which\n * present methods to be called.\n */\n extensions: TypedGuestConnection<T>[];\n /**\n * This is `true` until all extensions are loaded. Use for rendering spinners\n * or other intermediate UI.\n */\n loading: boolean;\n /**\n * Populated with an Error if there were any problems during the load process.\n */\n error?: Error;\n}\n\nconst NO_EXTENSIONS: [] = [];\n\n/**\n * Fetch extensions which implement an API, provide them methods, and use them.\n *\n * @remarks `useExtensions` does three things at once:\n * - Gets all extensions which implement the APIs described in the `require` field\n * - Exposes any functions defined in the `provide` field to those extensions\n * - Returns an object whose `extensions` property is a list of `Port` objects representing those extensions\n *\n * useExtensions will trigger a re-render when extensions load. You can choose whether it triggers that rerender as each extension loads, or only after all extensions have loaded.\n * @public\n */\nexport function useExtensions<\n Incoming extends GuestApis,\n Outgoing extends VirtualApi\n>(\n configFactory: (host: Host) => UseExtensionsConfig<Incoming, Outgoing>,\n deps: unknown[] = []\n): UseExtensionsResult<Incoming> {\n const { host, error } = useHost();\n if (error) {\n return {\n extensions: NO_EXTENSIONS,\n loading: false,\n error,\n };\n }\n const [hostError, setHostError] = useState<Error>();\n const extensionPoints = useContext(ExtensibleComponentBoundaryContext);\n const boundryExtensionPointsAsString = extensionPoints?.map(\n ({\n service,\n extensionPoint,\n version,\n }: ExtensionRegistryEndpointRegistration) =>\n `${service}/${extensionPoint}/${version}`\n );\n const baseDeps = [host, ...deps];\n const {\n requires,\n provides,\n updateOn = \"each\",\n } = useMemo(() => configFactory(host), baseDeps);\n\n const getExtensions = useCallback(() => {\n const newExtensions = [];\n const guests = host.getLoadedGuests(requires);\n\n // Extension filtering: If the boundary is provided, only return extensions which have extensionPoint\n // specified in a provided boundry. Otherwise no filtering is done.\n for (const guest of guests) {\n const allExtensionPoints: string[] =\n getAllExtensionPointsFromGuest(guest);\n\n if (\n !boundryExtensionPointsAsString ||\n !allExtensionPoints.length ||\n isGuestExtensionPointInBoundary(\n boundryExtensionPointsAsString,\n allExtensionPoints\n )\n ) {\n newExtensions.push(guest as unknown as TypedGuestConnection<Incoming>);\n }\n }\n return newExtensions.length === 0 ? NO_EXTENSIONS : newExtensions;\n }, [...baseDeps, requires]);\n\n const subscribe = useCallback(\n (handler: EventListener) => {\n const eventName = updateOn === \"all\" ? \"loadallguests\" : \"guestload\";\n host.addEventListener(eventName, handler);\n\n return () => {\n host.removeEventListener(eventName, handler);\n };\n },\n [...baseDeps, updateOn]\n );\n\n const subscribeToUnload = useCallback((handler: EventListener) => {\n host.addEventListener(\"guestunload\", handler);\n\n return () => {\n host.removeEventListener(\"guestunload\", handler);\n };\n }, baseDeps);\n\n const [extensions, setExtensions] = useState(() => getExtensions());\n\n useEffect(() => {\n return subscribe(() => setExtensions(getExtensions()));\n }, [subscribe]);\n\n const unloadExtentionCallback = (e: CustomEvent) => {\n const eventDetail = e.detail;\n const guest = eventDetail.guest as Port<GuestApis>;\n\n if (guest && guest.id) {\n setExtensions((prevExtensions) => {\n const filtered = prevExtensions.filter(\n (ext) => ext.id !== guest.id || ext.url !== guest.url\n );\n return filtered.length === 0 ? NO_EXTENSIONS : filtered;\n });\n }\n };\n\n useEffect(() => {\n return subscribeToUnload(unloadExtentionCallback);\n }, [subscribeToUnload]);\n\n useEffect(() => {\n for (const guest of extensions) {\n if (provides) {\n guest.provide(provides);\n }\n }\n }, [provides, extensions]);\n\n useEffect(\n () =>\n host.addEventListener(\n \"error\",\n (event: Extract<HostEvents, { detail: { error: Error } }>) =>\n setHostError(event.detail.error)\n ),\n baseDeps\n );\n\n return {\n extensions,\n loading: extensions.length === 0 ? false : host.loading,\n error: hostError,\n };\n}\n\n/**\n * Each extension/guest can have\n * 1. `extensioPoints` field as an array of strings\n * 2. Metadata with array of extensionPoints. If the metadata is present, we need to use it for fitering the extensions.\n * Returns cumulative extension points.\n * @param guest\n * @returns array of extension points as strings\n */\nfunction getAllExtensionPointsFromGuest(guest: Port<GuestApis>): string[] {\n try {\n const guestExtensionPointsFromMetadata = guest.metadata?.extensions?.map(\n (extension: { extensionPoint: string }) => extension?.extensionPoint\n );\n const allExtensionPoints = [\n ...(guest.extensionPoints || []),\n ...(guestExtensionPointsFromMetadata || []),\n ];\n return allExtensionPoints;\n } catch {\n console.error(\n \"Error occurred while getting extension points from guest and metadata. Extension boundaries will not be effective.\"\n );\n return [];\n }\n}\n\nfunction isGuestExtensionPointInBoundary(\n boundryExtensionPointsAsString: string[],\n guestExtensionPoints: string[]\n) {\n return (\n boundryExtensionPointsAsString?.length &&\n guestExtensionPoints?.length &&\n guestExtensionPoints.some((extensionPoint) =>\n boundryExtensionPointsAsString.includes(extensionPoint)\n )\n );\n}\n","/*\nCopyright 2022 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nimport { useContext } from \"react\";\nimport {\n ExtensionContext,\n ExtensibilityContext,\n} from \"../extension-context.js\";\n\n/**\n * Indicates if external extensions provider was processed. Returns true after passing extension list to the Host.\n *\n * @beta\n */\nexport function useExtensionListFetched(): boolean {\n const extensionsInfo = useContext<ExtensibilityContext>(ExtensionContext);\n\n return extensionsInfo.extensionListFetched;\n}\n"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/components/Extensible.tsx","../src/extension-context.ts","../src/components/GuestUIFrame.tsx","../src/hooks/useHost.ts","../src/components/ExtensibleComponentBoundary.tsx","../src/components/ExtensibleWrapper/ExtensibleWrapper.tsx","../src/components/ExtensibleWrapper/ExtensionManagerProvider.ts","../src/components/ExtensibleWrapper/UrlExtensionProvider.ts","../src/hooks/useExtensions.ts","../src/hooks/useExtensionListFetched.ts"],"names":["React","useEffect","useRef","Host","createContext","useContext","useMemo","useState"],"mappings":"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","sourcesContent":["/*\nCopyright 2022 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nexport * from \"@adobe/uix-host\";\nexport * from \"./components/index.js\";\nexport * from \"./hooks/index.js\";\nexport * from \"./extension-context\";\n","/*\nCopyright 2022 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nimport React, { useState, useEffect, useRef } from \"react\";\nimport type { PropsWithChildren } from \"react\";\nimport type {\n InstalledExtensions,\n ExtensionsProvider,\n HostConfig,\n PortOptions,\n SharedContextValues,\n} from \"@adobe/uix-host\";\nimport { Host } from \"@adobe/uix-host\";\nimport { ExtensionContext } from \"../extension-context.js\";\n\n/** @public */\nexport interface ExtensibleProps extends Omit<HostConfig, \"hostName\"> {\n /**\n * Unique name for identifying this extensible app. May be used as metadata in\n * extension registry in the future.\n */\n appName?: string;\n /**\n * Function which returns a promise for the full list of extensions.\n */\n extensionsProvider: ExtensionsProvider;\n /**\n * {@inheritDoc HostConfig.guestOptions}\n */\n guestOptions?: PortOptions;\n /**\n * {@inheritDoc HostConfig.sharedContext}\n */\n sharedContext?: SharedContextValues;\n extensionsListCallback?: (\n extensions: InstalledExtensions\n ) => InstalledExtensions;\n}\n\nfunction areExtensionsDifferent(\n set1: InstalledExtensions,\n set2: InstalledExtensions\n) {\n const ids1 = Object.keys(set1).sort();\n const ids2 = Object.keys(set2).sort();\n\n if (ids1.length !== ids2.length) {\n return true;\n }\n\n let isDifferent = false;\n\n ids1.forEach((id) => {\n if (isDifferent) {\n return;\n }\n\n const ext1 = set1[id];\n const ext2 = set2[id];\n\n if (typeof ext1 !== typeof ext2) {\n isDifferent = true;\n return;\n }\n\n if (typeof ext1 === \"string\" && typeof ext2 === \"string\") {\n if (ext1 !== ext2) {\n isDifferent = true;\n return;\n }\n }\n\n if (typeof ext1 === \"object\" && typeof ext2 === \"object\") {\n if (\n ext1.url !== ext2.url ||\n JSON.stringify(ext1.extensionPoints) !==\n JSON.stringify(ext2.extensionPoints) ||\n JSON.stringify(ext1.configuration) !==\n JSON.stringify(ext2.configuration)\n ) {\n isDifferent = true;\n return;\n }\n }\n });\n\n return isDifferent;\n}\n\n/**\n * Declares an extensible area in an app, and provides host and extension\n * objects to all descendents. The {@link useExtensions} hook can only be called\n * in a descendent of this component.\n *\n * @remarks\n * For many apps, there will be only one Extensible provider, fairly high in the\n * component tree. It is a context provider component that may be bundled with\n * other context providers in your app architecture.\n *\n * Each Extensible element creates one {@link @adobe/uix-host#Host} object and\n * one call to an extensions provider. If multiple Extensible elements are used,\n * this may cause redundant calls. Such a design should be carefully considered.\n *\n * @public\n */\nexport function Extensible({\n appName,\n children,\n extensionsProvider,\n guestOptions,\n runtimeContainer,\n debug,\n sharedContext,\n extensionsListCallback,\n}: PropsWithChildren<ExtensibleProps>) {\n const hostName = appName || window.location.host || \"mainframe\";\n\n const [extensions, setExtensions] = useState<InstalledExtensions>({});\n const [extensionListFetched, setExtensionListFetched] =\n useState<boolean>(false);\n const prevSharedContext = useRef(JSON.stringify(sharedContext));\n useEffect(() => {\n let cancelled = false;\n\n extensionsProvider()\n .then((loaded: InstalledExtensions) => {\n if (cancelled) {\n return;\n }\n setExtensions((prev) => {\n let newExtensions = loaded;\n\n if (extensionsListCallback && loaded) {\n newExtensions = extensionsListCallback(newExtensions);\n }\n\n const shouldUpdate = areExtensionsDifferent(prev, newExtensions);\n\n if (shouldUpdate) {\n return newExtensions;\n } else {\n return prev;\n }\n });\n })\n .catch((e: Error | unknown) => {\n if (!cancelled) {\n console.error(\"Fetching list of extensions failed!\", e);\n }\n })\n .finally(() => {\n if (!cancelled) {\n setExtensionListFetched(true);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [extensionsProvider, extensionsListCallback]);\n\n const [host, setHost] = useState<Host>();\n const hostRef = useRef<Host>();\n\n useEffect(() => {\n return () => {\n if (hostRef.current) {\n hostRef.current.unload().catch(() => {});\n hostRef.current = undefined;\n }\n };\n }, []);\n\n useEffect(() => {\n function logError(msg: string) {\n return (e: Error | unknown) => {\n const error = e instanceof Error ? e : new Error(String(e));\n console.error(msg, error, extensions, guestOptions);\n };\n }\n\n if (!extensions || !Object.keys(extensions).length) {\n return;\n }\n\n const loadExtensions = (hostInstance: Host) => {\n hostInstance\n .load(extensions, guestOptions)\n .catch(logError(\"Load of extensions failed!\"));\n };\n\n const sharedContextChanged =\n prevSharedContext.current !== JSON.stringify(sharedContext);\n\n if (sharedContextChanged) {\n prevSharedContext.current = JSON.stringify(sharedContext);\n }\n if (!host || sharedContextChanged) {\n if (hostRef.current) {\n hostRef.current.unload().catch(() => {});\n }\n const newHost = new Host({\n debug,\n hostName,\n runtimeContainer,\n sharedContext,\n });\n hostRef.current = newHost;\n setHost(newHost);\n loadExtensions(newHost);\n } else {\n loadExtensions(host);\n }\n }, [debug, hostName, runtimeContainer, extensions]);\n\n // skip render before host is initialized\n if (!host) {\n return <>{children}</>;\n }\n\n return (\n <ExtensionContext.Provider\n value={{\n host: host,\n extensionListFetched: extensionListFetched,\n }}\n >\n {children}\n </ExtensionContext.Provider>\n );\n}\nexport default Extensible;\n","/*\nCopyright 2022 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\n/**\n * @hidden\n */\nimport { createContext } from \"react\";\nimport { Host } from \"@adobe/uix-host\";\n\n/**\n * Context container with Host object and extensions load status.\n *\n * @internal\n */\nexport type ExtensibilityContext = {\n host: Host;\n extensionListFetched: boolean;\n};\n\n/**\n * @internal\n */\nexport const ExtensionContext = createContext<ExtensibilityContext>(\n {} as unknown as ExtensibilityContext\n);\n","/*\nCopyright 2022 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nimport { CrossRealmObject, UIFrameRect, VirtualApi } from \"@adobe/uix-core\";\nimport React, { useEffect, useRef } from \"react\";\nimport type { IframeHTMLAttributes } from \"react\";\nimport { useHost } from \"../hooks/useHost.js\";\nimport type { AttrTokens, SandboxToken } from \"@adobe/uix-host\";\nimport { makeSandboxAttrs, requiredIframeProps } from \"@adobe/uix-host\";\n\n/**\n * @internal\n */\ntype ReactIframeProps = IframeHTMLAttributes<HTMLIFrameElement>;\n\n/**\n * @public\n */\ntype FrameProps = Omit<ReactIframeProps, \"sandbox\">;\n\n/** @public */\nexport interface GuestUIProps extends FrameProps {\n guestId: string;\n /**\n * Receives the Penpal context when the frame is mounted.\n */\n onConnect?: () => void;\n /**\n * Called when the frame disconnects and unmounts.\n */\n onDisconnect?: () => void;\n /**\n * Called when the connection process throws an exception\n */\n onConnectionError?: (error: Error) => void;\n /**\n * Called when the document in the iframe changes size.\n */\n onResize?: (dimensions: UIFrameRect) => void;\n /**\n * Additional sandbox attributes GuestUIFrame might need.\n */\n sandbox?: AttrTokens<SandboxToken>;\n /**\n * Optional custom URL or path.\n */\n src: string;\n /**\n * Host methods to provide only to the guest inside all iframes.\n */\n methods?: VirtualApi;\n /**\n * Host methods to provide only to the guest inside this iframe.\n */\n privateMethods?: VirtualApi;\n}\n\nconst defaultIFrameProps: FrameProps = {\n width: \"100%\",\n height: \"100%\",\n style: {\n display: \"block\",\n border: \"none\",\n },\n};\n\nconst defaultSandbox = \"allow-scripts allow-forms allow-same-origin\";\n\n/**\n * An iframe that attaches to a running GuestServer, to display visible UI pages\n * delivered by the Extension server.\n * @public\n */\nexport const GuestUIFrame = ({\n guestId,\n src = \"\",\n onConnect,\n onDisconnect,\n onConnectionError,\n onResize,\n methods,\n privateMethods,\n sandbox = \"\",\n style,\n ...customIFrameProps\n}: GuestUIProps) => {\n const ref = useRef<HTMLIFrameElement>();\n const { host } = useHost();\n if (!host) {\n return null;\n }\n const guest = host.guests.get(guestId);\n const frameUrl = new URL(src, guest.url.href);\n\n useEffect(() => {\n if (ref.current) {\n let mounted = true;\n let connection: CrossRealmObject<VirtualApi>;\n const connectionFrame = ref.current;\n if (methods) {\n guest.provide(methods);\n }\n const connecting = guest.attachUI(connectionFrame, privateMethods);\n connecting\n .then((c) => {\n connection = c;\n if (!mounted) {\n c.tunnel.destroy();\n } else if (onConnect) {\n onConnect();\n }\n })\n .catch((error: Error) => {\n if (mounted && !connection && connectionFrame === ref.current) {\n const frameError = new Error(\n `GuestUIFrame connection failed: ${\n (error && error.stack) || error\n }`\n );\n Object.assign(frameError, {\n original: error,\n ref,\n guest,\n host,\n });\n if (onConnectionError) onConnectionError(frameError);\n }\n });\n return () => {\n mounted = false;\n if (connection) {\n connection.tunnel.destroy();\n }\n };\n }\n }, [guest.id]);\n\n useEffect(() => {\n if (ref.current && onResize) {\n const currentFrame = ref.current;\n return guest.addEventListener(\n \"guestresize\",\n ({ detail: { guestPort, iframe, dimensions } }) => {\n if (guestPort.id === guest.id && iframe === currentFrame) {\n onResize(dimensions);\n }\n }\n );\n }\n }, [ref.current, guest.id, onResize]);\n\n return (\n <iframe\n {...defaultIFrameProps}\n ref={ref}\n src={frameUrl.href}\n name={`uix-guest-${guest.id}`}\n sandbox={\n sandbox\n ? makeSandboxAttrs(defaultSandbox, sandbox).join(\" \")\n : defaultSandbox\n }\n style={\n style\n ? { ...style, ...defaultIFrameProps.style }\n : defaultIFrameProps.style\n }\n {...customIFrameProps}\n {...requiredIframeProps}\n />\n );\n};\nexport default GuestUIFrame;\n","/*\nCopyright 2022 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nimport { useContext } from \"react\";\nimport { Host } from \"@adobe/uix-host\";\nimport { ExtensionContext, ExtensibilityContext } from \"../extension-context\";\n\n/**\n * @public\n */\nexport class OutsideOfExtensionContextError extends Error {\n outsideOfExtensionContext: boolean;\n constructor(msg: string) {\n super(msg);\n this.outsideOfExtensionContext = true;\n Object.setPrototypeOf(this, OutsideOfExtensionContextError.prototype);\n }\n}\n\n/** @public */\ntype UseHostResponse =\n | { host: undefined; error: Error }\n | { host: Host; error: undefined };\n\n/**\n * Retrieve the {@link @adobe/uix-host#Host} object hosting all extensions inside the current parent provider.\n *\n * @remarks Returns a `{ host, error }` tuple, not the host object directly.\n * @beta\n */\nexport function useHost(): UseHostResponse {\n const extensionsInfo = useContext<ExtensibilityContext>(ExtensionContext);\n\n if (!(extensionsInfo.host instanceof Host)) {\n const error = new OutsideOfExtensionContextError(\n \"Attempt to use extensions outside of ExtensionContext. Wrap extensible part of application with Extensible component.\"\n );\n return {\n host: undefined,\n error,\n };\n }\n return { error: undefined, host: extensionsInfo.host };\n}\n","/*\nCopyright 2022 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\n/**\n * @hidden\n */\nimport React, { createContext } from \"react\";\nimport type { PropsWithChildren } from \"react\";\nimport { ExtensionRegistryEndpointRegistration } from \"@adobe/uix-host\";\n\n/**\n * @internal\n */\nexport const ExtensibleComponentBoundaryContext = createContext<\n ExtensionRegistryEndpointRegistration[]\n>(null as ExtensionRegistryEndpointRegistration[]);\n\n/** @public */\nexport type ExtensibleComponentProps = PropsWithChildren<{\n extensionPoints: ExtensionRegistryEndpointRegistration[];\n}>;\n\n/**\n * Wrapper that adds an extension point context to subcomponent tree.\n *\n * @public\n */\nexport const ExtensibleComponentBoundary = ({\n extensionPoints,\n children,\n}: ExtensibleComponentProps) => {\n return (\n <ExtensibleComponentBoundaryContext.Provider value={extensionPoints}>\n {children}\n </ExtensibleComponentBoundaryContext.Provider>\n );\n};\n","/*\n * Copyright 2023 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\nimport type { PropsWithChildren, ReactElement } from \"react\";\nimport React, { useMemo } from \"react\";\nimport { Extensible } from \"../Extensible\";\nimport {\n combineExtensionsFromProviders,\n InstalledExtensions,\n mutedProvider,\n type ExtensionsProvider,\n type HostConfig,\n type PortOptions,\n type SharedContextValues,\n} from \"@adobe/uix-host\";\nimport {\n ExtensionPointId,\n ExtensionProviderConfig,\n createExtensionManagerExtensionsProvider,\n} from \"./ExtensionManagerProvider\";\nimport { createUrlExtensionsProvider } from \"./UrlExtensionProvider\";\n\n/** @public */\nexport interface ExtensibleDefaultProps extends Omit<HostConfig, \"hostName\"> {\n /**\n * Unique name for identifying this extensible app. May be used as metadata in\n * extension registry in the future.\n */\n appName?: string;\n /**\n * {@inheritDoc HostConfig.guestOptions}\n */\n guestOptions?: PortOptions;\n /**\n * {@inheritDoc HostConfig.sharedContext}\n */\n sharedContext?: SharedContextValues;\n queryString?: string;\n service: string;\n extensionPoint: string;\n version: string;\n disableExtensionManager?: boolean;\n authConfig: {\n imsToken: string;\n imsOrg: string;\n apiKey: string;\n };\n scope?: Record<string, any>;\n experienceShellEnvironment?: \"prod\" | \"stage\";\n extensionsListCallback?: (\n extensions: InstalledExtensions\n ) => InstalledExtensions;\n}\n\nexport const ExtensibleWrapper = ({\n appName,\n children,\n guestOptions,\n runtimeContainer,\n debug,\n sharedContext,\n experienceShellEnvironment,\n queryString,\n service,\n extensionPoint,\n version,\n disableExtensionManager,\n authConfig,\n scope,\n extensionsListCallback,\n}: PropsWithChildren<ExtensibleDefaultProps>): ReactElement => {\n const defaultExtensionsProvider = useMemo(() => {\n const extensionPointId: ExtensionPointId = {\n service,\n name: extensionPoint,\n version,\n };\n const params: URLSearchParams = new URLSearchParams(queryString ?? \"\");\n const extensionManagerUrlOverride = params.get(\"em\");\n const appRegistryUrlOverride = params.get(\"er\");\n const urlExtensionsProvider: ExtensionsProvider =\n createUrlExtensionsProvider(extensionPointId, queryString);\n const providerConfig: ExtensionProviderConfig = {\n disableExtensionManager,\n };\n\n if (extensionManagerUrlOverride) {\n providerConfig.extensionManagerUrl = extensionManagerUrlOverride;\n }\n\n if (appRegistryUrlOverride) {\n providerConfig.appRegistryUrl = appRegistryUrlOverride;\n }\n\n const extensionManagerExtensionsProvider: ExtensionsProvider =\n createExtensionManagerExtensionsProvider(\n {\n experienceShellEnvironment,\n scope,\n },\n authConfig,\n providerConfig,\n extensionPointId\n );\n\n const extenstions = combineExtensionsFromProviders(\n urlExtensionsProvider,\n mutedProvider(extensionManagerExtensionsProvider)\n );\n\n return extenstions;\n }, [\n experienceShellEnvironment,\n queryString,\n service,\n extensionPoint,\n version,\n disableExtensionManager,\n authConfig,\n scope,\n ]);\n return (\n <Extensible\n extensionsProvider={defaultExtensionsProvider}\n appName={appName}\n runtimeContainer={runtimeContainer}\n debug={debug}\n guestOptions={guestOptions}\n sharedContext={sharedContext}\n extensionsListCallback={extensionsListCallback}\n >\n {children}\n </Extensible>\n );\n};\n\nexport default ExtensibleWrapper;\n","/*************************************************************************\n * ADOBE CONFIDENTIAL\n * ___________________\n *\n * Copyright 2024 Adobe\n * All Rights Reserved.\n *\n * NOTICE: All information contained herein is, and remains\n * the property of Adobe and its suppliers, if any. The intellectual\n * and technical concepts contained herein are proprietary to Adobe\n * and its suppliers and are protected by all applicable intellectual\n * property laws, including trade secret and copyright laws.\n * Dissemination of this information or reproduction of this material\n * is strictly forbidden unless prior written permission is obtained\n * from Adobe.\n **************************************************************************/\n\nimport {\n createExtensionRegistryAsObjectsProvider,\n ExtensionsProvider,\n InstalledExtensions,\n} from \"@adobe/uix-host\";\n\nconst EXTENSION_MANAGER_URL_PROD = \"https://aemx-mngr.adobe.io\";\nconst EXTENSION_MANAGER_URL_STAGE = \"https://aemx-mngr-stage.adobe.io\";\n\nconst APP_REGISTRY_URL_PROD = \"https://appregistry.adobe.io\";\nconst APP_REGISTRY_URL_STAGE = \"https://appregistry-stage.adobe.io\";\n\n// Extension Manager stores information about extension points that a particular extension implements\n// in the \"extensionPoints\" array of objects of the following \"ExtensionPoint\" type\n// where \"extensionPoint\" is the name of the extension point, for example, \"aem/assets/details/1\"\n// \"url\" is the extension url for the specified extension point\ntype ExtensionPoint = {\n extensionPoint: string;\n url: string;\n};\nexport type ExtensionManagerExtension = {\n id: string;\n name: string;\n title: string;\n description: string;\n status: string;\n supportEmail: string;\n extId: string;\n disabled: boolean;\n extensionPoints: ExtensionPoint[];\n scope: Record<string, unknown>;\n configuration?: Record<string, unknown>;\n};\n\ntype AuthEMConfig = {\n schema: \"Bearer\" | \"Basic\";\n imsToken: string;\n};\nexport interface ExtensionManagerConfig {\n apiKey: string;\n auth: AuthEMConfig;\n service: string;\n extensionPoint: string;\n version: string;\n imsOrg: string;\n baseUrl: string;\n scope?: Record<string, string>;\n}\n\n/** Authentication configuration, including IMS Org ID, access token, and API key */\nexport interface AuthConfig {\n /** IMS Org ID */\n imsOrg: string;\n /** Access token for the user */\n imsToken: string;\n /** API key */\n apiKey: string;\n}\n\n/** Discovery configuration, including environment and repo Id */\nexport interface DiscoveryConfig {\n /** Environment level for backend Extension resolution services */\n experienceShellEnvironment?: \"prod\" | \"stage\";\n scope?: Record<string, string>;\n}\n\n/** Extension point ID */\nexport interface ExtensionPointId {\n /** Service name */\n service: string;\n /** Extension point name */\n name: string;\n /** Extension point version */\n version: string;\n}\n\n/**\n * Sets up new ExtensionsProvider with authentication and discovery information needed to fetch the list of\n * Extensions from AppRegistry and Extension Manager service, along with the query string portion of URL\n * to extract the information about development Extensions\n */\nexport interface ExtensionsProviderConfig {\n /** Discovery configuration */\n discoveryConfig: DiscoveryConfig;\n /** Authentication configuration */\n authConfig: AuthConfig;\n /** Extension point ID */\n extensionPointId: ExtensionPointId;\n providerConfig: ExtensionProviderConfig;\n}\n\nexport interface ExtensionProviderConfig {\n extensionManagerUrl?: string;\n appRegistryUrl?: string;\n disableExtensionManager?: boolean;\n}\nexport const getExtensionRegistryBaseUrl = (\n environment: \"prod\" | \"stage\" | undefined,\n registry: string | null\n): string =>\n environment === \"prod\"\n ? APP_REGISTRY_URL_PROD\n : registry ?? APP_REGISTRY_URL_STAGE;\n\nexport const getExtensionManagerBaseUrl = (\n environment: \"prod\" | \"stage\" | undefined,\n extensionManager: string | null\n): string =>\n environment === \"prod\"\n ? EXTENSION_MANAGER_URL_PROD\n : extensionManager ?? EXTENSION_MANAGER_URL_STAGE;\n\n/**\n * Extracts programId and envId from the repo value\n * @param repo - the repo value\n * @returns object with programId and envId\n * @ignore\n */\nexport function extractProgramIdEnvId(repo: string): {\n programId: string;\n envId: string;\n} {\n const regex: RegExp = /p(\\d+)-e(\\d+)/;\n const match: RegExpMatchArray | null = regex.exec(repo);\n if (!match) {\n throw new Error(\"Error parsing a repo value\");\n }\n\n return {\n programId: match[1],\n envId: match[2],\n };\n}\n\n/**\n * Builds the URL for fetching extensions from the Extension Manager service\n * @param config - the Extension Manager configuration\n * @returns the URL for fetching extensions\n * @ignore\n */\nexport function buildExtensionManagerUrl(\n config: ExtensionManagerConfig\n): string {\n const scope = config.scope\n ? Object.fromEntries(\n Object.entries(config.scope).map(([k, v]) => [`scope.${k}`, v])\n )\n : {};\n const extensionPoints: string = `${config.service}/${config.extensionPoint}/${config.version}`;\n const queryParams = new URLSearchParams({\n ...scope,\n extensionPoints,\n });\n\n return `${config.baseUrl}/v2/extensions?${queryParams.toString()}`;\n}\n\n/**\n * @ignore\n */\nexport async function fetchExtensionsFromExtensionManager(\n config: ExtensionManagerConfig\n): Promise<ExtensionManagerExtension[]> {\n const resp: Response = await fetch(buildExtensionManagerUrl(config), {\n headers: {\n Authorization: `Bearer ${config.auth.imsToken}`,\n \"x-api-key\": config.apiKey,\n \"x-org-id\": config.imsOrg,\n },\n });\n\n if (resp.status !== 200) {\n throw new Error(\n `Extension Manager returned non-200 response (${\n resp.status\n }): ${await resp.text()}`\n );\n }\n\n return resp.json();\n}\n\n/**\n * Takes an array of extensions from the App Registry, an array of extensions from the Extension Manager, and\n * merges them into a list of Extensions. If an extension is disabled in the Extension Manager, it is removed from\n * the list.\n * Extension list from the App Registry is used as a base.\n * @ignore\n */\nexport function mergeExtensions(\n appRegistryExtensions: InstalledExtensions,\n extensionManagerExtensions: ExtensionManagerExtension[],\n extensionPointId: ExtensionPointId\n): InstalledExtensions {\n const mergedExtensions: InstalledExtensions = Object.assign(\n appRegistryExtensions,\n {}\n );\n extensionManagerExtensions.forEach((extension: ExtensionManagerExtension) => {\n if (extension.disabled) {\n // remove disabled extensions\n delete mergedExtensions[extension.name];\n } else {\n const extPoint: ExtensionPoint | undefined =\n extension.extensionPoints.find(\n (_extensionPoint: ExtensionPoint) =>\n _extensionPoint.extensionPoint ===\n `${extensionPointId.service}/${extensionPointId.name}/${extensionPointId.version}`\n );\n if (extPoint) {\n // add a new extension record or replace the existing one by an extension record from Extension Manager\n // extension points are useful for filtering out extensions\n mergedExtensions[extension.name] = {\n id: extension.name,\n url: extPoint.url,\n configuration: extension.configuration,\n extensionPoints: extension.extensionPoints.map(\n (point) => point.extensionPoint\n ),\n };\n } else {\n //this should never happen because we query Extension Manager service for our specific extension point\n console.warn(\n `Extension point ${extensionPointId.service}/${extensionPointId.name}/${extensionPointId.version} not found for extension ${extension.name}`\n );\n }\n }\n });\n\n return mergedExtensions;\n}\n\nasync function getExtensionManagerExtensions(\n discoveryConfig: DiscoveryConfig,\n authConfig: AuthConfig,\n providerConfig: ExtensionProviderConfig,\n extensionPointId: ExtensionPointId\n): Promise<InstalledExtensions> {\n const config = {\n apiKey: authConfig.apiKey,\n auth: {\n schema: \"Bearer\",\n imsToken: authConfig.imsToken,\n },\n service: extensionPointId.service,\n extensionPoint: extensionPointId.name,\n version: extensionPointId.version,\n imsOrg: authConfig.imsOrg,\n scope: discoveryConfig.scope,\n };\n\n const appRegistryConfig = {\n ...config,\n baseUrl: getExtensionRegistryBaseUrl(\n discoveryConfig.experienceShellEnvironment,\n providerConfig.appRegistryUrl\n ),\n } as ExtensionManagerConfig;\n const appRegistryExtensionsProvider: ExtensionsProvider =\n createExtensionRegistryAsObjectsProvider(appRegistryConfig);\n\n const extensionManagerConfiguration = {\n ...config,\n baseUrl: getExtensionManagerBaseUrl(\n discoveryConfig.experienceShellEnvironment,\n providerConfig.extensionManagerUrl\n ),\n } as ExtensionManagerConfig;\n const [appRegistryExtensions, extensionManagerExtensions] = await Promise.all(\n [\n appRegistryExtensionsProvider(),\n providerConfig.disableExtensionManager\n ? []\n : fetchExtensionsFromExtensionManager(extensionManagerConfiguration),\n ]\n );\n\n if (providerConfig.disableExtensionManager) {\n return appRegistryExtensions;\n } else {\n return mergeExtensions(\n appRegistryExtensions,\n extensionManagerExtensions,\n extensionPointId\n );\n }\n}\n\n/**\n * Creates an extension manager extension provider\n * @ignore\n */\nexport function createExtensionManagerExtensionsProvider(\n discoveryConfig: DiscoveryConfig,\n authConfig: AuthConfig,\n providerConfig: ExtensionProviderConfig,\n extensionPointId: ExtensionPointId\n): ExtensionsProvider {\n return () => {\n return getExtensionManagerExtensions(\n discoveryConfig,\n authConfig,\n providerConfig,\n extensionPointId\n );\n };\n}\n","/*************************************************************************\n * ADOBE CONFIDENTIAL\n * ___________________\n *\n * Copyright 2024 Adobe\n * All Rights Reserved.\n *\n * NOTICE: All information contained herein is, and remains\n * the property of Adobe and its suppliers, if any. The intellectual\n * and technical concepts contained herein are proprietary to Adobe\n * and its suppliers and are protected by all applicable intellectual\n * property laws, including trade secret and copyright laws.\n * Dissemination of this information or reproduction of this material\n * is strictly forbidden unless prior written permission is obtained\n * from Adobe.\n **************************************************************************/\nimport { ExtensionsProvider, InstalledExtensions } from \"@adobe/uix-host\";\nimport { Extension } from \"@adobe/uix-core\";\nimport { ExtensionPointId } from \"./ExtensionManagerProvider\";\nconst EXT_PARAM_PREFIX = \"ext\";\n\nexport interface ExtUrlParams {\n [key: string]: string;\n}\n\n/**\n * Validates if a URL is safe and only allows HTTP/HTTPS protocols\n * @param url - The URL string to validate\n * @returns true if the URL is valid and uses HTTP/HTTPS protocol, false otherwise\n */\nexport function isValidHttpUrl(url: string): boolean {\n try {\n const parsedUrl = new URL(url);\n\n return parsedUrl.protocol === \"http:\" || parsedUrl.protocol === \"https:\";\n } catch {\n return false;\n }\n}\n\n/**\n * Extracts extension URLs from the query string\n * @ignore\n */\nexport function extractExtUrlParams(\n queryString: string | undefined\n): ExtUrlParams {\n if (!queryString) {\n return {};\n }\n const params: URLSearchParams = new URLSearchParams(queryString);\n return Array.from(params.entries()).reduce((extParams, [key, value]) => {\n if (key === EXT_PARAM_PREFIX || key.startsWith(`${EXT_PARAM_PREFIX}.`)) {\n extParams[key] = value;\n }\n return extParams;\n }, {} as ExtUrlParams);\n}\n\n/**\n * Generates an extension ID from the extension URL\n * @ignore\n */\nexport function generateExtensionId(extensionUrl: string): string {\n return extensionUrl.replace(/\\W/g, \"_\");\n}\n\n/**\n * Creates an ExtensionsProvider that provides extensions from the URL\n * @ignore\n */\nexport function createUrlExtensionsProvider(\n extensionPointId: ExtensionPointId,\n queryString: string | undefined\n): ExtensionsProvider {\n const extUrlParams: ExtUrlParams = extractExtUrlParams(queryString);\n\n const extensionUrls: string[] = Object.keys(extUrlParams)\n .filter(\n (extParam) =>\n extParam === EXT_PARAM_PREFIX ||\n extParam ===\n `${EXT_PARAM_PREFIX}.${extensionPointId.service}/${extensionPointId.name}/${extensionPointId.version}`\n )\n .flatMap((extParam) => {\n const paramValue = extUrlParams[extParam];\n // If it's a single value, return it in an array. If it's already an array, return it as is.\n if (Array.isArray(paramValue)) {\n return paramValue.filter((param) => isValidHttpUrl(param));\n } else {\n return isValidHttpUrl(paramValue) ? [paramValue] : [];\n }\n });\n\n const installedExtensions: InstalledExtensions = extensionUrls\n .map((extensionUrl: string) => {\n return {\n id: generateExtensionId(extensionUrl),\n url: extensionUrl,\n extensionPoints: [\n `${extensionPointId.service}/${extensionPointId.name}/${extensionPointId.version}`,\n ],\n } as Extension;\n })\n .reduce((acc: InstalledExtensions, extension: Extension) => {\n acc[extension.id] = extension;\n return acc;\n }, {} as InstalledExtensions);\n\n return async () => installedExtensions;\n}\n","/*\nCopyright 2022 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nimport { useCallback, useContext, useEffect, useMemo, useState } from \"react\";\nimport type {\n GuestConnection,\n GuestApis,\n RemoteGuestApis,\n RemoteGuestApiNS,\n VirtualApi,\n} from \"@adobe/uix-core\";\n\nimport { Host, HostEvents } from \"@adobe/uix-host\";\nimport type { CapabilitySpec, Port } from \"@adobe/uix-host\";\nimport { useHost } from \"./useHost\";\nimport { ExtensibleComponentBoundaryContext } from \"../components/ExtensibleComponentBoundary\";\nimport { ExtensionRegistryEndpointRegistration } from \"@adobe/uix-host\";\n\n/**\n * @internal\n */\nexport interface TypedGuestConnection<T extends GuestApis>\n extends GuestConnection {\n id: GuestConnection[\"id\"];\n apis: RemoteGuestApis<T>;\n}\n\n/** @public */\nexport interface UseExtensionsConfig<\n Incoming extends GuestApis,\n Outgoing extends VirtualApi\n> {\n /**\n * A {@link @adobe/uix-host#CapabilitySpec} describing the namespaced methods\n * extensions must implement to be used by this component.\n *\n * @remarks\n * This declaration is used to filter the extensions that will be\n * returned; if they don't implement those methods, they will be filtered out.\n */\n requires?: CapabilitySpec<Required<Incoming>>;\n /**\n * A namespaced object of methods which extensions will be able to call.\n *\n * @remarks This is the counterpart of `requires`; in `requires`, the you\n * describes methods the extension must implement that your host code will\n * call, and in `provides`, you implement host methods that extensions will be\n * able to call.\n *\n * Most cases for host-side methods will use the state of the component. This\n * can cause unexpected bugs in React if the config callback is run on every\n * render. **useExtensions caches the config callback by default!**\n * So remember to pass a deps array, so that the config callback re-runs under\n * the right conditions.\n */\n provides?: Outgoing;\n /**\n * Sets when re-render is triggered on extension load.\n *\n * @remarks\n * Set to `each` to trigger a component re-render every time an individual\n * extension loads, which may result in multiple UI updates. Set to `all` to\n * wait until all extensions have loaded to re-render the component.\n * @defaultValue \"each\"\n */\n updateOn?: \"each\" | \"all\";\n}\n\n/** @public */\nexport interface UseExtensionsResult<T extends GuestApis> {\n /**\n * A list of loaded guests which implement the methods specified in\n * `requires`, represented as {@link @adobe/uix-host#Port} objects which\n * present methods to be called.\n */\n extensions: TypedGuestConnection<T>[];\n /**\n * This is `true` until all extensions are loaded. Use for rendering spinners\n * or other intermediate UI.\n */\n loading: boolean;\n /**\n * Populated with an Error if there were any problems during the load process.\n */\n error?: Error;\n}\n\nconst NO_EXTENSIONS: [] = [];\n\n/**\n * Fetch extensions which implement an API, provide them methods, and use them.\n *\n * @remarks `useExtensions` does three things at once:\n * - Gets all extensions which implement the APIs described in the `require` field\n * - Exposes any functions defined in the `provide` field to those extensions\n * - Returns an object whose `extensions` property is a list of `Port` objects representing those extensions\n *\n * useExtensions will trigger a re-render when extensions load. You can choose whether it triggers that rerender as each extension loads, or only after all extensions have loaded.\n * @public\n */\nexport function useExtensions<\n Incoming extends GuestApis,\n Outgoing extends VirtualApi\n>(\n configFactory: (host: Host) => UseExtensionsConfig<Incoming, Outgoing>,\n deps: unknown[] = []\n): UseExtensionsResult<Incoming> {\n const { host, error } = useHost();\n if (error) {\n return {\n extensions: NO_EXTENSIONS,\n loading: false,\n error,\n };\n }\n const [hostError, setHostError] = useState<Error>();\n const extensionPoints = useContext(ExtensibleComponentBoundaryContext);\n const boundryExtensionPointsAsString = extensionPoints?.map(\n ({\n service,\n extensionPoint,\n version,\n }: ExtensionRegistryEndpointRegistration) =>\n `${service}/${extensionPoint}/${version}`\n );\n const baseDeps = [host, ...deps];\n const {\n requires,\n provides,\n updateOn = \"each\",\n } = useMemo(() => configFactory(host), baseDeps);\n\n const getExtensions = useCallback(() => {\n const newExtensions = [];\n const guests = host.getLoadedGuests(requires);\n\n // Extension filtering: If the boundary is provided, only return extensions which have extensionPoint\n // specified in a provided boundry. Otherwise no filtering is done.\n for (const guest of guests) {\n const allExtensionPoints: string[] =\n getAllExtensionPointsFromGuest(guest);\n\n if (\n !boundryExtensionPointsAsString ||\n !allExtensionPoints.length ||\n isGuestExtensionPointInBoundary(\n boundryExtensionPointsAsString,\n allExtensionPoints\n )\n ) {\n newExtensions.push(guest as unknown as TypedGuestConnection<Incoming>);\n }\n }\n return newExtensions.length === 0 ? NO_EXTENSIONS : newExtensions;\n }, [...baseDeps, requires]);\n\n const subscribe = useCallback(\n (handler: EventListener) => {\n const eventName = updateOn === \"all\" ? \"loadallguests\" : \"guestload\";\n host.addEventListener(eventName, handler);\n\n return () => {\n host.removeEventListener(eventName, handler);\n };\n },\n [...baseDeps, updateOn]\n );\n\n const subscribeToUnload = useCallback((handler: EventListener) => {\n host.addEventListener(\"guestunload\", handler);\n\n return () => {\n host.removeEventListener(\"guestunload\", handler);\n };\n }, baseDeps);\n\n const [extensions, setExtensions] = useState(() => getExtensions());\n\n useEffect(() => {\n return subscribe(() => setExtensions(getExtensions()));\n }, [subscribe]);\n\n const unloadExtentionCallback = (e: CustomEvent) => {\n const eventDetail = e.detail;\n const guest = eventDetail.guest as Port<GuestApis>;\n\n if (guest && guest.id) {\n setExtensions((prevExtensions) => {\n const filtered = prevExtensions.filter(\n (ext) => ext.id !== guest.id || ext.url !== guest.url\n );\n return filtered.length === 0 ? NO_EXTENSIONS : filtered;\n });\n }\n };\n\n useEffect(() => {\n return subscribeToUnload(unloadExtentionCallback);\n }, [subscribeToUnload]);\n\n useEffect(() => {\n for (const guest of extensions) {\n if (provides) {\n guest.provide(provides);\n }\n }\n }, [provides, extensions]);\n\n useEffect(\n () =>\n host.addEventListener(\n \"error\",\n (event: Extract<HostEvents, { detail: { error: Error } }>) =>\n setHostError(event.detail.error)\n ),\n baseDeps\n );\n\n return {\n extensions,\n loading: extensions.length === 0 ? false : host.loading,\n error: hostError,\n };\n}\n\n/**\n * Each extension/guest can have\n * 1. `extensioPoints` field as an array of strings\n * 2. Metadata with array of extensionPoints. If the metadata is present, we need to use it for fitering the extensions.\n * Returns cumulative extension points.\n * @param guest\n * @returns array of extension points as strings\n */\nfunction getAllExtensionPointsFromGuest(guest: Port<GuestApis>): string[] {\n try {\n const guestExtensionPointsFromMetadata = guest.metadata?.extensions?.map(\n (extension: { extensionPoint: string }) => extension?.extensionPoint\n );\n const allExtensionPoints = [\n ...(guest.extensionPoints || []),\n ...(guestExtensionPointsFromMetadata || []),\n ];\n return allExtensionPoints;\n } catch {\n console.error(\n \"Error occurred while getting extension points from guest and metadata. Extension boundaries will not be effective.\"\n );\n return [];\n }\n}\n\nfunction isGuestExtensionPointInBoundary(\n boundryExtensionPointsAsString: string[],\n guestExtensionPoints: string[]\n) {\n return (\n boundryExtensionPointsAsString?.length &&\n guestExtensionPoints?.length &&\n guestExtensionPoints.some((extensionPoint) =>\n boundryExtensionPointsAsString.includes(extensionPoint)\n )\n );\n}\n","/*\nCopyright 2022 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nimport { useContext } from \"react\";\nimport {\n ExtensionContext,\n ExtensibilityContext,\n} from \"../extension-context.js\";\n\n/**\n * Indicates if external extensions provider was processed. Returns true after passing extension list to the Host.\n *\n * @beta\n */\nexport function useExtensionListFetched(): boolean {\n const extensionsInfo = useContext<ExtensibilityContext>(ExtensionContext);\n\n return extensionsInfo.extensionListFetched;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/uix-host-react",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -20,8 +20,8 @@
20
20
  ],
21
21
  "bugs": "https://github.com/adobe/uix-sdk/issues",
22
22
  "dependencies": {
23
- "@adobe/uix-core": "^1.1.5",
24
- "@adobe/uix-host": "^1.1.5"
23
+ "@adobe/uix-core": "^1.1.7",
24
+ "@adobe/uix-host": "^1.1.7"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@swc/jest": "^0.2.29",
@@ -0,0 +1,515 @@
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 React from "react";
14
+ import { render, waitFor, cleanup } from "@testing-library/react";
15
+ import { Extensible } from "./Extensible";
16
+ import { Host } from "@adobe/uix-host";
17
+ import type { InstalledExtensions } from "@adobe/uix-host";
18
+
19
+ jest.mock("@adobe/uix-host");
20
+
21
+ const MockedHost = Host as jest.MockedClass<typeof Host>;
22
+
23
+ describe("Extensible", () => {
24
+ let mockUnload: jest.Mock;
25
+ let mockLoad: jest.Mock;
26
+ let consoleErrorSpy: jest.SpyInstance;
27
+
28
+ beforeEach(() => {
29
+ jest.clearAllMocks();
30
+
31
+ mockUnload = jest.fn().mockResolvedValue(undefined);
32
+ mockLoad = jest.fn().mockResolvedValue(undefined);
33
+
34
+ MockedHost.mockImplementation(
35
+ () =>
36
+ ({
37
+ unload: mockUnload,
38
+ load: mockLoad,
39
+ addEventListener: jest.fn(),
40
+ removeEventListener: jest.fn(),
41
+ } as unknown as Host)
42
+ );
43
+
44
+ consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => {});
45
+ });
46
+
47
+ afterEach(() => {
48
+ cleanup();
49
+ consoleErrorSpy.mockRestore();
50
+ });
51
+
52
+ describe("Host cleanup on unmount", () => {
53
+ it("should unload host when component unmounts", async () => {
54
+ const mockExtensions: InstalledExtensions = {
55
+ "ext-1": { id: "ext-1", url: "https://example.com/ext1" },
56
+ };
57
+
58
+ const extensionsProvider = jest.fn().mockResolvedValue(mockExtensions);
59
+
60
+ const { unmount } = render(
61
+ <Extensible appName="test-app" extensionsProvider={extensionsProvider}>
62
+ <div>Test Child</div>
63
+ </Extensible>
64
+ );
65
+
66
+ // Wait for extensions to load and host to be created
67
+ await waitFor(() => {
68
+ expect(MockedHost).toHaveBeenCalled();
69
+ });
70
+
71
+ // Unmount the component
72
+ unmount();
73
+
74
+ // Verify that unload was called
75
+ await waitFor(() => {
76
+ expect(mockUnload).toHaveBeenCalled();
77
+ });
78
+ });
79
+
80
+ it("should handle unload errors gracefully on unmount", async () => {
81
+ const mockExtensions: InstalledExtensions = {
82
+ "ext-1": { id: "ext-1", url: "https://example.com/ext1" },
83
+ };
84
+
85
+ const extensionsProvider = jest.fn().mockResolvedValue(mockExtensions);
86
+
87
+ mockUnload.mockRejectedValue(new Error("Unload failed"));
88
+
89
+ const { unmount } = render(
90
+ <Extensible appName="test-app" extensionsProvider={extensionsProvider}>
91
+ <div>Test Child</div>
92
+ </Extensible>
93
+ );
94
+
95
+ await waitFor(() => {
96
+ expect(MockedHost).toHaveBeenCalled();
97
+ });
98
+
99
+ // Should not throw when unmounting even if unload fails
100
+ expect(() => unmount()).not.toThrow();
101
+
102
+ await waitFor(() => {
103
+ expect(mockUnload).toHaveBeenCalled();
104
+ });
105
+ });
106
+
107
+ it("should not call unload if host was never created", () => {
108
+ const extensionsProvider = jest.fn().mockResolvedValue({});
109
+
110
+ const { unmount } = render(
111
+ <Extensible appName="test-app" extensionsProvider={extensionsProvider}>
112
+ <div>Test Child</div>
113
+ </Extensible>
114
+ );
115
+
116
+ unmount();
117
+
118
+ // Unload should not be called if host was never created
119
+ expect(mockUnload).not.toHaveBeenCalled();
120
+ });
121
+ });
122
+
123
+ describe("Old host unloading when creating new host", () => {
124
+ it("should unload old host before creating a new one when sharedContext changes and extensions reload", async () => {
125
+ const firstExtensions: InstalledExtensions = {
126
+ "ext-1": { id: "ext-1", url: "https://example.com/ext1" },
127
+ };
128
+
129
+ const secondExtensions: InstalledExtensions = {
130
+ "ext-1": { id: "ext-1", url: "https://example.com/ext1" },
131
+ "ext-2": { id: "ext-2", url: "https://example.com/ext2" },
132
+ };
133
+
134
+ const firstProvider = jest.fn().mockResolvedValue(firstExtensions);
135
+ const secondProvider = jest.fn().mockResolvedValue(secondExtensions);
136
+
137
+ const { rerender } = render(
138
+ <Extensible
139
+ appName="test-app"
140
+ extensionsProvider={firstProvider}
141
+ sharedContext={{ theme: "light" }}
142
+ >
143
+ <div>Test Child</div>
144
+ </Extensible>
145
+ );
146
+
147
+ // Wait for initial host creation
148
+ await waitFor(() => {
149
+ expect(MockedHost).toHaveBeenCalledTimes(1);
150
+ });
151
+
152
+ // Change both sharedContext and extensions provider to trigger re-render and host recreation
153
+ rerender(
154
+ <Extensible
155
+ appName="test-app"
156
+ extensionsProvider={secondProvider}
157
+ sharedContext={{ theme: "dark" }}
158
+ >
159
+ <div>Test Child</div>
160
+ </Extensible>
161
+ );
162
+
163
+ // Wait for old host to be unloaded and new host to be created
164
+ await waitFor(() => {
165
+ expect(mockUnload).toHaveBeenCalled();
166
+ expect(MockedHost).toHaveBeenCalledTimes(2);
167
+ });
168
+ });
169
+
170
+ it("should not unload or recreate host when only debug changes", async () => {
171
+ const mockExtensions: InstalledExtensions = {
172
+ "ext-1": { id: "ext-1", url: "https://example.com/ext1" },
173
+ };
174
+
175
+ const extensionsProvider = jest.fn().mockResolvedValue(mockExtensions);
176
+
177
+ const { rerender } = render(
178
+ <Extensible
179
+ appName="test-app"
180
+ extensionsProvider={extensionsProvider}
181
+ debug={false}
182
+ >
183
+ <div>Test Child</div>
184
+ </Extensible>
185
+ );
186
+
187
+ await waitFor(() => {
188
+ expect(MockedHost).toHaveBeenCalledTimes(1);
189
+ });
190
+
191
+ mockUnload.mockClear();
192
+
193
+ // Change debug to trigger effect re-run
194
+ rerender(
195
+ <Extensible
196
+ appName="test-app"
197
+ extensionsProvider={extensionsProvider}
198
+ debug={true}
199
+ >
200
+ <div>Test Child</div>
201
+ </Extensible>
202
+ );
203
+
204
+ // Since debug is in the dependency array, the effect should re-run
205
+ // and since host already exists, it should NOT unload (it only unloads in the if (!host || sharedContextChanged) branch)
206
+ await waitFor(() => {
207
+ expect(MockedHost).toHaveBeenCalledTimes(1); // No new host created
208
+ });
209
+
210
+ // Unload should not be called because the existing host is reused
211
+ expect(mockUnload).not.toHaveBeenCalled();
212
+ });
213
+
214
+ it("should handle unload errors when creating new host due to sharedContext change", async () => {
215
+ const firstExtensions: InstalledExtensions = {
216
+ "ext-1": { id: "ext-1", url: "https://example.com/ext1" },
217
+ };
218
+
219
+ const secondExtensions: InstalledExtensions = {
220
+ "ext-1": { id: "ext-1", url: "https://example.com/ext1" },
221
+ "ext-2": { id: "ext-2", url: "https://example.com/ext2" },
222
+ };
223
+
224
+ const firstProvider = jest.fn().mockResolvedValue(firstExtensions);
225
+ const secondProvider = jest.fn().mockResolvedValue(secondExtensions);
226
+
227
+ mockUnload.mockRejectedValue(new Error("Unload failed"));
228
+
229
+ const { rerender } = render(
230
+ <Extensible
231
+ appName="test-app"
232
+ extensionsProvider={firstProvider}
233
+ sharedContext={{ theme: "light" }}
234
+ >
235
+ <div>Test Child</div>
236
+ </Extensible>
237
+ );
238
+
239
+ await waitFor(() => {
240
+ expect(MockedHost).toHaveBeenCalledTimes(1);
241
+ });
242
+
243
+ // Should not throw even if unload fails
244
+ expect(() =>
245
+ rerender(
246
+ <Extensible
247
+ appName="test-app"
248
+ extensionsProvider={secondProvider}
249
+ sharedContext={{ theme: "dark" }}
250
+ >
251
+ <div>Test Child</div>
252
+ </Extensible>
253
+ )
254
+ ).not.toThrow();
255
+
256
+ await waitFor(() => {
257
+ expect(mockUnload).toHaveBeenCalled();
258
+ expect(MockedHost).toHaveBeenCalledTimes(2);
259
+ });
260
+ });
261
+ });
262
+
263
+ describe("Cancel stale extension fetches", () => {
264
+ it("should cancel stale extension fetch when extensionsProvider changes", async () => {
265
+ const firstExtensions: InstalledExtensions = {
266
+ "ext-1": { id: "ext-1", url: "https://example.com/ext1" },
267
+ };
268
+
269
+ const secondExtensions: InstalledExtensions = {
270
+ "ext-2": { id: "ext-2", url: "https://example.com/ext2" },
271
+ };
272
+
273
+ let firstResolve: (value: InstalledExtensions) => void;
274
+ const firstProviderPromise = new Promise<InstalledExtensions>(
275
+ (resolve) => {
276
+ firstResolve = resolve;
277
+ }
278
+ );
279
+
280
+ const firstProvider = jest.fn().mockReturnValue(firstProviderPromise);
281
+ const secondProvider = jest.fn().mockResolvedValue(secondExtensions);
282
+
283
+ const { rerender } = render(
284
+ <Extensible appName="test-app" extensionsProvider={firstProvider}>
285
+ <div>Test Child</div>
286
+ </Extensible>
287
+ );
288
+
289
+ // Change the provider before the first one resolves
290
+ rerender(
291
+ <Extensible appName="test-app" extensionsProvider={secondProvider}>
292
+ <div>Test Child</div>
293
+ </Extensible>
294
+ );
295
+
296
+ // Now resolve the first provider (after deps have changed)
297
+ firstResolve!(firstExtensions);
298
+
299
+ // Wait for second provider to complete
300
+ await waitFor(() => {
301
+ expect(secondProvider).toHaveBeenCalled();
302
+ });
303
+
304
+ // Verify that Host was only created for the second set of extensions
305
+ await waitFor(() => {
306
+ expect(MockedHost).toHaveBeenCalled();
307
+ });
308
+
309
+ // The mock load should be called with secondExtensions, not firstExtensions
310
+ await waitFor(() => {
311
+ expect(mockLoad).toHaveBeenCalledWith(secondExtensions, undefined);
312
+ });
313
+ });
314
+
315
+ it("should not log error for cancelled fetch", async () => {
316
+ let firstReject: (reason: Error) => void;
317
+ const firstProviderPromise = new Promise<InstalledExtensions>(
318
+ (_resolve, reject) => {
319
+ firstReject = reject;
320
+ }
321
+ );
322
+
323
+ const secondExtensions: InstalledExtensions = {
324
+ "ext-2": { id: "ext-2", url: "https://example.com/ext2" },
325
+ };
326
+
327
+ const firstProvider = jest.fn().mockReturnValue(firstProviderPromise);
328
+ const secondProvider = jest.fn().mockResolvedValue(secondExtensions);
329
+
330
+ const { rerender } = render(
331
+ <Extensible appName="test-app" extensionsProvider={firstProvider}>
332
+ <div>Test Child</div>
333
+ </Extensible>
334
+ );
335
+
336
+ // Change the provider before the first one completes
337
+ rerender(
338
+ <Extensible appName="test-app" extensionsProvider={secondProvider}>
339
+ <div>Test Child</div>
340
+ </Extensible>
341
+ );
342
+
343
+ // Reject the first provider (simulating an error)
344
+ firstReject!(new Error("Fetch failed"));
345
+
346
+ // Wait for second provider to complete
347
+ await waitFor(() => {
348
+ expect(secondProvider).toHaveBeenCalled();
349
+ });
350
+
351
+ // Console.error should not be called for the cancelled fetch
352
+ expect(consoleErrorSpy).not.toHaveBeenCalledWith(
353
+ "Fetching list of extensions failed!",
354
+ expect.any(Error)
355
+ );
356
+ });
357
+
358
+ it("should cancel extension fetch on unmount", async () => {
359
+ let resolveExtensions: (value: InstalledExtensions) => void;
360
+ const extensionsPromise = new Promise<InstalledExtensions>((resolve) => {
361
+ resolveExtensions = resolve;
362
+ });
363
+
364
+ const extensionsProvider = jest.fn().mockReturnValue(extensionsPromise);
365
+
366
+ const { unmount } = render(
367
+ <Extensible appName="test-app" extensionsProvider={extensionsProvider}>
368
+ <div>Test Child</div>
369
+ </Extensible>
370
+ );
371
+
372
+ // Unmount before the promise resolves
373
+ unmount();
374
+
375
+ // Resolve the promise after unmount
376
+ resolveExtensions!({
377
+ "ext-1": { id: "ext-1", url: "https://example.com/ext1" },
378
+ });
379
+
380
+ // Wait a bit to ensure any pending updates are processed
381
+ await new Promise((resolve) => setTimeout(resolve, 100));
382
+
383
+ // Host should not be created since component was unmounted
384
+ expect(MockedHost).not.toHaveBeenCalled();
385
+ });
386
+
387
+ it("should handle extensionsListCallback with cancelled fetch", async () => {
388
+ const firstExtensions: InstalledExtensions = {
389
+ "ext-1": { id: "ext-1", url: "https://example.com/ext1" },
390
+ };
391
+
392
+ const secondExtensions: InstalledExtensions = {
393
+ "ext-2": { id: "ext-2", url: "https://example.com/ext2" },
394
+ };
395
+
396
+ let firstResolve: (value: InstalledExtensions) => void;
397
+ const firstProviderPromise = new Promise<InstalledExtensions>(
398
+ (resolve) => {
399
+ firstResolve = resolve;
400
+ }
401
+ );
402
+
403
+ const firstProvider = jest.fn().mockReturnValue(firstProviderPromise);
404
+ const secondProvider = jest.fn().mockResolvedValue(secondExtensions);
405
+
406
+ const extensionsListCallback = jest.fn((exts) => exts);
407
+
408
+ const { rerender } = render(
409
+ <Extensible
410
+ appName="test-app"
411
+ extensionsProvider={firstProvider}
412
+ extensionsListCallback={extensionsListCallback}
413
+ >
414
+ <div>Test Child</div>
415
+ </Extensible>
416
+ );
417
+
418
+ // Change the callback (which is a dependency)
419
+ const newCallback = jest.fn((exts) => exts);
420
+ rerender(
421
+ <Extensible
422
+ appName="test-app"
423
+ extensionsProvider={secondProvider}
424
+ extensionsListCallback={newCallback}
425
+ >
426
+ <div>Test Child</div>
427
+ </Extensible>
428
+ );
429
+
430
+ // Resolve the first provider after deps changed
431
+ firstResolve!(firstExtensions);
432
+
433
+ await waitFor(() => {
434
+ expect(secondProvider).toHaveBeenCalled();
435
+ });
436
+
437
+ // Only the second callback should be called
438
+ await waitFor(() => {
439
+ expect(newCallback).toHaveBeenCalled();
440
+ });
441
+
442
+ // The first callback should not be called with cancelled results
443
+ expect(extensionsListCallback).not.toHaveBeenCalled();
444
+ });
445
+ });
446
+
447
+ describe("Integration scenarios", () => {
448
+ it("should handle rapid provider changes and cancel stale fetches", async () => {
449
+ const providers = Array.from({ length: 5 }, (_, i) =>
450
+ jest.fn().mockResolvedValue({
451
+ [`ext-${i}`]: { id: `ext-${i}`, url: `https://example.com/ext${i}` },
452
+ })
453
+ );
454
+
455
+ const { rerender } = render(
456
+ <Extensible appName="test-app" extensionsProvider={providers[0]}>
457
+ <div>Test Child</div>
458
+ </Extensible>
459
+ );
460
+
461
+ // Rapidly change providers
462
+ for (let i = 1; i < providers.length; i++) {
463
+ rerender(
464
+ <Extensible appName="test-app" extensionsProvider={providers[i]}>
465
+ <div>Test Child</div>
466
+ </Extensible>
467
+ );
468
+ }
469
+
470
+ // Wait for everything to settle
471
+ await waitFor(() => {
472
+ expect(providers[providers.length - 1]).toHaveBeenCalled();
473
+ });
474
+
475
+ // All providers should have been called
476
+ providers.forEach((provider) => {
477
+ expect(provider).toHaveBeenCalled();
478
+ });
479
+
480
+ // Host should be created (at least for the last provider)
481
+ await waitFor(() => {
482
+ expect(MockedHost).toHaveBeenCalled();
483
+ });
484
+ });
485
+
486
+ it("should properly cleanup when unmounting during active fetch", async () => {
487
+ let resolveExtensions: (value: InstalledExtensions) => void;
488
+ const extensionsPromise = new Promise<InstalledExtensions>((resolve) => {
489
+ resolveExtensions = resolve;
490
+ });
491
+
492
+ const extensionsProvider = jest.fn().mockReturnValue(extensionsPromise);
493
+
494
+ const { unmount } = render(
495
+ <Extensible appName="test-app" extensionsProvider={extensionsProvider}>
496
+ <div>Test Child</div>
497
+ </Extensible>
498
+ );
499
+
500
+ // Unmount while fetch is still pending
501
+ unmount();
502
+
503
+ // Resolve after unmount
504
+ resolveExtensions!({
505
+ "ext-1": { id: "ext-1", url: "https://example.com/ext1" },
506
+ });
507
+
508
+ await new Promise((resolve) => setTimeout(resolve, 100));
509
+
510
+ // Should not have created a host or logged errors
511
+ expect(MockedHost).not.toHaveBeenCalled();
512
+ expect(consoleErrorSpy).not.toHaveBeenCalled();
513
+ });
514
+ });
515
+ });
@@ -129,8 +129,13 @@ export function Extensible({
129
129
  useState<boolean>(false);
130
130
  const prevSharedContext = useRef(JSON.stringify(sharedContext));
131
131
  useEffect(() => {
132
+ let cancelled = false;
133
+
132
134
  extensionsProvider()
133
135
  .then((loaded: InstalledExtensions) => {
136
+ if (cancelled) {
137
+ return;
138
+ }
134
139
  setExtensions((prev) => {
135
140
  let newExtensions = loaded;
136
141
 
@@ -148,14 +153,33 @@ export function Extensible({
148
153
  });
149
154
  })
150
155
  .catch((e: Error | unknown) => {
151
- console.error("Fetching list of extensions failed!", e);
156
+ if (!cancelled) {
157
+ console.error("Fetching list of extensions failed!", e);
158
+ }
152
159
  })
153
160
  .finally(() => {
154
- setExtensionListFetched(true);
161
+ if (!cancelled) {
162
+ setExtensionListFetched(true);
163
+ }
155
164
  });
165
+
166
+ return () => {
167
+ cancelled = true;
168
+ };
156
169
  }, [extensionsProvider, extensionsListCallback]);
157
170
 
158
171
  const [host, setHost] = useState<Host>();
172
+ const hostRef = useRef<Host>();
173
+
174
+ useEffect(() => {
175
+ return () => {
176
+ if (hostRef.current) {
177
+ hostRef.current.unload().catch(() => {});
178
+ hostRef.current = undefined;
179
+ }
180
+ };
181
+ }, []);
182
+
159
183
  useEffect(() => {
160
184
  function logError(msg: string) {
161
185
  return (e: Error | unknown) => {
@@ -181,12 +205,16 @@ export function Extensible({
181
205
  prevSharedContext.current = JSON.stringify(sharedContext);
182
206
  }
183
207
  if (!host || sharedContextChanged) {
208
+ if (hostRef.current) {
209
+ hostRef.current.unload().catch(() => {});
210
+ }
184
211
  const newHost = new Host({
185
212
  debug,
186
213
  hostName,
187
214
  runtimeContainer,
188
215
  sharedContext,
189
216
  });
217
+ hostRef.current = newHost;
190
218
  setHost(newHost);
191
219
  loadExtensions(newHost);
192
220
  } else {