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