@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.
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 +872 -0
  19. package/dist/cjs/web/index.d.cts +206 -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 +206 -0
  35. package/dist/es/web/index.mjs +861 -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,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;