@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.
Files changed (40) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +1 -1
  3. package/dist/cjs/acl-resource-id-DBlYU0DE.d.cts +39 -0
  4. package/dist/cjs/acl-resource-id-D_hCU1Qg.cjs +69 -0
  5. package/dist/cjs/api/index.cjs +252 -0
  6. package/dist/cjs/api/index.d.cts +126 -0
  7. package/dist/cjs/grid-columns/index.cjs +151 -0
  8. package/dist/cjs/grid-columns/index.d.cts +146 -0
  9. package/dist/cjs/mass-actions/index.cjs +188 -0
  10. package/dist/cjs/mass-actions/index.d.cts +160 -0
  11. package/dist/cjs/menu/index.cjs +91 -0
  12. package/dist/cjs/menu/index.d.cts +63 -0
  13. package/dist/cjs/order-view-buttons/index.cjs +126 -0
  14. package/dist/cjs/order-view-buttons/index.d.cts +113 -0
  15. package/dist/cjs/rolldown-runtime-Cx6hovH8.cjs +67 -0
  16. package/dist/cjs/schemas-Ce10uBzN.cjs +41 -0
  17. package/dist/cjs/utils-B59fjd_w.cjs +39 -0
  18. package/dist/cjs/web/index.cjs +830 -0
  19. package/dist/cjs/web/index.d.cts +192 -0
  20. package/dist/es/acl-resource-id-DBlYU0DE.d.mts +39 -0
  21. package/dist/es/acl-resource-id-pryVxI_c.mjs +57 -0
  22. package/dist/es/api/index.d.mts +126 -0
  23. package/dist/es/api/index.mjs +262 -0
  24. package/dist/es/grid-columns/index.d.mts +146 -0
  25. package/dist/es/grid-columns/index.mjs +143 -0
  26. package/dist/es/mass-actions/index.d.mts +160 -0
  27. package/dist/es/mass-actions/index.mjs +178 -0
  28. package/dist/es/menu/index.d.mts +63 -0
  29. package/dist/es/menu/index.mjs +80 -0
  30. package/dist/es/order-view-buttons/index.d.mts +113 -0
  31. package/dist/es/order-view-buttons/index.mjs +119 -0
  32. package/dist/es/schemas-BFT8ys8P.mjs +34 -0
  33. package/dist/es/utils-COPGW1HO.mjs +32 -0
  34. package/dist/es/web/index.d.mts +192 -0
  35. package/dist/es/web/index.mjs +819 -0
  36. package/package.json +87 -10
  37. package/dist/cjs/index.cjs +0 -139
  38. package/dist/cjs/index.d.cts +0 -56
  39. package/dist/es/index.d.mts +0 -56
  40. 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;