@adobe/aio-commerce-lib-admin-ui 0.1.0 → 0.2.0-beta-20260714082406
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/README.md +1 -1
- package/dist/cjs/acl-resource-id-DBlYU0DE.d.cts +39 -0
- package/dist/cjs/acl-resource-id-D_hCU1Qg.cjs +69 -0
- package/dist/cjs/api/index.cjs +252 -0
- package/dist/cjs/api/index.d.cts +126 -0
- package/dist/cjs/grid-columns/index.cjs +151 -0
- package/dist/cjs/grid-columns/index.d.cts +146 -0
- package/dist/cjs/mass-actions/index.cjs +188 -0
- package/dist/cjs/mass-actions/index.d.cts +160 -0
- package/dist/cjs/menu/index.cjs +91 -0
- package/dist/cjs/menu/index.d.cts +63 -0
- package/dist/cjs/order-view-buttons/index.cjs +126 -0
- package/dist/cjs/order-view-buttons/index.d.cts +113 -0
- package/dist/cjs/rolldown-runtime-Cx6hovH8.cjs +67 -0
- package/dist/cjs/schemas-Ce10uBzN.cjs +41 -0
- package/dist/cjs/utils-B59fjd_w.cjs +39 -0
- package/dist/cjs/web/index.cjs +830 -0
- package/dist/cjs/web/index.d.cts +192 -0
- package/dist/es/acl-resource-id-DBlYU0DE.d.mts +39 -0
- package/dist/es/acl-resource-id-pryVxI_c.mjs +57 -0
- package/dist/es/api/index.d.mts +126 -0
- package/dist/es/api/index.mjs +262 -0
- package/dist/es/grid-columns/index.d.mts +146 -0
- package/dist/es/grid-columns/index.mjs +143 -0
- package/dist/es/mass-actions/index.d.mts +160 -0
- package/dist/es/mass-actions/index.mjs +178 -0
- package/dist/es/menu/index.d.mts +63 -0
- package/dist/es/menu/index.mjs +80 -0
- package/dist/es/order-view-buttons/index.d.mts +113 -0
- package/dist/es/order-view-buttons/index.mjs +119 -0
- package/dist/es/schemas-BFT8ys8P.mjs +34 -0
- package/dist/es/utils-COPGW1HO.mjs +32 -0
- package/dist/es/web/index.d.mts +192 -0
- package/dist/es/web/index.mjs +819 -0
- package/package.json +87 -10
- package/dist/cjs/index.cjs +0 -139
- package/dist/cjs/index.d.cts +0 -56
- package/dist/es/index.d.mts +0 -56
- package/dist/es/index.mjs +0 -114
|
@@ -0,0 +1,830 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
*
|
|
4
|
+
* Copyright 2026 Adobe. All rights reserved.
|
|
5
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
7
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
10
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
11
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
12
|
+
* governing permissions and limitations under the License.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
16
|
+
const require_rolldown_runtime = require('../rolldown-runtime-Cx6hovH8.cjs');
|
|
17
|
+
let react = require("react");
|
|
18
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
19
|
+
let _adobe_exc_app = require("@adobe/exc-app");
|
|
20
|
+
_adobe_exc_app = require_rolldown_runtime.__toESM(_adobe_exc_app, 1);
|
|
21
|
+
let _adobe_exc_app_page_js = require("@adobe/exc-app/page.js");
|
|
22
|
+
_adobe_exc_app_page_js = require_rolldown_runtime.__toESM(_adobe_exc_app_page_js, 1);
|
|
23
|
+
let _tanstack_react_router = require("@tanstack/react-router");
|
|
24
|
+
let react_dom_client = require("react-dom/client");
|
|
25
|
+
let _adobe_uix_guest = require("@adobe/uix-guest");
|
|
26
|
+
let _react_spectrum_s2_Provider = require("@react-spectrum/s2/Provider");
|
|
27
|
+
let _react_spectrum_s2_ProgressCircle = require("@react-spectrum/s2/ProgressCircle");
|
|
28
|
+
let _react_spectrum_s2_ButtonGroup = require("@react-spectrum/s2/ButtonGroup");
|
|
29
|
+
let _react_spectrum_s2_IllustratedMessage = require("@react-spectrum/s2/IllustratedMessage");
|
|
30
|
+
let _react_spectrum_s2_illustrations_linear_Error = require("@react-spectrum/s2/illustrations/linear/Error");
|
|
31
|
+
_react_spectrum_s2_illustrations_linear_Error = require_rolldown_runtime.__toESM(_react_spectrum_s2_illustrations_linear_Error, 1);
|
|
32
|
+
let react_error_boundary = require("react-error-boundary");
|
|
33
|
+
|
|
34
|
+
//#region source/web/react/commerce/lib.ts
|
|
35
|
+
/**
|
|
36
|
+
* Extracts the order ID from the given URL (must be absolute).
|
|
37
|
+
* @param href - The URL to read the order ID from (typically `window.location.href`).
|
|
38
|
+
*/
|
|
39
|
+
function parseOrderId(href) {
|
|
40
|
+
const urlObj = new URL(href);
|
|
41
|
+
return urlObj.searchParams.get("orderId") ?? new URLSearchParams(urlObj.hash.split("?")[1]).get("orderId");
|
|
42
|
+
}
|
|
43
|
+
/** Whether the app is embedded in a host frame (the Commerce Admin or the Experience Cloud shell). */
|
|
44
|
+
function isEmbeddedInHost() {
|
|
45
|
+
return globalThis.window.parent !== globalThis.window;
|
|
46
|
+
}
|
|
47
|
+
/** Whether this window is a Commerce UIX guest UI frame, as opposed to a control frame or standalone. */
|
|
48
|
+
function isUiFrame() {
|
|
49
|
+
return isEmbeddedInHost() && window.name.startsWith("uix-guest-");
|
|
50
|
+
}
|
|
51
|
+
/** Whether this window is a Commerce UIX guest control frame, which registers instead of attaching. */
|
|
52
|
+
function isControlFrame() {
|
|
53
|
+
return isEmbeddedInHost() && !window.name;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region source/web/react/auth/context/ims-context.tsx
|
|
58
|
+
const ImsContextValue = (0, react.createContext)(void 0);
|
|
59
|
+
/**
|
|
60
|
+
* Returns the IMS credentials provided by the host. Works inside the Commerce Admin and the
|
|
61
|
+
* Experience Cloud shell.
|
|
62
|
+
*
|
|
63
|
+
* @throws If no host provides credentials (e.g. the app is running standalone, outside both the
|
|
64
|
+
* Commerce Admin and the Experience Cloud shell).
|
|
65
|
+
*/
|
|
66
|
+
function useIms() {
|
|
67
|
+
const credentials = (0, react.use)(ImsContextValue);
|
|
68
|
+
if (credentials === void 0) throw new Error("useIms must be used inside an ImsContextProvider.");
|
|
69
|
+
if (!credentials) throw new Error("useIms requires running inside the Commerce Admin or the Experience Cloud shell, which provide the IMS credentials.");
|
|
70
|
+
return credentials;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Provides the IMS credentials for a mounted Admin UI iframe app.
|
|
74
|
+
* @param props - The resolved credentials (or null while unavailable) and the children to render.
|
|
75
|
+
*/
|
|
76
|
+
function ImsContextProvider(props) {
|
|
77
|
+
const { children, credentials } = props;
|
|
78
|
+
if (isEmbeddedInHost() && !credentials) return null;
|
|
79
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImsContextValue.Provider, {
|
|
80
|
+
value: credentials,
|
|
81
|
+
children
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region source/web/react/commerce/context/shared-context.tsx
|
|
87
|
+
const SharedContextValue = (0, react.createContext)(void 0);
|
|
88
|
+
/**
|
|
89
|
+
* Returns the current Commerce shared context provider state.
|
|
90
|
+
*/
|
|
91
|
+
function useInternalSharedContext() {
|
|
92
|
+
const context = (0, react.use)(SharedContextValue);
|
|
93
|
+
if (context === void 0) throw new Error("useSharedContext must be used inside a SharedContextProvider, which is only available in the Commerce Admin.");
|
|
94
|
+
return context;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Returns the current Commerce shared context. The guest connection is already established by
|
|
98
|
+
* the time this can be called (see {@link SharedContextProvider}).
|
|
99
|
+
*
|
|
100
|
+
* This is a low-level escape hatch that exposes the raw `sharedContext` and `host` objects.
|
|
101
|
+
* Prefer a purpose-built hook ({@link useCommerce}, {@link useMassActionContext},
|
|
102
|
+
* {@link useOrderViewButtonContext}) when one covers what you need.
|
|
103
|
+
*
|
|
104
|
+
* @throws If used outside a {@link SharedContextProvider}.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```tsx
|
|
108
|
+
* import { useSharedContext } from "@adobe/aio-commerce-lib-admin-ui/web";
|
|
109
|
+
*
|
|
110
|
+
* function ImsTokenLabel() {
|
|
111
|
+
* const { sharedContext } = useSharedContext();
|
|
112
|
+
* return <span>{sharedContext.get("imsToken")}</span>;
|
|
113
|
+
* }
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
function useSharedContext() {
|
|
117
|
+
const { extensionId, guestConnection } = useInternalSharedContext();
|
|
118
|
+
const sharedContext = useLiveSharedContext(guestConnection);
|
|
119
|
+
return {
|
|
120
|
+
extensionId,
|
|
121
|
+
host: guestConnection.host,
|
|
122
|
+
sharedContext
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Tracks the live UIX `sharedContext` for a connection.
|
|
127
|
+
*
|
|
128
|
+
* The host shares `sharedContext` on connect and may reassign it on later `contextchange`
|
|
129
|
+
* events. `useSyncExternalStore` subscribes to those events and surfaces the current instance
|
|
130
|
+
* (whose reference changes on each update), so consumers re-render when the host updates it.
|
|
131
|
+
*
|
|
132
|
+
* @param guestConnection - The established guest connection.
|
|
133
|
+
*/
|
|
134
|
+
function useLiveSharedContext(guestConnection) {
|
|
135
|
+
return (0, react.useSyncExternalStore)((0, react.useCallback)((onContextChange) => guestConnection.addEventListener("contextchange", onContextChange), [guestConnection]), () => guestConnection.sharedContext);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Provides the Commerce shared context for a mounted Admin UI iframe app.
|
|
139
|
+
* @param props - The props needed to initialize the shared context, including the already
|
|
140
|
+
* established guest connection.
|
|
141
|
+
*/
|
|
142
|
+
function SharedContextProvider(props) {
|
|
143
|
+
const { children, guestConnection, extensionId } = props;
|
|
144
|
+
const value = (0, react.useMemo)(() => ({
|
|
145
|
+
extensionId,
|
|
146
|
+
guestConnection
|
|
147
|
+
}), [extensionId, guestConnection]);
|
|
148
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SharedContextValue.Provider, {
|
|
149
|
+
value,
|
|
150
|
+
children
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
//#endregion
|
|
155
|
+
//#region source/web/react/promise-cache.ts
|
|
156
|
+
/**
|
|
157
|
+
* Creates a keyed cache that memoizes in-flight, resolved, and rejected promises, giving `use()`
|
|
158
|
+
* the reference-stable promise it needs while suspended and single-flighting side-effecting
|
|
159
|
+
* establishment calls across re-renders, remounts, and StrictMode double-invocation.
|
|
160
|
+
*
|
|
161
|
+
* Rejections are retained (not evicted) so `use()` replays them to an error boundary instead of
|
|
162
|
+
* suspending forever on a fresh pending promise. Retry a failed key via {@link RetryablePromiseCache.evict}.
|
|
163
|
+
*/
|
|
164
|
+
function createRetryablePromiseCache() {
|
|
165
|
+
const cache = /* @__PURE__ */ new Map();
|
|
166
|
+
return {
|
|
167
|
+
evictIfRejected(key) {
|
|
168
|
+
if (cache.get(key)?.rejected) cache.delete(key);
|
|
169
|
+
},
|
|
170
|
+
get(key, create) {
|
|
171
|
+
const cached = cache.get(key);
|
|
172
|
+
if (cached) return cached.promise;
|
|
173
|
+
const entry = {
|
|
174
|
+
promise: create(),
|
|
175
|
+
rejected: false
|
|
176
|
+
};
|
|
177
|
+
entry.promise.catch(() => {
|
|
178
|
+
entry.rejected = true;
|
|
179
|
+
});
|
|
180
|
+
cache.set(key, entry);
|
|
181
|
+
return entry.promise;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region source/web/react/commerce/hooks/use-commerce.ts
|
|
188
|
+
const commerceHosts = createRetryablePromiseCache();
|
|
189
|
+
/**
|
|
190
|
+
* Returns the cached Commerce Admin host promise for an extension, resolving it once over the
|
|
191
|
+
* given guest connection. The value is static for the lifetime of the connection.
|
|
192
|
+
*
|
|
193
|
+
* @param extensionId - The unique identifier for the extension app.
|
|
194
|
+
* @param connection - The established guest connection.
|
|
195
|
+
*/
|
|
196
|
+
function getCommerceHostPromise(extensionId, connection) {
|
|
197
|
+
const { integration } = connection.host;
|
|
198
|
+
if (!integration) throw new Error("The host does not provide the integration API needed to resolve the Commerce host.");
|
|
199
|
+
return commerceHosts.get(extensionId, () => integration.getCommerceHost());
|
|
200
|
+
}
|
|
201
|
+
/** Drops a failed Commerce host resolution for `extensionId`, so a later render retries it. */
|
|
202
|
+
function retryCommerceHost(extensionId) {
|
|
203
|
+
commerceHosts.evictIfRejected(extensionId);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Returns the host (domain) of the Commerce Admin the extension is embedded in, resolving it over
|
|
207
|
+
* the guest connection.
|
|
208
|
+
*
|
|
209
|
+
* @throws If used outside a Commerce Admin UI frame, or when the host does not expose the
|
|
210
|
+
* Commerce integration API.
|
|
211
|
+
*/
|
|
212
|
+
function useCommerce() {
|
|
213
|
+
const { extensionId, guestConnection } = useInternalSharedContext();
|
|
214
|
+
return { commerceHost: (0, react.use)(getCommerceHostPromise(extensionId, guestConnection)) };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
//#endregion
|
|
218
|
+
//#region source/web/react/commerce/hooks/use-extension-context.ts
|
|
219
|
+
/**
|
|
220
|
+
* Returns the context for a mass-action extension point: the selected row IDs the action was
|
|
221
|
+
* triggered with. The value is read from the host-provided Commerce context.
|
|
222
|
+
*
|
|
223
|
+
* @throws If used outside the Commerce shared context, or when that context does not include a
|
|
224
|
+
* mass-action selection.
|
|
225
|
+
*/
|
|
226
|
+
function useMassActionContext() {
|
|
227
|
+
const { sharedContext } = useSharedContext();
|
|
228
|
+
return (0, react.useMemo)(() => {
|
|
229
|
+
const selectedIds = sharedContext.get("selectedIds");
|
|
230
|
+
if (!Array.isArray(selectedIds)) throw new Error("Could not find `selectedIds` in the Commerce shared context. Is this frame running as a mass-action extension point?");
|
|
231
|
+
return { selectedIds };
|
|
232
|
+
}, [sharedContext]);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Returns the context for an order view-button extension point: the order ID the button was
|
|
236
|
+
* triggered from.
|
|
237
|
+
*
|
|
238
|
+
* @throws If no order ID is present in the page URL.
|
|
239
|
+
*/
|
|
240
|
+
function useOrderViewButtonContext() {
|
|
241
|
+
return (0, react.useMemo)(() => {
|
|
242
|
+
const orderId = parseOrderId(globalThis.location.href);
|
|
243
|
+
if (orderId === null) throw new Error("Could not find an order ID. Is this frame running as an order view-button extension point?");
|
|
244
|
+
return { orderId };
|
|
245
|
+
}, []);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
//#endregion
|
|
249
|
+
//#region source/web/react/commerce/hooks/use-host-connection.ts
|
|
250
|
+
/**
|
|
251
|
+
* Returns typed helpers for interacting with the Commerce Admin host.
|
|
252
|
+
*
|
|
253
|
+
* @throws If called before the guest connection is established, or when the host frame actions
|
|
254
|
+
* are unavailable.
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* ```tsx
|
|
258
|
+
* import { useHostConnection } from "@adobe/aio-commerce-lib-admin-ui/web";
|
|
259
|
+
*
|
|
260
|
+
* function DoneButton() {
|
|
261
|
+
* const { close } = useHostConnection();
|
|
262
|
+
* return <button onClick={() => void close()}>Done</button>;
|
|
263
|
+
* }
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
function useHostConnection() {
|
|
267
|
+
const { host } = useSharedContext();
|
|
268
|
+
return (0, react.useMemo)(() => {
|
|
269
|
+
const { field } = host;
|
|
270
|
+
const requireField = () => {
|
|
271
|
+
if (!field) throw new Error("Host frame actions are unavailable. They require an established guest connection with a host that exposes frame actions.");
|
|
272
|
+
return field;
|
|
273
|
+
};
|
|
274
|
+
return {
|
|
275
|
+
close: () => requireField().close(),
|
|
276
|
+
closeWithError: () => requireField().onError()
|
|
277
|
+
};
|
|
278
|
+
}, [host]);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
//#endregion
|
|
282
|
+
//#region source/web/react/routing/lib.ts
|
|
283
|
+
const HASH_ROUTE_PREFIX_PATTERN = /^[#/]+/u;
|
|
284
|
+
/**
|
|
285
|
+
* Determines if a given route is an index route.
|
|
286
|
+
* @param route The route to check.
|
|
287
|
+
*/
|
|
288
|
+
function isIndexRoute(route) {
|
|
289
|
+
return "index" in route && route.index;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Returns the path for a given route, removing any leading hash or slash characters.
|
|
293
|
+
* @param route The route to get the path for.
|
|
294
|
+
*/
|
|
295
|
+
function getRoutePath(route) {
|
|
296
|
+
if (isIndexRoute(route)) return "/";
|
|
297
|
+
return route.path.replace(HASH_ROUTE_PREFIX_PATTERN, "");
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Returns the "to" path for a given route, removing any leading hash or slash characters.
|
|
301
|
+
* @param path The path to convert to a "to" path.
|
|
302
|
+
*/
|
|
303
|
+
function getRouteTo(path) {
|
|
304
|
+
const routePath = path.replace(HASH_ROUTE_PREFIX_PATTERN, "");
|
|
305
|
+
return routePath ? `/${routePath}` : "/";
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Returns a router instance for an extension app, given the root component and route entries.
|
|
309
|
+
*
|
|
310
|
+
* @param rootComponent The root component of the extension app.
|
|
311
|
+
* @param routeEntries The route entries for the extension app.
|
|
312
|
+
* @param history The history implementation to use. Defaults to hash history; pass a memory
|
|
313
|
+
* history to get deterministic, seedable navigation (e.g. in tests).
|
|
314
|
+
*/
|
|
315
|
+
function createExtensionRouter(rootComponent, routeEntries, history = (0, _tanstack_react_router.createHashHistory)()) {
|
|
316
|
+
const rootRoute = (0, _tanstack_react_router.createRootRoute)({ component: () => rootComponent });
|
|
317
|
+
const routes = routeEntries.map((route) => (0, _tanstack_react_router.createRoute)({
|
|
318
|
+
component: () => route.element,
|
|
319
|
+
getParentRoute: () => rootRoute,
|
|
320
|
+
path: getRoutePath(route)
|
|
321
|
+
}));
|
|
322
|
+
return (0, _tanstack_react_router.createRouter)({
|
|
323
|
+
history,
|
|
324
|
+
routeTree: rootRoute.addChildren(routes)
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
//#endregion
|
|
329
|
+
//#region source/web/runtime-loader.ts
|
|
330
|
+
const EXPERIENCE_CLOUD_RUNTIME_HOST_PATTERN = /^(exc-unifiedcontent\.)?experience(-qa|-stage|-cdn|-cdn-stage)?\.adobe\.(com|net)$/u;
|
|
331
|
+
/** Convenience no-operation function */
|
|
332
|
+
const noop = () => void 0;
|
|
333
|
+
/** Creates a mock implementation of the Experience Cloud runtime to be used when the real runtime is not available. */
|
|
334
|
+
function createMockRuntime() {
|
|
335
|
+
return {
|
|
336
|
+
configured: false,
|
|
337
|
+
emit: noop,
|
|
338
|
+
lastConfigurationPayload: null,
|
|
339
|
+
off: noop,
|
|
340
|
+
on: noop
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
/** Retrieve the URL of the Experience Cloud runtime script from the current page's query parameters or session storage. */
|
|
344
|
+
function getRuntimeScriptUrl() {
|
|
345
|
+
return new URL(globalThis.location.href).searchParams.get("_mr") || globalThis.sessionStorage.getItem("unifiedShellMRScript");
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Loads the Experience Cloud runtime script into the current document.
|
|
349
|
+
* @throws {Error} If the runtime script cannot be loaded due to a missing or invalid script URL.
|
|
350
|
+
*/
|
|
351
|
+
function loadExperienceCloudRuntime() {
|
|
352
|
+
const runtimeScriptUrl = getRuntimeScriptUrl();
|
|
353
|
+
if (!runtimeScriptUrl) throw new Error("Module Runtime: Missing script.");
|
|
354
|
+
const url = new URL(decodeURIComponent(runtimeScriptUrl));
|
|
355
|
+
if (url.protocol !== "https:") throw new Error("Module Runtime: Must be HTTPS.");
|
|
356
|
+
if (!(EXPERIENCE_CLOUD_RUNTIME_HOST_PATTERN.test(url.hostname) || url.hostname === "localhost.corp.adobe.com" || url.hostname.endsWith(".localhost.corp.adobe.com"))) throw new Error("Module Runtime: Invalid domain.");
|
|
357
|
+
if (!url.pathname.endsWith(".js")) throw new Error("Module Runtime: Must be a JavaScript file.");
|
|
358
|
+
globalThis.sessionStorage.setItem("unifiedShellMRScript", url.toString());
|
|
359
|
+
const script = document.createElement("script");
|
|
360
|
+
script.async = true;
|
|
361
|
+
script.src = url.toString();
|
|
362
|
+
script.onload = () => {
|
|
363
|
+
if ("EXC_MR_READY" in globalThis && typeof globalThis.EXC_MR_READY === "function") globalThis.EXC_MR_READY();
|
|
364
|
+
};
|
|
365
|
+
document.head.append(script);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
//#endregion
|
|
369
|
+
//#region source/web/react/auth/lib.ts
|
|
370
|
+
/**
|
|
371
|
+
* Resolves IMS credentials from the Commerce shared context.
|
|
372
|
+
* @param sharedContext - The Commerce shared context, established over the guest connection.
|
|
373
|
+
*/
|
|
374
|
+
function resolveCommerceImsCredentials(sharedContext) {
|
|
375
|
+
const imsToken = sharedContext.get("imsToken");
|
|
376
|
+
const imsOrgId = sharedContext.get("imsOrgId");
|
|
377
|
+
if (!(imsToken && imsOrgId)) return null;
|
|
378
|
+
return {
|
|
379
|
+
imsOrgId,
|
|
380
|
+
imsToken
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Resolves IMS credentials from the Experience Cloud shell configuration.
|
|
385
|
+
* @param shellConfiguration - The Experience Cloud shell configuration, if available.
|
|
386
|
+
*/
|
|
387
|
+
function resolveShellImsCredentials(shellConfiguration) {
|
|
388
|
+
if (!shellConfiguration) return null;
|
|
389
|
+
const { imsToken, imsOrg: imsOrgId } = shellConfiguration;
|
|
390
|
+
return {
|
|
391
|
+
imsOrgId,
|
|
392
|
+
imsToken
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
//#endregion
|
|
397
|
+
//#region source/web/react/centered-progress.tsx
|
|
398
|
+
/**
|
|
399
|
+
* A centered, indeterminate progress indicator used as a Suspense fallback.
|
|
400
|
+
* @param props - The props needed to render the progress indicator.
|
|
401
|
+
*/
|
|
402
|
+
function CenteredProgress(props) {
|
|
403
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
404
|
+
style: {
|
|
405
|
+
alignItems: "center",
|
|
406
|
+
display: "flex",
|
|
407
|
+
justifyContent: "center",
|
|
408
|
+
minHeight: "calc(100dvh - 48px)",
|
|
409
|
+
padding: 24
|
|
410
|
+
},
|
|
411
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spectrum_s2_ProgressCircle.ProgressCircle, {
|
|
412
|
+
"aria-label": props["aria-label"],
|
|
413
|
+
isIndeterminate: true
|
|
414
|
+
})
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
//#endregion
|
|
419
|
+
//#region source/web/react/commerce/hooks/use-guest-connection.ts
|
|
420
|
+
const guestConnections = createRetryablePromiseCache();
|
|
421
|
+
function getGuestConnectionPromise(extensionId) {
|
|
422
|
+
return guestConnections.get(extensionId, () => {
|
|
423
|
+
const promise = (0, _adobe_uix_guest.attach)({ id: extensionId });
|
|
424
|
+
promise.catch((err) => {
|
|
425
|
+
console.error("UIX guest attach failed:", err);
|
|
426
|
+
});
|
|
427
|
+
return promise;
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Suspends until the guest connection for the given extension is established.
|
|
432
|
+
* @param extensionId - The unique identifier for the extension app.
|
|
433
|
+
*/
|
|
434
|
+
function useGuestConnection(extensionId) {
|
|
435
|
+
return (0, react.use)(getGuestConnectionPromise(extensionId));
|
|
436
|
+
}
|
|
437
|
+
/** Drops a failed connection for `extensionId`, so a later render re-attaches. */
|
|
438
|
+
function retryGuestConnection(extensionId) {
|
|
439
|
+
guestConnections.evictIfRejected(extensionId);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
//#endregion
|
|
443
|
+
//#region source/web/react/routing/hooks/use-spectrum-router.ts
|
|
444
|
+
/**
|
|
445
|
+
* Sets up a router instance for an extension app, based on the TanStack React Router example of React Spectrum.
|
|
446
|
+
* @see https://react-spectrum.adobe.com/getting-started
|
|
447
|
+
*/
|
|
448
|
+
function useSpectrumRouter() {
|
|
449
|
+
const router = (0, _tanstack_react_router.useRouter)();
|
|
450
|
+
return (0, react.useMemo)(() => ({
|
|
451
|
+
navigate: (href, options) => {
|
|
452
|
+
if (typeof href === "string") return router.navigate({
|
|
453
|
+
to: getRouteTo(href),
|
|
454
|
+
...options
|
|
455
|
+
});
|
|
456
|
+
return router.navigate({
|
|
457
|
+
...href,
|
|
458
|
+
...options
|
|
459
|
+
});
|
|
460
|
+
},
|
|
461
|
+
useHref: (href) => {
|
|
462
|
+
const toOptions = typeof href === "string" ? { to: getRouteTo(href) } : href;
|
|
463
|
+
return router.buildLocation(toOptions).href;
|
|
464
|
+
}
|
|
465
|
+
}), [router]);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
//#endregion
|
|
469
|
+
//#region source/web/react/theme.ts
|
|
470
|
+
/**
|
|
471
|
+
* Maps host theme values to Spectrum S2 color schemes.
|
|
472
|
+
* @param theme - The theme value provided by the host runtime.
|
|
473
|
+
*/
|
|
474
|
+
function getShellColorScheme(theme) {
|
|
475
|
+
switch (theme) {
|
|
476
|
+
case "dark":
|
|
477
|
+
case "spectrum--darkest": return "dark";
|
|
478
|
+
case "light":
|
|
479
|
+
case "spectrum--lightest": return "light";
|
|
480
|
+
default: return;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Syncs the root `data-color-scheme` attribute used by Spectrum S2 page styles.
|
|
485
|
+
*
|
|
486
|
+
* @param colorScheme - The Spectrum S2 color scheme to apply.
|
|
487
|
+
*/
|
|
488
|
+
function syncRootColorScheme(colorScheme) {
|
|
489
|
+
if (!colorScheme) {
|
|
490
|
+
delete document.documentElement.dataset.colorScheme;
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
document.documentElement.dataset.colorScheme = colorScheme;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
//#endregion
|
|
497
|
+
//#region source/web/react/extension/error-boundary.tsx
|
|
498
|
+
function getErrorMessage(error) {
|
|
499
|
+
if (error instanceof Error) return error.message;
|
|
500
|
+
return typeof error === "string" ? error : "An unexpected error occurred.";
|
|
501
|
+
}
|
|
502
|
+
function ErrorFallback({ error, resetErrorBoundary }) {
|
|
503
|
+
const router = (0, _tanstack_react_router.useRouter)();
|
|
504
|
+
const canGoBack = (0, _tanstack_react_router.useCanGoBack)();
|
|
505
|
+
const goBack = (0, react.useCallback)(() => {
|
|
506
|
+
router.history.back();
|
|
507
|
+
}, [router]);
|
|
508
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
509
|
+
style: {
|
|
510
|
+
alignItems: "center",
|
|
511
|
+
display: "flex",
|
|
512
|
+
justifyContent: "center",
|
|
513
|
+
minHeight: "calc(100dvh - 48px)",
|
|
514
|
+
padding: 24
|
|
515
|
+
},
|
|
516
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_react_spectrum_s2_IllustratedMessage.IllustratedMessage, { children: [
|
|
517
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spectrum_s2_illustrations_linear_Error.default, {}),
|
|
518
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spectrum_s2_IllustratedMessage.Heading, { children: "Something went wrong" }),
|
|
519
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spectrum_s2_IllustratedMessage.Content, { children: getErrorMessage(error) }),
|
|
520
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_react_spectrum_s2_ButtonGroup.ButtonGroup, { children: [canGoBack && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spectrum_s2_ButtonGroup.Button, {
|
|
521
|
+
onPress: goBack,
|
|
522
|
+
variant: "secondary",
|
|
523
|
+
children: "Go back"
|
|
524
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spectrum_s2_ButtonGroup.Button, {
|
|
525
|
+
onPress: resetErrorBoundary,
|
|
526
|
+
variant: "accent",
|
|
527
|
+
children: "Try again"
|
|
528
|
+
})] })
|
|
529
|
+
] })
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* A wrapper component that provides an error boundary for extension apps.
|
|
534
|
+
* It catches JavaScript errors anywhere in its child component tree, logs those errors, and displays a fallback UI.
|
|
535
|
+
*
|
|
536
|
+
* @param props - The props for the component, including children elements and an optional
|
|
537
|
+
* `onReset` callback run before the boundary re-renders its children (via "Try again" or a
|
|
538
|
+
* route change), e.g. to drop cached failures so a retry starts fresh.
|
|
539
|
+
*/
|
|
540
|
+
function ExtensionErrorBoundary(props) {
|
|
541
|
+
const routeKey = (0, _tanstack_react_router.useRouterState)({ select: (state) => state.matches.at(-1)?.pathname });
|
|
542
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_error_boundary.ErrorBoundary, {
|
|
543
|
+
FallbackComponent: ErrorFallback,
|
|
544
|
+
onReset: props.onReset,
|
|
545
|
+
resetKeys: [routeKey],
|
|
546
|
+
children: props.children
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
//#endregion
|
|
551
|
+
//#region source/web/react/extension/commerce-app.tsx
|
|
552
|
+
const controlFrameRegistrations = createRetryablePromiseCache();
|
|
553
|
+
/**
|
|
554
|
+
* Registers a Commerce Admin control frame with the UIX host, once per extension. `register`
|
|
555
|
+
* has no teardown, so this is memoized rather than run inside an effect body every mount.
|
|
556
|
+
*
|
|
557
|
+
* @param extensionId - The unique identifier for the extension app.
|
|
558
|
+
*/
|
|
559
|
+
function registerControlFrame(extensionId) {
|
|
560
|
+
return controlFrameRegistrations.get(extensionId, () => {
|
|
561
|
+
const promise = (0, _adobe_uix_guest.register)({
|
|
562
|
+
id: extensionId,
|
|
563
|
+
methods: {}
|
|
564
|
+
});
|
|
565
|
+
promise.catch((err) => {
|
|
566
|
+
console.error("UIX guest register failed:", err);
|
|
567
|
+
});
|
|
568
|
+
return promise;
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
/** The fallback UI shown when connecting to the Commerce host. */
|
|
572
|
+
function ConnectionFallback() {
|
|
573
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CenteredProgress, { "aria-label": "Connecting to Commerce Admin" });
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Renders the Commerce Admin flow: a control frame only needs to register itself with the UIX
|
|
577
|
+
* host (no visible content), while a UI frame renders the routed extension-point content once
|
|
578
|
+
* its guest connection is established.
|
|
579
|
+
*
|
|
580
|
+
* @param props - The props needed to initialize the extension app.
|
|
581
|
+
*/
|
|
582
|
+
function CommerceExtensionApp(props) {
|
|
583
|
+
const { extensionId } = props;
|
|
584
|
+
const spectrumRouter = useSpectrumRouter();
|
|
585
|
+
const resetConnections = (0, react.useCallback)(() => {
|
|
586
|
+
retryGuestConnection(extensionId);
|
|
587
|
+
retryCommerceHost(extensionId);
|
|
588
|
+
}, [extensionId]);
|
|
589
|
+
(0, react.useEffect)(() => {
|
|
590
|
+
syncRootColorScheme("light");
|
|
591
|
+
}, []);
|
|
592
|
+
if (isControlFrame()) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ControlFrameRegistration, { extensionId });
|
|
593
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spectrum_s2_Provider.Provider, {
|
|
594
|
+
colorScheme: "light",
|
|
595
|
+
router: spectrumRouter,
|
|
596
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExtensionErrorBoundary, {
|
|
597
|
+
onReset: resetConnections,
|
|
598
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, {
|
|
599
|
+
fallback: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConnectionFallback, {}),
|
|
600
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommerceGuestConnection, { extensionId })
|
|
601
|
+
})
|
|
602
|
+
})
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Registers a Commerce Admin control frame with the UIX host. Renders no visible content.
|
|
607
|
+
* @param props - The props needed to initialize the extension app.
|
|
608
|
+
*/
|
|
609
|
+
function ControlFrameRegistration(props) {
|
|
610
|
+
const { extensionId } = props;
|
|
611
|
+
(0, react.useEffect)(() => {
|
|
612
|
+
registerControlFrame(extensionId);
|
|
613
|
+
}, [extensionId]);
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Suspends until the guest connection is established, then provides the Commerce shared context.
|
|
618
|
+
* @param props - The props needed to initialize the extension app.
|
|
619
|
+
*/
|
|
620
|
+
function CommerceGuestConnection(props) {
|
|
621
|
+
const { extensionId } = props;
|
|
622
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SharedContextProvider, {
|
|
623
|
+
extensionId,
|
|
624
|
+
guestConnection: useGuestConnection(extensionId),
|
|
625
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommerceExtensionContent, {})
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
/** Renders IMS-gated route content for a Commerce Admin UI frame. */
|
|
629
|
+
function CommerceExtensionContent() {
|
|
630
|
+
const { sharedContext } = useSharedContext();
|
|
631
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImsContextProvider, {
|
|
632
|
+
credentials: resolveCommerceImsCredentials(sharedContext),
|
|
633
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tanstack_react_router.Outlet, {})
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
//#endregion
|
|
638
|
+
//#region source/web/react/shell/hooks/use-extension-color-scheme.ts
|
|
639
|
+
/**
|
|
640
|
+
* Keeps the Spectrum S2 color scheme aligned with Experience Shell or browser defaults.
|
|
641
|
+
* @param shellConfiguration - The Experience Shell configuration, if available.
|
|
642
|
+
*/
|
|
643
|
+
function useExtensionColorScheme(shellConfiguration) {
|
|
644
|
+
const colorScheme = getShellColorScheme(shellConfiguration?.theme);
|
|
645
|
+
(0, react.useEffect)(() => {
|
|
646
|
+
syncRootColorScheme(colorScheme);
|
|
647
|
+
}, [colorScheme]);
|
|
648
|
+
return colorScheme;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
//#endregion
|
|
652
|
+
//#region source/web/react/shell/hooks/use-shell-configuration.ts
|
|
653
|
+
/**
|
|
654
|
+
* Extracts the shell configuration from the runtime configuration (only the components we want to expose).
|
|
655
|
+
* @param config The runtime configuration object.
|
|
656
|
+
*/
|
|
657
|
+
function extractShellConfiguration(config) {
|
|
658
|
+
const { imsOrg, imsToken, theme } = config;
|
|
659
|
+
return {
|
|
660
|
+
imsOrg,
|
|
661
|
+
imsToken,
|
|
662
|
+
theme
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Derives the exposed shell configuration from the given runtime instance.
|
|
667
|
+
* @param runtime The runtime instance.
|
|
668
|
+
*/
|
|
669
|
+
function useShellConfiguration(runtime, initialConfiguration) {
|
|
670
|
+
const router = (0, _tanstack_react_router.useRouter)();
|
|
671
|
+
const [shellConfiguration, setShellConfiguration] = (0, react.useState)(() => initialConfiguration ? extractShellConfiguration(initialConfiguration) : null);
|
|
672
|
+
(0, react.useEffect)(() => {
|
|
673
|
+
const onConfiguration = (event) => {
|
|
674
|
+
if (event) setShellConfiguration(extractShellConfiguration(event));
|
|
675
|
+
};
|
|
676
|
+
const onHistory = (event) => {
|
|
677
|
+
if (event?.type === "external" && typeof event.path === "string") {
|
|
678
|
+
const to = getRouteTo(event.path);
|
|
679
|
+
if (to !== router.state.location.pathname) router.navigate({ to });
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
runtime.on("configuration", onConfiguration);
|
|
683
|
+
runtime.on("history", onHistory);
|
|
684
|
+
return () => {
|
|
685
|
+
runtime.off("configuration", onConfiguration);
|
|
686
|
+
runtime.off("history", onHistory);
|
|
687
|
+
};
|
|
688
|
+
}, [router, runtime]);
|
|
689
|
+
return shellConfiguration;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
//#endregion
|
|
693
|
+
//#region source/web/react/extension/experience-shell-app.tsx
|
|
694
|
+
/** The fallback UI shown while waiting for the Experience Cloud shell to become ready. */
|
|
695
|
+
function ShellFallback() {
|
|
696
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CenteredProgress, { "aria-label": "Loading experience cloud runtime" });
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Renders the Experience Cloud shell flow, waiting for the shell's runtime configuration.
|
|
700
|
+
* @param props - The props needed to initialize the extension app.
|
|
701
|
+
*/
|
|
702
|
+
function ExperienceShellExtensionApp(props) {
|
|
703
|
+
const { initialConfigurationPromise, runtime } = props;
|
|
704
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spectrum_s2_Provider.Provider, {
|
|
705
|
+
colorScheme: void 0,
|
|
706
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, {
|
|
707
|
+
fallback: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShellFallback, {}),
|
|
708
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShellExtensionContent, {
|
|
709
|
+
initialConfigurationPromise,
|
|
710
|
+
runtime
|
|
711
|
+
})
|
|
712
|
+
})
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Renders IMS-gated route content once the shell/runtime configuration is known.
|
|
717
|
+
* @param props - The props needed to initialize the extension app.
|
|
718
|
+
*/
|
|
719
|
+
function ShellExtensionContent(props) {
|
|
720
|
+
const { initialConfigurationPromise, runtime } = props;
|
|
721
|
+
const shellConfiguration = useShellConfiguration(runtime, (0, react.use)(initialConfigurationPromise));
|
|
722
|
+
const spectrumRouter = useSpectrumRouter();
|
|
723
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spectrum_s2_Provider.Provider, {
|
|
724
|
+
colorScheme: useExtensionColorScheme(shellConfiguration),
|
|
725
|
+
router: spectrumRouter,
|
|
726
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExtensionErrorBoundary, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImsContextProvider, {
|
|
727
|
+
credentials: resolveShellImsCredentials(shellConfiguration),
|
|
728
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tanstack_react_router.Outlet, {})
|
|
729
|
+
}) })
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
//#endregion
|
|
734
|
+
//#region source/web/react/extension/standalone-app.tsx
|
|
735
|
+
/** Renders the raw HTML page running standalone, with no host frame at all. Nothing to wait on. */
|
|
736
|
+
function StandaloneExtensionApp() {
|
|
737
|
+
const spectrumRouter = useSpectrumRouter();
|
|
738
|
+
(0, react.useEffect)(() => {
|
|
739
|
+
syncRootColorScheme(void 0);
|
|
740
|
+
}, []);
|
|
741
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spectrum_s2_Provider.Provider, {
|
|
742
|
+
colorScheme: void 0,
|
|
743
|
+
router: spectrumRouter,
|
|
744
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExtensionErrorBoundary, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImsContextProvider, {
|
|
745
|
+
credentials: null,
|
|
746
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tanstack_react_router.Outlet, {})
|
|
747
|
+
}) })
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
//#endregion
|
|
752
|
+
//#region source/web/react/extension/entrypoint.tsx
|
|
753
|
+
/**
|
|
754
|
+
* The Entrypoint component is the main entry point for an extension app.
|
|
755
|
+
* It picks between the Commerce Admin, Experience Cloud shell, and standalone flows.
|
|
756
|
+
*
|
|
757
|
+
* @param props - The props needed to initialize the extension app.
|
|
758
|
+
*/
|
|
759
|
+
function Entrypoint(props) {
|
|
760
|
+
const { extensionId, initialConfigurationPromise, runtime } = props;
|
|
761
|
+
if (isUiFrame() || isControlFrame()) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommerceExtensionApp, { extensionId });
|
|
762
|
+
if (isEmbeddedInHost() && initialConfigurationPromise !== null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExperienceShellExtensionApp, {
|
|
763
|
+
initialConfigurationPromise,
|
|
764
|
+
runtime
|
|
765
|
+
});
|
|
766
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StandaloneExtensionApp, {});
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
//#endregion
|
|
770
|
+
//#region source/web/react/extension/create-app.tsx
|
|
771
|
+
/**
|
|
772
|
+
* Mounts a Commerce Admin UI iframe app and handles Experience Cloud Shell, UIX
|
|
773
|
+
* registration, shared-context attachment, routing, and Spectrum setup.
|
|
774
|
+
*
|
|
775
|
+
* The app is wrapped in React's `<StrictMode>`, so in development builds (e.g. when
|
|
776
|
+
* served via `aio app dev` or `aio app run`) components render twice and effects run
|
|
777
|
+
* an extra setup + cleanup cycle on mount. Production builds are unaffected.
|
|
778
|
+
*
|
|
779
|
+
* @param options - App bootstrap options.
|
|
780
|
+
*
|
|
781
|
+
* @example
|
|
782
|
+
* ```tsx
|
|
783
|
+
* import { createExtensionApp } from "@adobe/aio-commerce-lib-admin-ui/web";
|
|
784
|
+
* import { MainPage } from "./pages/main-page.jsx";
|
|
785
|
+
*
|
|
786
|
+
* createExtensionApp({
|
|
787
|
+
* metadata: { extensionId: "my-extension-id" },
|
|
788
|
+
* routes: [{ index: true, element: <MainPage /> }],
|
|
789
|
+
* });
|
|
790
|
+
* ```
|
|
791
|
+
*/
|
|
792
|
+
function createExtensionApp({ metadata, routes, root: customRoot }) {
|
|
793
|
+
const rootElement = customRoot ?? document.getElementById("root");
|
|
794
|
+
if (!rootElement) throw new Error("Could not find an element with id \"root\".");
|
|
795
|
+
const root = (0, react_dom_client.createRoot)(rootElement);
|
|
796
|
+
const render = (runtime, initialConfigurationPromise) => {
|
|
797
|
+
const router = createExtensionRouter(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Entrypoint, {
|
|
798
|
+
extensionId: metadata.extensionId,
|
|
799
|
+
initialConfigurationPromise,
|
|
800
|
+
runtime
|
|
801
|
+
}), routes);
|
|
802
|
+
root.render(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.StrictMode, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tanstack_react_router.RouterProvider, { router }) }));
|
|
803
|
+
};
|
|
804
|
+
try {
|
|
805
|
+
loadExperienceCloudRuntime();
|
|
806
|
+
(0, _adobe_exc_app.init)(() => {
|
|
807
|
+
const runtime = (0, _adobe_exc_app.default)();
|
|
808
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
809
|
+
_adobe_exc_app_page_js.default.title = document.title;
|
|
810
|
+
runtime.on("ready", (configuration) => {
|
|
811
|
+
resolve(configuration ?? runtime.lastConfigurationPayload);
|
|
812
|
+
_adobe_exc_app_page_js.default.done().catch(() => {
|
|
813
|
+
console.warn("Failed to mark page as done in Experience Cloud Shell.");
|
|
814
|
+
});
|
|
815
|
+
});
|
|
816
|
+
render(runtime, promise);
|
|
817
|
+
});
|
|
818
|
+
} catch {
|
|
819
|
+
render(createMockRuntime(), null);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
//#endregion
|
|
824
|
+
exports.createExtensionApp = createExtensionApp;
|
|
825
|
+
exports.useCommerce = useCommerce;
|
|
826
|
+
exports.useHostConnection = useHostConnection;
|
|
827
|
+
exports.useIms = useIms;
|
|
828
|
+
exports.useMassActionContext = useMassActionContext;
|
|
829
|
+
exports.useOrderViewButtonContext = useOrderViewButtonContext;
|
|
830
|
+
exports.useSharedContext = useSharedContext;
|