@fluid-app/portal-sdk 0.1.437 → 0.1.438

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,19 +1,15 @@
1
1
  import "./translation-adapter-BTEUT4wV.mjs";
2
- import "./build-widget-registry-DR6xEwnq.mjs";
3
2
  import "./transforms-D8tNbtFA.mjs";
4
3
  import "./FluidProvider-BmR0_BHr.mjs";
5
4
  import "./use-account-vIWrrngf.mjs";
6
5
  import "./use-store-BH0GPS-f.mjs";
7
6
  import "./mysite-api-context-BbxSfNft.mjs";
8
7
  import "./ContactsTranslationBridge-XnneGT_P.mjs";
9
- import "./FluidThemeProvider-mIKhJTlY.mjs";
10
- import "./PortalTenantClientProvider-Bms_pfpm.mjs";
11
8
  import "./static-dict-adapter-qxjr0Wsy.mjs";
12
9
  import "./translation-api-context-factory-DFr9yJ6Q.mjs";
13
10
  import "./shell-translation-api-context-Cxetel1o.mjs";
14
11
  import "./translation-adapter-CnS_Wly3.mjs";
15
12
  import "./translation-adapter-DE6ZYd_c.mjs";
16
- import "./AppNavigationContext-sWuBzay1.mjs";
17
13
  import "./ScreenHeaderContext-BCGcBC1Q.mjs";
18
14
  import "./src-ecrGJpIO.mjs";
19
15
  import { n as messagingScreenPropertySchema, t as MessagingScreen } from "./MessagingScreen-DkYf-0Yo.mjs";
@@ -1,19 +1,15 @@
1
1
  import "./translation-adapter-BTEUT4wV.mjs";
2
- import "./build-widget-registry-DR6xEwnq.mjs";
3
2
  import "./transforms-D8tNbtFA.mjs";
4
3
  import "./FluidProvider-BmR0_BHr.mjs";
5
4
  import "./use-account-vIWrrngf.mjs";
6
5
  import "./use-store-BH0GPS-f.mjs";
7
6
  import "./mysite-api-context-BbxSfNft.mjs";
8
7
  import "./ContactsTranslationBridge-XnneGT_P.mjs";
9
- import "./FluidThemeProvider-mIKhJTlY.mjs";
10
- import "./PortalTenantClientProvider-Bms_pfpm.mjs";
11
8
  import "./static-dict-adapter-qxjr0Wsy.mjs";
12
9
  import "./translation-api-context-factory-DFr9yJ6Q.mjs";
13
10
  import "./shell-translation-api-context-Cxetel1o.mjs";
14
11
  import "./translation-adapter-CnS_Wly3.mjs";
15
12
  import "./translation-adapter-DE6ZYd_c.mjs";
16
- import "./AppNavigationContext-sWuBzay1.mjs";
17
13
  import "./ScreenHeaderContext-BCGcBC1Q.mjs";
18
14
  import "./src-ecrGJpIO.mjs";
19
15
  import "./display-XGjysdr1.mjs";
@@ -1,19 +1,15 @@
1
1
  import "./translation-adapter-BTEUT4wV.mjs";
2
- import "./build-widget-registry-DR6xEwnq.mjs";
3
2
  import "./transforms-D8tNbtFA.mjs";
4
3
  import "./FluidProvider-BmR0_BHr.mjs";
5
4
  import "./use-account-vIWrrngf.mjs";
6
5
  import "./use-store-BH0GPS-f.mjs";
7
6
  import "./mysite-api-context-BbxSfNft.mjs";
8
7
  import "./ContactsTranslationBridge-XnneGT_P.mjs";
9
- import "./FluidThemeProvider-mIKhJTlY.mjs";
10
- import "./PortalTenantClientProvider-Bms_pfpm.mjs";
11
8
  import "./static-dict-adapter-qxjr0Wsy.mjs";
12
9
  import "./translation-api-context-factory-DFr9yJ6Q.mjs";
13
10
  import "./shell-translation-api-context-Cxetel1o.mjs";
14
11
  import "./translation-adapter-CnS_Wly3.mjs";
15
12
  import "./translation-adapter-DE6ZYd_c.mjs";
16
- import "./AppNavigationContext-sWuBzay1.mjs";
17
13
  import "./ScreenHeaderContext-BCGcBC1Q.mjs";
18
14
  import "./src-ecrGJpIO.mjs";
19
15
  import "./use-portal-card-add-3ds-flow-d8VVK8_k.mjs";
@@ -2,11 +2,9 @@ import "./translation-adapter-BTEUT4wV.mjs";
2
2
  import "./use-account-vIWrrngf.mjs";
3
3
  import "./use-store-BH0GPS-f.mjs";
4
4
  import "./mysite-api-context-BbxSfNft.mjs";
5
- import "./PortalTenantClientProvider-Bms_pfpm.mjs";
6
5
  import "./static-dict-adapter-qxjr0Wsy.mjs";
7
6
  import { n as shareablesScreenPropertySchema, t as ShareablesScreen } from "./ShareablesScreen-CuBfB922.mjs";
8
7
  import "./translation-api-context-factory-DFr9yJ6Q.mjs";
9
- import "./AppNavigationContext-sWuBzay1.mjs";
10
8
  import "./ScreenHeaderContext-BCGcBC1Q.mjs";
11
9
  import "./src-ecrGJpIO.mjs";
12
10
  import "./use-mysite-portal-CARrubVw.mjs";
@@ -1,19 +1,15 @@
1
1
  import "./translation-adapter-BTEUT4wV.mjs";
2
- import "./build-widget-registry-DR6xEwnq.mjs";
3
2
  import "./transforms-D8tNbtFA.mjs";
4
3
  import "./FluidProvider-BmR0_BHr.mjs";
5
4
  import "./use-account-vIWrrngf.mjs";
6
5
  import "./use-store-BH0GPS-f.mjs";
7
6
  import "./mysite-api-context-BbxSfNft.mjs";
8
7
  import "./ContactsTranslationBridge-XnneGT_P.mjs";
9
- import "./FluidThemeProvider-mIKhJTlY.mjs";
10
- import "./PortalTenantClientProvider-Bms_pfpm.mjs";
11
8
  import "./static-dict-adapter-qxjr0Wsy.mjs";
12
9
  import "./translation-api-context-factory-DFr9yJ6Q.mjs";
13
10
  import "./shell-translation-api-context-Cxetel1o.mjs";
14
11
  import "./translation-adapter-CnS_Wly3.mjs";
15
12
  import "./translation-adapter-DE6ZYd_c.mjs";
16
- import "./AppNavigationContext-sWuBzay1.mjs";
17
13
  import "./ScreenHeaderContext-BCGcBC1Q.mjs";
18
14
  import "./src-ecrGJpIO.mjs";
19
15
  import { r as shopScreenPropertySchema, t as ShopScreen } from "./ShopScreen-5j2kJ60N.mjs";
@@ -1,19 +1,15 @@
1
1
  import "./translation-adapter-BTEUT4wV.mjs";
2
- import "./build-widget-registry-DR6xEwnq.mjs";
3
2
  import "./transforms-D8tNbtFA.mjs";
4
3
  import "./FluidProvider-BmR0_BHr.mjs";
5
4
  import "./use-account-vIWrrngf.mjs";
6
5
  import "./use-store-BH0GPS-f.mjs";
7
6
  import "./mysite-api-context-BbxSfNft.mjs";
8
7
  import "./ContactsTranslationBridge-XnneGT_P.mjs";
9
- import "./FluidThemeProvider-mIKhJTlY.mjs";
10
- import "./PortalTenantClientProvider-Bms_pfpm.mjs";
11
8
  import "./static-dict-adapter-qxjr0Wsy.mjs";
12
9
  import "./translation-api-context-factory-DFr9yJ6Q.mjs";
13
10
  import "./shell-translation-api-context-Cxetel1o.mjs";
14
11
  import "./translation-adapter-CnS_Wly3.mjs";
15
12
  import "./translation-adapter-DE6ZYd_c.mjs";
16
- import "./AppNavigationContext-sWuBzay1.mjs";
17
13
  import "./ScreenHeaderContext-BCGcBC1Q.mjs";
18
14
  import "./src-ecrGJpIO.mjs";
19
15
  import { n as subscriptionsScreenPropertySchema, t as SubscriptionsScreen } from "./SubscriptionsScreen-CHTlfIC_.mjs";
@@ -184,4 +184,4 @@ function isActivitySlug(value) {
184
184
  //#endregion
185
185
  export { isErrorResult as a, selectProperty as c, useAppDefinition as d, isActivitySlug as i, useFluidTheme as l, getProperty as n, isIdle as o, hasData as r, isLoading as s, ACTIVITY_SLUGS as t, APP_DEFINITION_QUERY_KEY as u };
186
186
 
187
- //# sourceMappingURL=property-schema-types-DJgFKHAG.mjs.map
187
+ //# sourceMappingURL=hook-types-D5HQXU4S.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-types-D5HQXU4S.mjs","names":[],"sources":["../src/hooks/use-app-definition.ts","../src/hooks/use-fluid-theme.ts","../src/hooks/hook-types.ts"],"sourcesContent":["import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useAppDefinitionApi } from \"@fluid-app/portal-core/app-definition-api-context\";\nimport type { AppDefinitionResponse } from \"@fluid-app/portal-core/app-definition-types\";\nimport { useCompanyScopedQueryKey } from \"./query-keys\";\n\n/**\n * Base query key for the app definition endpoint.\n * The runtime key includes a company prefix via {@link useCompanyScopedQueryKey}.\n */\nexport const APP_DEFINITION_QUERY_KEY = [\"fluid\", \"app-definition\"] as const;\n\n/**\n * Hook to fetch the active app manifest from the portal-tenant BFF.\n *\n * Returns the manifest metadata (id, name, version)\n * sourced from `GET /api/app/manifest` via the {@link AppDefinitionApi} port.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { data } = useAppDefinition();\n * const definitionId = data?.definition.id ?? 0;\n * }\n * ```\n */\nexport function useAppDefinition(options?: {\n enabled?: boolean;\n}): UseQueryResult<AppDefinitionResponse> {\n const api = useAppDefinitionApi();\n const { scopeKey } = useCompanyScopedQueryKey();\n\n return useQuery({\n queryKey: scopeKey(APP_DEFINITION_QUERY_KEY),\n queryFn: () => api.fetchDefinition(),\n ...(options?.enabled !== undefined && { enabled: options.enabled }),\n });\n}\n","import { useThemeContext } from \"../providers/FluidThemeProvider\";\nimport type { ThemeDefinition } from \"../types/theme\";\n\n/**\n * Result of useFluidTheme hook\n */\nexport interface UseFluidThemeResult {\n /** Currently active theme */\n currentTheme: ThemeDefinition | null;\n /** Switch to a different theme */\n setTheme: (theme: ThemeDefinition) => void;\n /** Switch between light and dark mode */\n setThemeMode: (mode: \"light\" | \"dark\") => void;\n /** Current theme mode (convenience accessor) */\n mode: \"light\" | \"dark\" | undefined;\n}\n\n/**\n * Hook to access and control theme settings\n *\n * @example\n * ```tsx\n * function ThemeSwitcher({ themes }: { themes: ThemeDefinition[] }) {\n * const { currentTheme, setTheme, setThemeMode, mode } = useFluidTheme();\n *\n * return (\n * <div>\n * <select\n * value={currentTheme?.name}\n * onChange={(e) => {\n * const theme = themes.find(t => t.name === e.target.value);\n * if (theme) setTheme(theme);\n * }}\n * >\n * {themes.map(theme => (\n * <option key={theme.name} value={theme.name}>\n * {theme.name}\n * </option>\n * ))}\n * </select>\n *\n * <button onClick={() => setThemeMode(mode === \"dark\" ? \"light\" : \"dark\")}>\n * Toggle {mode === \"dark\" ? \"Light\" : \"Dark\"} Mode\n * </button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useFluidTheme(): UseFluidThemeResult {\n const { currentTheme, setTheme, setThemeMode, mode } = useThemeContext();\n\n return {\n currentTheme,\n setTheme,\n setThemeMode,\n mode,\n };\n}\n","/**\n * Hook type utilities and type predicates.\n *\n * This module provides:\n * - Generic hook result types with default type parameters\n * - Type predicates for query state narrowing\n * - Reusable patterns for type-safe property access in hooks\n *\n * Following generics best practices:\n * - generics-default-type-parameters: Default E to Error for common case\n * - generics-type-predicates: Type predicates for result state narrowing\n * - generics-constrain-type-parameters: K extends keyof T for property access\n */\n\n// =============================================================================\n// Base Result Types with Default Type Parameters\n// =============================================================================\n\n/**\n * Base result type for query hooks with default error type.\n * Uses default type parameter for E (generics-default-type-parameters rule).\n *\n * @typeParam T - The data type\n * @typeParam E - The error type (defaults to Error)\n *\n * @example\n * // Error type defaults to Error\n * type UsersResult = QueryResult<User[]>;\n *\n * // Can override when needed\n * type CustomResult = QueryResult<User[], ApiError>;\n */\nexport interface QueryResult<T, E = Error> {\n readonly data: T;\n readonly isLoading: boolean;\n readonly isError: boolean;\n readonly error?: E | undefined;\n}\n\n/**\n * Result type for hooks that may not have data yet.\n * Extends QueryResult with nullable data.\n *\n * @typeParam T - The data type\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface QueryResultNullable<T, E = Error> {\n readonly data: T | null | undefined;\n readonly isLoading: boolean;\n readonly isError: boolean;\n readonly error?: E | undefined;\n}\n\n/**\n * Result type for list/collection hooks with aggregates.\n *\n * @typeParam T - The item type in the array\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface ListQueryResult<T, E = Error> extends QueryResult<T[], E> {\n readonly totalCount: number;\n}\n\n/**\n * Result type for list hooks with value aggregation (e.g., deals with total value).\n *\n * @typeParam T - The item type in the array\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface ValueListQueryResult<T, E = Error> extends ListQueryResult<\n T,\n E\n> {\n readonly totalValue: number;\n}\n\n// =============================================================================\n// Type Predicates for Query State Narrowing\n// =============================================================================\n\n/**\n * Type predicate to check if a query result has successfully loaded data.\n * Narrows the data type from T | null | undefined to T.\n *\n * @example\n * const result = useContact(id);\n * if (hasData(result)) {\n * // TypeScript knows result.data is Contact, not Contact | null\n * console.log(result.data.name);\n * }\n */\nexport function hasData<T, E = Error>(\n result: QueryResultNullable<T, E>,\n): result is QueryResultNullable<T, E> & { readonly data: T } {\n return result.data != null && !result.isLoading && !result.isError;\n}\n\n/**\n * Type predicate to check if a query result is in loading state.\n * Useful for conditional rendering.\n *\n * @example\n * if (isLoading(result)) {\n * return <Spinner />;\n * }\n */\nexport function isLoading<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): boolean {\n return result.isLoading;\n}\n\n/**\n * Type predicate to check if a query result has an error.\n * Narrows to include the error property.\n *\n * @example\n * if (isErrorResult(result)) {\n * console.error(result.error); // error is E, not undefined\n * }\n */\nexport function isErrorResult<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): result is (QueryResult<T, E> | QueryResultNullable<T, E>) & {\n readonly isError: true;\n readonly error: E;\n} {\n return result.isError && result.error !== undefined;\n}\n\n/**\n * Type predicate to check if a query result is in idle state (not loading, no error, has data).\n *\n * @example\n * if (isIdle(result)) {\n * // Safe to access and render data\n * }\n */\nexport function isIdle<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): boolean {\n return !result.isLoading && !result.isError;\n}\n\n// =============================================================================\n// Generic Property Selector Pattern\n// =============================================================================\n\n/**\n * Type-safe property selector for hook results.\n * Uses K extends keyof T constraint (generics-function-constraints rule).\n *\n * @typeParam T - The data object type\n * @typeParam K - Key of T (constrained to actual keys)\n *\n * @example\n * const users = [{ name: \"Alice\", age: 30 }];\n * const names = selectProperty(users, \"name\"); // string[]\n * const ages = selectProperty(users, \"age\"); // number[]\n * selectProperty(users, \"invalid\"); // Error: \"invalid\" not in \"name\" | \"age\"\n */\nexport function selectProperty<T, K extends keyof T>(\n items: readonly T[],\n key: K,\n): T[K][] {\n return items.map((item) => item[key]);\n}\n\n/**\n * Type-safe property getter for a single item.\n * Returns undefined if item is null/undefined.\n *\n * @typeParam T - The data object type\n * @typeParam K - Key of T (constrained to actual keys)\n */\nexport function getProperty<T, K extends keyof T>(\n item: T | null | undefined,\n key: K,\n): T[K] | undefined {\n return item?.[key];\n}\n\n// =============================================================================\n// Hook Factory Types\n// =============================================================================\n\n/**\n * Generic type for hooks that fetch a single resource by ID.\n * Useful for creating consistent API across different resource types.\n *\n * @typeParam T - The resource type\n * @typeParam E - The error type (defaults to Error)\n */\nexport type UseSingleResourceHook<T, E = Error> = (\n id: string,\n) => QueryResultNullable<T, E>;\n\n/**\n * Generic type for hooks that fetch a list of resources with optional params.\n * Uses generics-default-type-parameters for the params type.\n *\n * @typeParam T - The item type\n * @typeParam P - The params type (defaults to empty object)\n * @typeParam E - The error type (defaults to Error)\n */\nexport type UseListResourceHook<\n T,\n P extends Record<string, unknown> = Record<string, never>,\n E = Error,\n> = (params?: P) => ListQueryResult<T, E>;\n\n/**\n * Transforms a nullable result to a non-nullable one if data exists.\n * Useful when you've already checked hasData().\n */\nexport type WithData<R extends QueryResultNullable<unknown>> =\n R extends QueryResultNullable<infer T, infer E>\n ? QueryResultNullable<T, E> & { readonly data: T }\n : never;\n\n// =============================================================================\n// Domain Types\n// Shared between hook files and demo-data files to avoid circular imports.\n// =============================================================================\n\n/**\n * Activity slug constants as a const object.\n * Derive the ActivitySlug type from this single source of truth.\n */\nexport const ACTIVITY_SLUGS = {\n abandonedCart: \"abandoned_cart\",\n announcements: \"announcements\",\n cartItemsAdded: \"cart_items_added\",\n commentReply: \"comment_reply\",\n directMessage: \"direct_message\",\n fantasyPoint: \"fantasy_point\",\n newLead: \"new_lead\",\n orderPlaced: \"order_placed\",\n pageViews: \"page_views\",\n pageViewsContact: \"page_views_contact\",\n tasks: \"tasks\",\n upcomingEvent: \"upcoming_event\",\n video: \"video\",\n videoComplete: \"video_complete\",\n videoCompleteContact: \"video_complete_contact\",\n videoContact: \"video_contact\",\n messageReceived: \"message_received\",\n messageSent: \"message_sent\",\n newCartItemsAdded: \"new_cart_items_added\",\n smartLinkClicked: \"smart_link_clicked\",\n reviewLeft: \"review_left\",\n} as const;\n\n/** Activity slug union type derived from ACTIVITY_SLUGS constant. */\nexport type ActivitySlug = (typeof ACTIVITY_SLUGS)[keyof typeof ACTIVITY_SLUGS];\n\n/** Type predicate to check if a string is a valid ActivitySlug. */\nexport function isActivitySlug(value: string): value is ActivitySlug {\n return Object.values(ACTIVITY_SLUGS).includes(value as ActivitySlug);\n}\n\n/** Transformed activity for display. */\nexport interface Activity {\n readonly id: number;\n readonly userName: string;\n readonly avatarUrl: string | null;\n readonly activityType: string;\n readonly targetName: string;\n readonly timestamp: string;\n readonly slug: ActivitySlug;\n}\n\n/** Description/rich text metadata for a calendar event. */\nexport interface CalendarEventDescription {\n readonly id?: number | null;\n readonly name?: string | null;\n readonly body?: string | null;\n readonly record_type?: string | null;\n readonly record_id?: number | null;\n readonly created_at?: string | null;\n readonly updated_at?: string | null;\n readonly locale?: string | null;\n}\n\n/** Calendar event data from the API. */\nexport interface CalendarEvent {\n readonly id: number;\n readonly title: string;\n readonly description?: CalendarEventDescription | null;\n readonly color?: string | null;\n readonly url?: string | null;\n readonly start: string;\n readonly end: string;\n readonly active?: boolean | null;\n readonly time_zone?: string | null;\n readonly status?: string | null;\n readonly image_url?: string | null;\n readonly images?: readonly unknown[] | null;\n readonly venue?: string | null;\n readonly countries?: readonly string[] | null;\n readonly hasTomorrow?: boolean | null;\n readonly hasYesterday?: boolean | null;\n readonly isAllDay?: boolean;\n}\n\n/** Catch up suggestion data from the API. */\nexport interface CatchUp {\n readonly id: number;\n readonly suggestion_title: string;\n}\n\n/** MySite data returned by the hook. */\nexport interface MySiteData {\n readonly url: string | null;\n readonly views: number;\n readonly leads: number;\n readonly userName: string;\n}\n\n/** Transformed todo for display. */\nexport interface Todo {\n readonly id: number;\n readonly body: string;\n readonly dueAt: string | null;\n readonly completedAt: string | null;\n readonly createdAt: string;\n readonly contactName: string | null;\n}\n"],"mappings":";;;;;;;;AASA,MAAa,2BAA2B,CAAC,SAAS,iBAAiB;;;;;;;;;;;;;;;AAgBnE,SAAgB,iBAAiB,SAES;CACxC,MAAM,MAAM,qBAAqB;CACjC,MAAM,EAAE,aAAa,0BAA0B;AAE/C,QAAO,SAAS;EACd,UAAU,SAAS,yBAAyB;EAC5C,eAAe,IAAI,iBAAiB;EACpC,GAAI,SAAS,YAAY,KAAA,KAAa,EAAE,SAAS,QAAQ,SAAS;EACnE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACcJ,SAAgB,gBAAqC;CACnD,MAAM,EAAE,cAAc,UAAU,cAAc,SAAS,iBAAiB;AAExE,QAAO;EACL;EACA;EACA;EACA;EACD;;;;;;;;;;;;;;;ACkCH,SAAgB,QACd,QAC4D;AAC5D,QAAO,OAAO,QAAQ,QAAQ,CAAC,OAAO,aAAa,CAAC,OAAO;;;;;;;;;;;AAY7D,SAAgB,UACd,QACS;AACT,QAAO,OAAO;;;;;;;;;;;AAYhB,SAAgB,cACd,QAIA;AACA,QAAO,OAAO,WAAW,OAAO,UAAU,KAAA;;;;;;;;;;AAW5C,SAAgB,OACd,QACS;AACT,QAAO,CAAC,OAAO,aAAa,CAAC,OAAO;;;;;;;;;;;;;;;AAoBtC,SAAgB,eACd,OACA,KACQ;AACR,QAAO,MAAM,KAAK,SAAS,KAAK,KAAK;;;;;;;;;AAUvC,SAAgB,YACd,MACA,KACkB;AAClB,QAAO,OAAO;;;;;;AAkDhB,MAAa,iBAAiB;CAC5B,eAAe;CACf,eAAe;CACf,gBAAgB;CAChB,cAAc;CACd,eAAe;CACf,cAAc;CACd,SAAS;CACT,aAAa;CACb,WAAW;CACX,kBAAkB;CAClB,OAAO;CACP,eAAe;CACf,OAAO;CACP,eAAe;CACf,sBAAsB;CACtB,cAAc;CACd,iBAAiB;CACjB,aAAa;CACb,mBAAmB;CACnB,kBAAkB;CAClB,YAAY;CACb;;AAMD,SAAgB,eAAe,OAAsC;AACnE,QAAO,OAAO,OAAO,eAAe,CAAC,SAAS,MAAsB"}
package/dist/index.mjs CHANGED
@@ -24,8 +24,8 @@ import { i as useMessagingAuth, n as messagingScreenPropertySchema, r as useMess
24
24
  import { n as subscriptionsScreenPropertySchema, t as SubscriptionsScreen } from "./SubscriptionsScreen-CHTlfIC_.mjs";
25
25
  import { a as FallbackCard, i as CoreScreenPlaceholder, r as customersScreenPropertySchema, t as CustomersScreen } from "./CustomersScreen-BqM-rDhz.mjs";
26
26
  import { a as memberTypeSlug, i as memberTypeName, n as deniesRepSurfaces, r as hasMemberPermission, t as canAccessRepSurfaces } from "./member-permissions-C_VhSum3.mjs";
27
- import { a as loadWidgetPackages, c as getDefaultWidgetPackageManifestUrl, d as useFluidProfile, f as createDefaultFluidConfig, i as installAndReadFluidWidgetsRegistry, l as useCompanySwitch, n as resolveHostedWidgetPackageManifestUrl, o as subscribeToFluidWidgetsRegistry, p as useLogout, r as fetchWidgetPackageDescriptors, t as dedupeWidgetPackagesByPackageIdAndVersion, u as PROFILE_QUERY_KEY } from "./widget-package-utils-4Jm9jLbe.mjs";
28
- import { a as isErrorResult, c as selectProperty, d as useAppDefinition, i as isActivitySlug, l as useFluidTheme, n as getProperty, o as isIdle, r as hasData, s as isLoading, t as ACTIVITY_SLUGS, u as APP_DEFINITION_QUERY_KEY } from "./property-schema-types-DJgFKHAG.mjs";
27
+ import { a as loadWidgetPackages, c as getDefaultWidgetPackageManifestUrl, d as useFluidProfile, f as createDefaultFluidConfig, i as installAndReadFluidWidgetsRegistry, l as useCompanySwitch, n as resolveHostedWidgetPackageManifestUrl, o as subscribeToFluidWidgetsRegistry, p as useLogout, r as fetchWidgetPackageDescriptors, t as dedupeWidgetPackagesByPackageIdAndVersion, u as PROFILE_QUERY_KEY } from "./widget-package-utils-DjEsgclS.mjs";
28
+ import { a as isErrorResult, c as selectProperty, d as useAppDefinition, i as isActivitySlug, l as useFluidTheme, n as getProperty, o as isIdle, r as hasData, s as isLoading, t as ACTIVITY_SLUGS, u as APP_DEFINITION_QUERY_KEY } from "./hook-types-D5HQXU4S.mjs";
29
29
  import { a as groupPropertyFields, i as extractPropertyValues, n as PROPERTY_FIELD_TYPES, o as isPropertyFieldType, r as applyPropertyValues, t as gapValues } from "./registries-B_LFR-8_.mjs";
30
30
  import { c as sourceWidgetPackagesToManifests, i as isSourceWidgetPackage, n as defineWidgetPackage, t as defineWidget } from "./authoring-CqJmyKw5.mjs";
31
31
  import { a as implementPortalFunction } from "./portal-function-CALnUDmM.mjs";
@@ -2491,14 +2491,14 @@ function resolveMemberTypePage(registry, memberTypeSlug, currentSlug, baseSlug)
2491
2491
  }
2492
2492
  //#endregion
2493
2493
  //#region src/shell/system-screen-map.ts
2494
- const ProfileScreen$1 = lazy(() => import("./ProfileScreen-BYHAQYpZ.mjs").then((m) => ({ default: m.ProfileScreen })));
2495
- const OrdersScreen$1 = lazy(() => import("./OrdersScreen-Cw61Avcj.mjs").then((m) => ({ default: m.OrdersScreen })));
2496
- const SubscriptionsScreen$1 = lazy(() => import("./SubscriptionsScreen-CcMMlP7h.mjs").then((m) => ({ default: m.SubscriptionsScreen })));
2497
- const MessagingScreen$1 = lazy(() => import("./MessagingScreen-Dio6B247.mjs").then((m) => ({ default: m.MessagingScreen })));
2494
+ const ProfileScreen$1 = lazy(() => import("./ProfileScreen-Csfa6rJx.mjs").then((m) => ({ default: m.ProfileScreen })));
2495
+ const OrdersScreen$1 = lazy(() => import("./OrdersScreen-a7S3d8a1.mjs").then((m) => ({ default: m.OrdersScreen })));
2496
+ const SubscriptionsScreen$1 = lazy(() => import("./SubscriptionsScreen-T-AdATEO.mjs").then((m) => ({ default: m.SubscriptionsScreen })));
2497
+ const MessagingScreen$1 = lazy(() => import("./MessagingScreen-FVQv8eTt.mjs").then((m) => ({ default: m.MessagingScreen })));
2498
2498
  const ContactsScreen$1 = lazy(() => import("./ContactsScreen-DJX1UWHR.mjs").then((n) => n.n).then((m) => ({ default: m.ContactsScreen })));
2499
- const ShopScreen$1 = lazy(() => import("./ShopScreen-B10Gydzu.mjs").then((m) => ({ default: m.ShopScreen })));
2499
+ const ShopScreen$1 = lazy(() => import("./ShopScreen-BwZjrAys.mjs").then((m) => ({ default: m.ShopScreen })));
2500
2500
  const CustomersScreen$1 = lazy(() => import("./CustomersScreen-BqM-rDhz.mjs").then((n) => n.n).then((m) => ({ default: m.CustomersScreen })));
2501
- const ShareablesScreen$1 = lazy(() => import("./ShareablesScreen-Bz9tKI_6.mjs").then((m) => ({ default: m.ShareablesScreen })));
2501
+ const ShareablesScreen$1 = lazy(() => import("./ShareablesScreen-BNv3xHry.mjs").then((m) => ({ default: m.ShareablesScreen })));
2502
2502
  const MySiteScreen$1 = lazy(() => import("./MySiteScreen-D3AA3Zti.mjs").then((n) => n.n).then((m) => ({ default: m.MySiteScreen })));
2503
2503
  const AppDownloadScreen = lazy(() => import("./AppDownloadScreen-DSH6LlYf.mjs").then((n) => n.t).then((m) => ({ default: m.AppDownloadScreen })));
2504
2504
  const SYSTEM_SLUG_SCREEN_MAP = {
@@ -4212,15 +4212,15 @@ const CORE_PAGE_IDS = {
4212
4212
  //#endregion
4213
4213
  //#region src/screens/index.ts
4214
4214
  const screenPropertySchemas = {
4215
- ProfileScreen: () => import("./ProfileScreen-BYHAQYpZ.mjs").then((m) => m.profileScreenPropertySchema),
4216
- MessagingScreen: () => import("./MessagingScreen-Dio6B247.mjs").then((m) => m.messagingScreenPropertySchema),
4215
+ ProfileScreen: () => import("./ProfileScreen-Csfa6rJx.mjs").then((m) => m.profileScreenPropertySchema),
4216
+ MessagingScreen: () => import("./MessagingScreen-FVQv8eTt.mjs").then((m) => m.messagingScreenPropertySchema),
4217
4217
  ContactsScreen: () => import("./ContactsScreen-DJX1UWHR.mjs").then((n) => n.n).then((m) => m.contactsScreenPropertySchema),
4218
- OrdersScreen: () => import("./OrdersScreen-Cw61Avcj.mjs").then((m) => m.ordersScreenPropertySchema),
4219
- SubscriptionsScreen: () => import("./SubscriptionsScreen-CcMMlP7h.mjs").then((m) => m.subscriptionsScreenPropertySchema),
4218
+ OrdersScreen: () => import("./OrdersScreen-a7S3d8a1.mjs").then((m) => m.ordersScreenPropertySchema),
4219
+ SubscriptionsScreen: () => import("./SubscriptionsScreen-T-AdATEO.mjs").then((m) => m.subscriptionsScreenPropertySchema),
4220
4220
  CustomersScreen: () => import("./CustomersScreen-BqM-rDhz.mjs").then((n) => n.n).then((m) => m.customersScreenPropertySchema),
4221
4221
  MySiteScreen: () => import("./MySiteScreen-D3AA3Zti.mjs").then((n) => n.n).then((m) => m.mySiteScreenPropertySchema),
4222
- ShareablesScreen: () => import("./ShareablesScreen-Bz9tKI_6.mjs").then((m) => m.shareablesScreenPropertySchema),
4223
- ShopScreen: () => import("./ShopScreen-B10Gydzu.mjs").then((m) => m.shopScreenPropertySchema),
4222
+ ShareablesScreen: () => import("./ShareablesScreen-BNv3xHry.mjs").then((m) => m.shareablesScreenPropertySchema),
4223
+ ShopScreen: () => import("./ShopScreen-BwZjrAys.mjs").then((m) => m.shopScreenPropertySchema),
4224
4224
  AppDownloadScreen: () => import("./AppDownloadScreen-DSH6LlYf.mjs").then((n) => n.t).then((m) => m.appDownloadScreenPropertySchema)
4225
4225
  };
4226
4226
  /**
@@ -6,16 +6,12 @@ import "../use-account-vIWrrngf.mjs";
6
6
  import "../use-store-BH0GPS-f.mjs";
7
7
  import "../mysite-api-context-BbxSfNft.mjs";
8
8
  import "../ContactsTranslationBridge-XnneGT_P.mjs";
9
- import "../FluidThemeProvider-mIKhJTlY.mjs";
10
- import "../PortalTenantClientProvider-Bms_pfpm.mjs";
11
9
  import "../static-dict-adapter-qxjr0Wsy.mjs";
12
10
  import "../translation-api-context-factory-DFr9yJ6Q.mjs";
13
11
  import "../shell-translation-api-context-Cxetel1o.mjs";
14
12
  import "../translation-adapter-CnS_Wly3.mjs";
15
13
  import "../translation-adapter-DE6ZYd_c.mjs";
16
- import "../AppNavigationContext-sWuBzay1.mjs";
17
- import { a as loadWidgetPackages, f as createDefaultFluidConfig, i as installAndReadFluidWidgetsRegistry, t as dedupeWidgetPackagesByPackageIdAndVersion } from "../widget-package-utils-4Jm9jLbe.mjs";
18
- import "../property-schema-types-DJgFKHAG.mjs";
14
+ import { a as loadWidgetPackages, f as createDefaultFluidConfig, i as installAndReadFluidWidgetsRegistry, t as dedupeWidgetPackagesByPackageIdAndVersion } from "../widget-package-utils-DjEsgclS.mjs";
19
15
  import "../registries-B_LFR-8_.mjs";
20
16
  import { n as isPreviewCommand, t as isAllowedOrigin } from "../protocol-C0coHAEe.mjs";
21
17
  import { Component, StrictMode, useCallback, useEffect, useRef, useState } from "react";
@@ -1 +1 @@
1
- {"version":3,"file":"preview-entry.mjs","names":[],"sources":["../../src/preview/PreviewRenderer.tsx","../../src/preview/preview-entry.tsx"],"sourcesContent":["import {\n Component,\n type ReactElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport type { ComponentType } from \"react\";\nimport type {\n PreviewCommand,\n PreviewEvent,\n WidgetRenderedEvent,\n} from \"@fluid-app/portal-core/preview/protocol\";\nimport { isPreviewCommand } from \"@fluid-app/portal-core/preview/protocol\";\n\ninterface PreviewRendererProps {\n /** Widget registry mapping type → component. */\n widgetRegistry: Record<string, ComponentType<Record<string, unknown>>>;\n /** Called when the builder sends an auth token. */\n onAuth: (token: string) => void;\n /** Origin of the builder (exact-match validated on every postMessage). */\n builderOrigin: string | null;\n /**\n * When true, skip sending the initial `ready` event on mount.\n * Used when `createPreview()` already sent `ready` before the React tree exists.\n */\n skipReady?: boolean;\n}\n\ninterface WidgetState {\n widgetType: string;\n props: Record<string, unknown>;\n}\n\n/**\n * Renders a single widget inside the preview iframe, driven by\n * postMessage commands from the builder.\n *\n * Lifecycle:\n * 1. Sends `ready` event on mount\n * 2. Waits for `auth` command → calls onAuth()\n * 3. Waits for `render-widget` command → renders the widget\n * 4. Reports dimensions via ResizeObserver\n */\nexport function PreviewRenderer({\n widgetRegistry,\n onAuth,\n builderOrigin,\n skipReady = false,\n}: PreviewRendererProps): ReactElement | null {\n const [widget, setWidget] = useState<WidgetState | null>(null);\n const [error, setError] = useState<string | null>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const sentReady = useRef(false);\n\n const sendToBuilder = useCallback(\n (event: PreviewEvent) => {\n if (!builderOrigin) return;\n window.parent.postMessage(event, builderOrigin);\n },\n [builderOrigin],\n );\n\n // Send `ready` once on mount (skipped when createPreview() already sent it)\n useEffect(() => {\n if (skipReady) return;\n if (sentReady.current) return;\n sentReady.current = true;\n sendToBuilder({ type: \"ready\" });\n }, [sendToBuilder, skipReady]);\n\n // Send `auth-ready` on mount ONLY when using the createPreview() flow\n // (skipReady=true means auth was already handled before mount).\n // In the standalone flow, auth-ready is sent from the auth command handler.\n const sentAuthReady = useRef(false);\n useEffect(() => {\n if (!skipReady) return;\n if (sentAuthReady.current) return;\n sentAuthReady.current = true;\n sendToBuilder({ type: \"auth-ready\" });\n }, [sendToBuilder, skipReady]);\n\n // Listen for commands from the builder (origin-locked to this session's builderOrigin)\n useEffect(() => {\n if (!builderOrigin) return;\n\n function handleMessage(event: MessageEvent) {\n if (event.origin !== builderOrigin) return;\n if (!isPreviewCommand(event.data)) return;\n\n const command = event.data as PreviewCommand;\n\n switch (command.type) {\n case \"auth\":\n onAuth(command.token);\n if (!sentAuthReady.current) {\n sentAuthReady.current = true;\n sendToBuilder({ type: \"auth-ready\" });\n }\n break;\n case \"render-widget\":\n setError(null);\n setWidget({\n widgetType: command.widgetType,\n props: command.props,\n });\n break;\n case \"select-widget\":\n // Selection is managed by the builder; no response needed.\n // Future: could store selected widgetType for visual indicators.\n break;\n case \"unmount-widget\":\n setWidget(null);\n setError(null);\n break;\n default: {\n // Exhaustiveness guard — TypeScript will error if a new\n // PreviewCommand variant is added without handling it here.\n const _exhaustive: never = command;\n void _exhaustive;\n }\n }\n }\n\n window.addEventListener(\"message\", handleMessage);\n return () => window.removeEventListener(\"message\", handleMessage);\n }, [builderOrigin, onAuth, sendToBuilder]);\n\n // Report dimensions via ResizeObserver\n useEffect(() => {\n const container = containerRef.current;\n if (!container || !widget) return;\n\n const observer = new ResizeObserver((entries) => {\n const entry = entries[0];\n if (!entry) return;\n\n const { width, height } = entry.contentRect;\n const event: WidgetRenderedEvent = {\n type: \"widget-rendered\",\n widgetType: widget.widgetType,\n dimensions: { width: Math.ceil(width), height: Math.ceil(height) },\n };\n sendToBuilder(event);\n });\n\n observer.observe(container);\n return () => observer.disconnect();\n }, [widget, sendToBuilder]);\n\n // HMR: notify builder when module updates\n useEffect(() => {\n const hot = (import.meta as unknown as Record<string, unknown>).hot as\n | {\n on: (event: string, cb: () => void) => void;\n off: (event: string, cb: () => void) => void;\n }\n | undefined;\n if (hot) {\n const handler = () => sendToBuilder({ type: \"widget-hmr\" });\n hot.on(\"vite:afterUpdate\", handler);\n return () => hot.off(\"vite:afterUpdate\", handler);\n }\n }, [sendToBuilder]);\n\n // Report unknown widget types via effect (not during render)\n const WidgetComponent = widget\n ? widgetRegistry[widget.widgetType]\n : undefined;\n useEffect(() => {\n if (!widget) return;\n if (!widgetRegistry[widget.widgetType]) {\n sendToBuilder({\n type: \"widget-error\",\n widgetType: widget.widgetType,\n error: `Unknown widget type: ${widget.widgetType}`,\n });\n }\n }, [widget, widgetRegistry, sendToBuilder]);\n\n if (!widget) return null;\n if (!WidgetComponent) return null;\n if (error) return null;\n\n return (\n <div\n ref={containerRef}\n onClick={() =>\n sendToBuilder({ type: \"widget-clicked\", widgetType: widget.widgetType })\n }\n >\n <ErrorBoundary\n key={widget.widgetType}\n widgetType={widget.widgetType}\n onError={(err) => {\n setError(err);\n sendToBuilder({\n type: \"widget-error\",\n widgetType: widget.widgetType,\n error: err,\n });\n }}\n >\n <WidgetComponent {...widget.props} />\n </ErrorBoundary>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Error boundary for catching render errors in custom widgets\n// ---------------------------------------------------------------------------\n\ninterface ErrorBoundaryProps {\n widgetType: string;\n onError: (error: string) => void;\n children: ReactElement;\n}\n\ninterface ErrorBoundaryState {\n hasError: boolean;\n}\n\nclass ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {\n constructor(props: ErrorBoundaryProps) {\n super(props);\n this.state = { hasError: false };\n }\n\n static getDerivedStateFromError(): ErrorBoundaryState {\n return { hasError: true };\n }\n\n override componentDidCatch(error: Error): void {\n this.props.onError(error.message);\n }\n\n override render() {\n if (this.state.hasError) {\n return null;\n }\n return this.props.children;\n }\n}\n","/**\n * Preview iframe bootstrap function.\n *\n * Called from the portal project's src/preview-entry.tsx with the\n * customWidgets array from portal.config.ts. Mirrors the createPortal()\n * pattern — SDK handles the wiring, user project provides the widgets.\n *\n * Two-phase auth handshake:\n * Phase 1: Send `ready`, wait for `auth` command, store token\n * Phase 2: Mount provider stack + PreviewRenderer, send `auth-ready`\n */\n\nimport { StrictMode, useEffect, useState } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { isAllowedOrigin } from \"@fluid-app/portal-core/preview/protocol\";\nimport type { AuthCommand } from \"@fluid-app/portal-core/preview/protocol\";\nimport type { WidgetManifest } from \"@fluid-app/portal-core/registries\";\nimport type { WidgetPackageDescriptor } from \"../types/widget-package-descriptor\";\nimport { FluidProvider } from \"../providers/FluidProvider\";\nimport { createDefaultFluidConfig } from \"../config/defaults\";\nimport {\n buildWidgetRegistry,\n mergeRegisteredWidgetsIntoRegistry,\n type SdkWidgetRegistry,\n} from \"../utils/build-widget-registry\";\nimport type { WidgetPackageLoadSummary } from \"../utils/load-widget-packages\";\nimport {\n installAndReadFluidWidgetsRegistry,\n loadWidgetPackages,\n} from \"../utils/load-widget-packages\";\nimport { dedupeWidgetPackagesByPackageIdAndVersion } from \"../utils/widget-package-utils\";\nimport { PreviewRenderer } from \"./PreviewRenderer\";\n\nexport interface CreatePreviewOptions {\n /** Custom widgets from portal.config.ts. */\n customWidgets?: WidgetManifest[];\n /** Trusted remote widget packages to load at runtime. */\n widgetPackages?: readonly WidgetPackageDescriptor[];\n /** DOM element ID to mount into. Defaults to \"preview-root\". */\n rootId?: string;\n}\n\nexport function createPreview(options: CreatePreviewOptions = {}): void {\n const {\n customWidgets = [],\n widgetPackages = [],\n rootId = \"preview-root\",\n } = options;\n\n const dedupedWidgetPackages =\n dedupeWidgetPackagesByPackageIdAndVersion(widgetPackages);\n\n const initialWidgetRegistry = mergeRegisteredWidgetsIntoRegistry(\n buildWidgetRegistry(customWidgets),\n installAndReadFluidWidgetsRegistry(),\n );\n\n // Extract builder origin from query param (set by IframeWidgetPreview)\n const params = new URLSearchParams(window.location.search);\n const rawBuilderOrigin = params.get(\"builderOrigin\");\n const builderOrigin =\n rawBuilderOrigin && isAllowedOrigin(rawBuilderOrigin)\n ? rawBuilderOrigin\n : null;\n\n // Phase 1: Send ready, wait for auth token via postMessage\n if (builderOrigin) {\n window.parent.postMessage({ type: \"ready\" }, builderOrigin);\n }\n\n function handleAuthMessage(event: MessageEvent) {\n if (event.origin !== builderOrigin) return;\n if (\n typeof event.data !== \"object\" ||\n event.data === null ||\n event.data.type !== \"auth\"\n )\n return;\n\n window.removeEventListener(\"message\", handleAuthMessage);\n\n const command = event.data as AuthCommand;\n mountApp({\n initialWidgetRegistry,\n widgetPackages: dedupedWidgetPackages,\n builderOrigin,\n rootId,\n authToken: command.token,\n });\n }\n\n if (builderOrigin) {\n window.addEventListener(\"message\", handleAuthMessage);\n } else {\n // Show visible error instead of blank page when origin is missing/invalid\n const rootElement = document.getElementById(rootId);\n if (rootElement) {\n rootElement.style.cssText =\n \"display:flex;align-items:center;justify-content:center;height:100vh;font-family:system-ui;color:#888;font-size:14px\";\n rootElement.textContent = rawBuilderOrigin\n ? `Preview unavailable — origin \"${rawBuilderOrigin}\" is not allowed`\n : \"Preview unavailable — missing builderOrigin parameter\";\n }\n }\n}\n\nfunction mountApp({\n initialWidgetRegistry,\n widgetPackages,\n builderOrigin,\n rootId,\n authToken,\n}: {\n initialWidgetRegistry: SdkWidgetRegistry;\n widgetPackages: readonly WidgetPackageDescriptor[];\n builderOrigin: string | null;\n rootId: string;\n authToken: string;\n}): void {\n const rootElement = document.getElementById(rootId);\n if (!rootElement) return;\n\n // Use the iframe's own origin as baseUrl so API calls (e.g. /api/...)\n // go through the portal's Vite proxy to the real backend.\n // This avoids CORS issues and lets the builder's auth token work.\n const iframeOrigin = window.location.origin;\n\n // Close over the token directly instead of reading from localStorage.\n // Cross-origin iframe storage partitioning can prevent getStoredToken()\n // from finding the token we just stored via storeToken().\n const fluidConfig = createDefaultFluidConfig({\n baseUrl: iframeOrigin,\n getAuthToken: () => authToken,\n });\n const widgetPackageLoadPromise =\n builderOrigin && widgetPackages.length\n ? loadWidgetPackages(widgetPackages)\n : undefined;\n\n const tree = (\n <StrictMode>\n <PreviewRuntimeWidgetBootstrap\n fluidConfig={fluidConfig}\n initialWidgetRegistry={initialWidgetRegistry}\n widgetPackageLoadPromise={widgetPackageLoadPromise}\n builderOrigin={builderOrigin}\n />\n </StrictMode>\n );\n\n const root = createRoot(rootElement);\n root.render(tree);\n // auth-ready is sent from PreviewRenderer's sentAuthReady mount effect.\n // Because postMessage delivers asynchronously (macrotask), the builder's\n // render-widget response always arrives after all effects in this commit\n // have run — including the handleMessage registration in effect 3.\n}\n\nfunction PreviewRuntimeWidgetBootstrap({\n fluidConfig,\n initialWidgetRegistry,\n widgetPackageLoadPromise,\n builderOrigin,\n}: {\n fluidConfig: ReturnType<typeof createDefaultFluidConfig>;\n initialWidgetRegistry: SdkWidgetRegistry;\n widgetPackageLoadPromise?: Promise<WidgetPackageLoadSummary>;\n builderOrigin: string | null;\n}): React.JSX.Element {\n const [runtimeWidgetState, setRuntimeWidgetState] = useState(() => ({\n widgetRegistry: initialWidgetRegistry,\n packageLoadSettled: !widgetPackageLoadPromise,\n }));\n\n useEffect(() => {\n if (!widgetPackageLoadPromise) {\n setRuntimeWidgetState({\n widgetRegistry: initialWidgetRegistry,\n packageLoadSettled: true,\n });\n return;\n }\n\n let cancelled = false;\n setRuntimeWidgetState({\n widgetRegistry: initialWidgetRegistry,\n packageLoadSettled: false,\n });\n\n void widgetPackageLoadPromise\n .then((summary) => {\n if (cancelled) return;\n setRuntimeWidgetState({\n widgetRegistry: mergeRegisteredWidgetsIntoRegistry(\n initialWidgetRegistry,\n summary.registeredWidgets,\n ),\n packageLoadSettled: true,\n });\n })\n .catch((error: unknown) => {\n if (cancelled) return;\n console.error(\n \"[portal] Failed to load preview widget packages:\",\n error,\n );\n setRuntimeWidgetState({\n widgetRegistry: initialWidgetRegistry,\n packageLoadSettled: true,\n });\n });\n\n return () => {\n cancelled = true;\n };\n }, [initialWidgetRegistry, widgetPackageLoadPromise]);\n\n return (\n <FluidProvider\n config={fluidConfig}\n widgetRegistry={runtimeWidgetState.widgetRegistry}\n >\n {runtimeWidgetState.packageLoadSettled ? (\n <PreviewRenderer\n widgetRegistry={runtimeWidgetState.widgetRegistry}\n onAuth={() => {}}\n builderOrigin={builderOrigin}\n skipReady\n />\n ) : null}\n </FluidProvider>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,SAAgB,gBAAgB,EAC9B,gBACA,QACA,eACA,YAAY,SACgC;CAC5C,MAAM,CAAC,QAAQ,aAAa,SAA6B,KAAK;CAC9D,MAAM,CAAC,OAAO,YAAY,SAAwB,KAAK;CACvD,MAAM,eAAe,OAAuB,KAAK;CACjD,MAAM,YAAY,OAAO,MAAM;CAE/B,MAAM,gBAAgB,aACnB,UAAwB;AACvB,MAAI,CAAC,cAAe;AACpB,SAAO,OAAO,YAAY,OAAO,cAAc;IAEjD,CAAC,cAAc,CAChB;AAGD,iBAAgB;AACd,MAAI,UAAW;AACf,MAAI,UAAU,QAAS;AACvB,YAAU,UAAU;AACpB,gBAAc,EAAE,MAAM,SAAS,CAAC;IAC/B,CAAC,eAAe,UAAU,CAAC;CAK9B,MAAM,gBAAgB,OAAO,MAAM;AACnC,iBAAgB;AACd,MAAI,CAAC,UAAW;AAChB,MAAI,cAAc,QAAS;AAC3B,gBAAc,UAAU;AACxB,gBAAc,EAAE,MAAM,cAAc,CAAC;IACpC,CAAC,eAAe,UAAU,CAAC;AAG9B,iBAAgB;AACd,MAAI,CAAC,cAAe;EAEpB,SAAS,cAAc,OAAqB;AAC1C,OAAI,MAAM,WAAW,cAAe;AACpC,OAAI,CAAC,iBAAiB,MAAM,KAAK,CAAE;GAEnC,MAAM,UAAU,MAAM;AAEtB,WAAQ,QAAQ,MAAhB;IACE,KAAK;AACH,YAAO,QAAQ,MAAM;AACrB,SAAI,CAAC,cAAc,SAAS;AAC1B,oBAAc,UAAU;AACxB,oBAAc,EAAE,MAAM,cAAc,CAAC;;AAEvC;IACF,KAAK;AACH,cAAS,KAAK;AACd,eAAU;MACR,YAAY,QAAQ;MACpB,OAAO,QAAQ;MAChB,CAAC;AACF;IACF,KAAK,gBAGH;IACF,KAAK;AACH,eAAU,KAAK;AACf,cAAS,KAAK;AACd;IACF;;;AASJ,SAAO,iBAAiB,WAAW,cAAc;AACjD,eAAa,OAAO,oBAAoB,WAAW,cAAc;IAChE;EAAC;EAAe;EAAQ;EAAc,CAAC;AAG1C,iBAAgB;EACd,MAAM,YAAY,aAAa;AAC/B,MAAI,CAAC,aAAa,CAAC,OAAQ;EAE3B,MAAM,WAAW,IAAI,gBAAgB,YAAY;GAC/C,MAAM,QAAQ,QAAQ;AACtB,OAAI,CAAC,MAAO;GAEZ,MAAM,EAAE,OAAO,WAAW,MAAM;AAMhC,iBALmC;IACjC,MAAM;IACN,YAAY,OAAO;IACnB,YAAY;KAAE,OAAO,KAAK,KAAK,MAAM;KAAE,QAAQ,KAAK,KAAK,OAAO;KAAE;IACnE,CACmB;IACpB;AAEF,WAAS,QAAQ,UAAU;AAC3B,eAAa,SAAS,YAAY;IACjC,CAAC,QAAQ,cAAc,CAAC;AAG3B,iBAAgB;EACd,MAAM,MAAO,OAAO,KAA4C;AAMhE,MAAI,KAAK;GACP,MAAM,gBAAgB,cAAc,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAI,GAAG,oBAAoB,QAAQ;AACnC,gBAAa,IAAI,IAAI,oBAAoB,QAAQ;;IAElD,CAAC,cAAc,CAAC;CAGnB,MAAM,kBAAkB,SACpB,eAAe,OAAO,cACtB,KAAA;AACJ,iBAAgB;AACd,MAAI,CAAC,OAAQ;AACb,MAAI,CAAC,eAAe,OAAO,YACzB,eAAc;GACZ,MAAM;GACN,YAAY,OAAO;GACnB,OAAO,wBAAwB,OAAO;GACvC,CAAC;IAEH;EAAC;EAAQ;EAAgB;EAAc,CAAC;AAE3C,KAAI,CAAC,OAAQ,QAAO;AACpB,KAAI,CAAC,gBAAiB,QAAO;AAC7B,KAAI,MAAO,QAAO;AAElB,QACE,oBAAC,OAAD;EACE,KAAK;EACL,eACE,cAAc;GAAE,MAAM;GAAkB,YAAY,OAAO;GAAY,CAAC;YAG1E,oBAAC,eAAD;GAEE,YAAY,OAAO;GACnB,UAAU,QAAQ;AAChB,aAAS,IAAI;AACb,kBAAc;KACZ,MAAM;KACN,YAAY,OAAO;KACnB,OAAO;KACR,CAAC;;aAGJ,oBAAC,iBAAD,EAAiB,GAAI,OAAO,OAAS,CAAA;GACvB,EAZT,OAAO,WAYE;EACZ,CAAA;;AAkBV,IAAM,gBAAN,cAA4B,UAAkD;CAC5E,YAAY,OAA2B;AACrC,QAAM,MAAM;AACZ,OAAK,QAAQ,EAAE,UAAU,OAAO;;CAGlC,OAAO,2BAA+C;AACpD,SAAO,EAAE,UAAU,MAAM;;CAG3B,kBAA2B,OAAoB;AAC7C,OAAK,MAAM,QAAQ,MAAM,QAAQ;;CAGnC,SAAkB;AAChB,MAAI,KAAK,MAAM,SACb,QAAO;AAET,SAAO,KAAK,MAAM;;;;;;;;;;;;;;;;ACxMtB,SAAgB,cAAc,UAAgC,EAAE,EAAQ;CACtE,MAAM,EACJ,gBAAgB,EAAE,EAClB,iBAAiB,EAAE,EACnB,SAAS,mBACP;CAEJ,MAAM,wBACJ,0CAA0C,eAAe;CAE3D,MAAM,wBAAwB,mCAC5B,oBAAoB,cAAc,EAClC,oCAAoC,CACrC;CAID,MAAM,mBADS,IAAI,gBAAgB,OAAO,SAAS,OAAO,CAC1B,IAAI,gBAAgB;CACpD,MAAM,gBACJ,oBAAoB,gBAAgB,iBAAiB,GACjD,mBACA;AAGN,KAAI,cACF,QAAO,OAAO,YAAY,EAAE,MAAM,SAAS,EAAE,cAAc;CAG7D,SAAS,kBAAkB,OAAqB;AAC9C,MAAI,MAAM,WAAW,cAAe;AACpC,MACE,OAAO,MAAM,SAAS,YACtB,MAAM,SAAS,QACf,MAAM,KAAK,SAAS,OAEpB;AAEF,SAAO,oBAAoB,WAAW,kBAAkB;EAExD,MAAM,UAAU,MAAM;AACtB,WAAS;GACP;GACA,gBAAgB;GAChB;GACA;GACA,WAAW,QAAQ;GACpB,CAAC;;AAGJ,KAAI,cACF,QAAO,iBAAiB,WAAW,kBAAkB;MAChD;EAEL,MAAM,cAAc,SAAS,eAAe,OAAO;AACnD,MAAI,aAAa;AACf,eAAY,MAAM,UAChB;AACF,eAAY,cAAc,mBACtB,iCAAiC,iBAAiB,oBAClD;;;;AAKV,SAAS,SAAS,EAChB,uBACA,gBACA,eACA,QACA,aAOO;CACP,MAAM,cAAc,SAAS,eAAe,OAAO;AACnD,KAAI,CAAC,YAAa;CAKlB,MAAM,eAAe,OAAO,SAAS;CAcrC,MAAM,OACJ,oBAAC,YAAD,EAAA,UACE,oBAAC,+BAAD;EACE,aAZc,yBAAyB;GAC3C,SAAS;GACT,oBAAoB;GACrB,CAAC;EAU2B;EACvB,0BATJ,iBAAiB,eAAe,SAC5B,mBAAmB,eAAe,GAClC,KAAA;EAQe;EACf,CAAA,EACS,CAAA;AAGF,YAAW,YAAY,CAC/B,OAAO,KAAK;;AAOnB,SAAS,8BAA8B,EACrC,aACA,uBACA,0BACA,iBAMoB;CACpB,MAAM,CAAC,oBAAoB,yBAAyB,gBAAgB;EAClE,gBAAgB;EAChB,oBAAoB,CAAC;EACtB,EAAE;AAEH,iBAAgB;AACd,MAAI,CAAC,0BAA0B;AAC7B,yBAAsB;IACpB,gBAAgB;IAChB,oBAAoB;IACrB,CAAC;AACF;;EAGF,IAAI,YAAY;AAChB,wBAAsB;GACpB,gBAAgB;GAChB,oBAAoB;GACrB,CAAC;AAEG,2BACF,MAAM,YAAY;AACjB,OAAI,UAAW;AACf,yBAAsB;IACpB,gBAAgB,mCACd,uBACA,QAAQ,kBACT;IACD,oBAAoB;IACrB,CAAC;IACF,CACD,OAAO,UAAmB;AACzB,OAAI,UAAW;AACf,WAAQ,MACN,oDACA,MACD;AACD,yBAAsB;IACpB,gBAAgB;IAChB,oBAAoB;IACrB,CAAC;IACF;AAEJ,eAAa;AACX,eAAY;;IAEb,CAAC,uBAAuB,yBAAyB,CAAC;AAErD,QACE,oBAAC,eAAD;EACE,QAAQ;EACR,gBAAgB,mBAAmB;YAElC,mBAAmB,qBAClB,oBAAC,iBAAD;GACE,gBAAgB,mBAAmB;GACnC,cAAc;GACC;GACf,WAAA;GACA,CAAA,GACA;EACU,CAAA"}
1
+ {"version":3,"file":"preview-entry.mjs","names":[],"sources":["../../src/preview/PreviewRenderer.tsx","../../src/preview/preview-entry.tsx"],"sourcesContent":["import {\n Component,\n type ReactElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport type { ComponentType } from \"react\";\nimport type {\n PreviewCommand,\n PreviewEvent,\n WidgetRenderedEvent,\n} from \"@fluid-app/portal-core/preview/protocol\";\nimport { isPreviewCommand } from \"@fluid-app/portal-core/preview/protocol\";\n\ninterface PreviewRendererProps {\n /** Widget registry mapping type → component. */\n widgetRegistry: Record<string, ComponentType<Record<string, unknown>>>;\n /** Called when the builder sends an auth token. */\n onAuth: (token: string) => void;\n /** Origin of the builder (exact-match validated on every postMessage). */\n builderOrigin: string | null;\n /**\n * When true, skip sending the initial `ready` event on mount.\n * Used when `createPreview()` already sent `ready` before the React tree exists.\n */\n skipReady?: boolean;\n}\n\ninterface WidgetState {\n widgetType: string;\n props: Record<string, unknown>;\n}\n\n/**\n * Renders a single widget inside the preview iframe, driven by\n * postMessage commands from the builder.\n *\n * Lifecycle:\n * 1. Sends `ready` event on mount\n * 2. Waits for `auth` command → calls onAuth()\n * 3. Waits for `render-widget` command → renders the widget\n * 4. Reports dimensions via ResizeObserver\n */\nexport function PreviewRenderer({\n widgetRegistry,\n onAuth,\n builderOrigin,\n skipReady = false,\n}: PreviewRendererProps): ReactElement | null {\n const [widget, setWidget] = useState<WidgetState | null>(null);\n const [error, setError] = useState<string | null>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const sentReady = useRef(false);\n\n const sendToBuilder = useCallback(\n (event: PreviewEvent) => {\n if (!builderOrigin) return;\n window.parent.postMessage(event, builderOrigin);\n },\n [builderOrigin],\n );\n\n // Send `ready` once on mount (skipped when createPreview() already sent it)\n useEffect(() => {\n if (skipReady) return;\n if (sentReady.current) return;\n sentReady.current = true;\n sendToBuilder({ type: \"ready\" });\n }, [sendToBuilder, skipReady]);\n\n // Send `auth-ready` on mount ONLY when using the createPreview() flow\n // (skipReady=true means auth was already handled before mount).\n // In the standalone flow, auth-ready is sent from the auth command handler.\n const sentAuthReady = useRef(false);\n useEffect(() => {\n if (!skipReady) return;\n if (sentAuthReady.current) return;\n sentAuthReady.current = true;\n sendToBuilder({ type: \"auth-ready\" });\n }, [sendToBuilder, skipReady]);\n\n // Listen for commands from the builder (origin-locked to this session's builderOrigin)\n useEffect(() => {\n if (!builderOrigin) return;\n\n function handleMessage(event: MessageEvent) {\n if (event.origin !== builderOrigin) return;\n if (!isPreviewCommand(event.data)) return;\n\n const command = event.data as PreviewCommand;\n\n switch (command.type) {\n case \"auth\":\n onAuth(command.token);\n if (!sentAuthReady.current) {\n sentAuthReady.current = true;\n sendToBuilder({ type: \"auth-ready\" });\n }\n break;\n case \"render-widget\":\n setError(null);\n setWidget({\n widgetType: command.widgetType,\n props: command.props,\n });\n break;\n case \"select-widget\":\n // Selection is managed by the builder; no response needed.\n // Future: could store selected widgetType for visual indicators.\n break;\n case \"unmount-widget\":\n setWidget(null);\n setError(null);\n break;\n default: {\n // Exhaustiveness guard — TypeScript will error if a new\n // PreviewCommand variant is added without handling it here.\n const _exhaustive: never = command;\n void _exhaustive;\n }\n }\n }\n\n window.addEventListener(\"message\", handleMessage);\n return () => window.removeEventListener(\"message\", handleMessage);\n }, [builderOrigin, onAuth, sendToBuilder]);\n\n // Report dimensions via ResizeObserver\n useEffect(() => {\n const container = containerRef.current;\n if (!container || !widget) return;\n\n const observer = new ResizeObserver((entries) => {\n const entry = entries[0];\n if (!entry) return;\n\n const { width, height } = entry.contentRect;\n const event: WidgetRenderedEvent = {\n type: \"widget-rendered\",\n widgetType: widget.widgetType,\n dimensions: { width: Math.ceil(width), height: Math.ceil(height) },\n };\n sendToBuilder(event);\n });\n\n observer.observe(container);\n return () => observer.disconnect();\n }, [widget, sendToBuilder]);\n\n // HMR: notify builder when module updates\n useEffect(() => {\n const hot = (import.meta as unknown as Record<string, unknown>).hot as\n | {\n on: (event: string, cb: () => void) => void;\n off: (event: string, cb: () => void) => void;\n }\n | undefined;\n if (hot) {\n const handler = () => sendToBuilder({ type: \"widget-hmr\" });\n hot.on(\"vite:afterUpdate\", handler);\n return () => hot.off(\"vite:afterUpdate\", handler);\n }\n }, [sendToBuilder]);\n\n // Report unknown widget types via effect (not during render)\n const WidgetComponent = widget\n ? widgetRegistry[widget.widgetType]\n : undefined;\n useEffect(() => {\n if (!widget) return;\n if (!widgetRegistry[widget.widgetType]) {\n sendToBuilder({\n type: \"widget-error\",\n widgetType: widget.widgetType,\n error: `Unknown widget type: ${widget.widgetType}`,\n });\n }\n }, [widget, widgetRegistry, sendToBuilder]);\n\n if (!widget) return null;\n if (!WidgetComponent) return null;\n if (error) return null;\n\n return (\n <div\n ref={containerRef}\n onClick={() =>\n sendToBuilder({ type: \"widget-clicked\", widgetType: widget.widgetType })\n }\n >\n <ErrorBoundary\n key={widget.widgetType}\n widgetType={widget.widgetType}\n onError={(err) => {\n setError(err);\n sendToBuilder({\n type: \"widget-error\",\n widgetType: widget.widgetType,\n error: err,\n });\n }}\n >\n <WidgetComponent {...widget.props} />\n </ErrorBoundary>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Error boundary for catching render errors in custom widgets\n// ---------------------------------------------------------------------------\n\ninterface ErrorBoundaryProps {\n widgetType: string;\n onError: (error: string) => void;\n children: ReactElement;\n}\n\ninterface ErrorBoundaryState {\n hasError: boolean;\n}\n\nclass ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {\n constructor(props: ErrorBoundaryProps) {\n super(props);\n this.state = { hasError: false };\n }\n\n static getDerivedStateFromError(): ErrorBoundaryState {\n return { hasError: true };\n }\n\n override componentDidCatch(error: Error): void {\n this.props.onError(error.message);\n }\n\n override render() {\n if (this.state.hasError) {\n return null;\n }\n return this.props.children;\n }\n}\n","/**\n * Preview iframe bootstrap function.\n *\n * Called from the portal project's src/preview-entry.tsx with the\n * customWidgets array from portal.config.ts. Mirrors the createPortal()\n * pattern — SDK handles the wiring, user project provides the widgets.\n *\n * Two-phase auth handshake:\n * Phase 1: Send `ready`, wait for `auth` command, store token\n * Phase 2: Mount provider stack + PreviewRenderer, send `auth-ready`\n */\n\nimport { StrictMode, useEffect, useState } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { isAllowedOrigin } from \"@fluid-app/portal-core/preview/protocol\";\nimport type { AuthCommand } from \"@fluid-app/portal-core/preview/protocol\";\nimport type { WidgetManifest } from \"@fluid-app/portal-core/registries\";\nimport type { WidgetPackageDescriptor } from \"../types/widget-package-descriptor\";\nimport { FluidProvider } from \"../providers/FluidProvider\";\nimport { createDefaultFluidConfig } from \"../config/defaults\";\nimport {\n buildWidgetRegistry,\n mergeRegisteredWidgetsIntoRegistry,\n type SdkWidgetRegistry,\n} from \"../utils/build-widget-registry\";\nimport type { WidgetPackageLoadSummary } from \"../utils/load-widget-packages\";\nimport {\n installAndReadFluidWidgetsRegistry,\n loadWidgetPackages,\n} from \"../utils/load-widget-packages\";\nimport { dedupeWidgetPackagesByPackageIdAndVersion } from \"../utils/widget-package-utils\";\nimport { PreviewRenderer } from \"./PreviewRenderer\";\n\nexport interface CreatePreviewOptions {\n /** Custom widgets from portal.config.ts. */\n customWidgets?: WidgetManifest[];\n /** Trusted remote widget packages to load at runtime. */\n widgetPackages?: readonly WidgetPackageDescriptor[];\n /** DOM element ID to mount into. Defaults to \"preview-root\". */\n rootId?: string;\n}\n\nexport function createPreview(options: CreatePreviewOptions = {}): void {\n const {\n customWidgets = [],\n widgetPackages = [],\n rootId = \"preview-root\",\n } = options;\n\n const dedupedWidgetPackages =\n dedupeWidgetPackagesByPackageIdAndVersion(widgetPackages);\n\n const initialWidgetRegistry = mergeRegisteredWidgetsIntoRegistry(\n buildWidgetRegistry(customWidgets),\n installAndReadFluidWidgetsRegistry(),\n );\n\n // Extract builder origin from query param (set by IframeWidgetPreview)\n const params = new URLSearchParams(window.location.search);\n const rawBuilderOrigin = params.get(\"builderOrigin\");\n const builderOrigin =\n rawBuilderOrigin && isAllowedOrigin(rawBuilderOrigin)\n ? rawBuilderOrigin\n : null;\n\n // Phase 1: Send ready, wait for auth token via postMessage\n if (builderOrigin) {\n window.parent.postMessage({ type: \"ready\" }, builderOrigin);\n }\n\n function handleAuthMessage(event: MessageEvent) {\n if (event.origin !== builderOrigin) return;\n if (\n typeof event.data !== \"object\" ||\n event.data === null ||\n event.data.type !== \"auth\"\n )\n return;\n\n window.removeEventListener(\"message\", handleAuthMessage);\n\n const command = event.data as AuthCommand;\n mountApp({\n initialWidgetRegistry,\n widgetPackages: dedupedWidgetPackages,\n builderOrigin,\n rootId,\n authToken: command.token,\n });\n }\n\n if (builderOrigin) {\n window.addEventListener(\"message\", handleAuthMessage);\n } else {\n // Show visible error instead of blank page when origin is missing/invalid\n const rootElement = document.getElementById(rootId);\n if (rootElement) {\n rootElement.style.cssText =\n \"display:flex;align-items:center;justify-content:center;height:100vh;font-family:system-ui;color:#888;font-size:14px\";\n rootElement.textContent = rawBuilderOrigin\n ? `Preview unavailable — origin \"${rawBuilderOrigin}\" is not allowed`\n : \"Preview unavailable — missing builderOrigin parameter\";\n }\n }\n}\n\nfunction mountApp({\n initialWidgetRegistry,\n widgetPackages,\n builderOrigin,\n rootId,\n authToken,\n}: {\n initialWidgetRegistry: SdkWidgetRegistry;\n widgetPackages: readonly WidgetPackageDescriptor[];\n builderOrigin: string | null;\n rootId: string;\n authToken: string;\n}): void {\n const rootElement = document.getElementById(rootId);\n if (!rootElement) return;\n\n // Use the iframe's own origin as baseUrl so API calls (e.g. /api/...)\n // go through the portal's Vite proxy to the real backend.\n // This avoids CORS issues and lets the builder's auth token work.\n const iframeOrigin = window.location.origin;\n\n // Close over the token directly instead of reading from localStorage.\n // Cross-origin iframe storage partitioning can prevent getStoredToken()\n // from finding the token we just stored via storeToken().\n const fluidConfig = createDefaultFluidConfig({\n baseUrl: iframeOrigin,\n getAuthToken: () => authToken,\n });\n const widgetPackageLoadPromise =\n builderOrigin && widgetPackages.length\n ? loadWidgetPackages(widgetPackages)\n : undefined;\n\n const tree = (\n <StrictMode>\n <PreviewRuntimeWidgetBootstrap\n fluidConfig={fluidConfig}\n initialWidgetRegistry={initialWidgetRegistry}\n widgetPackageLoadPromise={widgetPackageLoadPromise}\n builderOrigin={builderOrigin}\n />\n </StrictMode>\n );\n\n const root = createRoot(rootElement);\n root.render(tree);\n // auth-ready is sent from PreviewRenderer's sentAuthReady mount effect.\n // Because postMessage delivers asynchronously (macrotask), the builder's\n // render-widget response always arrives after all effects in this commit\n // have run — including the handleMessage registration in effect 3.\n}\n\nfunction PreviewRuntimeWidgetBootstrap({\n fluidConfig,\n initialWidgetRegistry,\n widgetPackageLoadPromise,\n builderOrigin,\n}: {\n fluidConfig: ReturnType<typeof createDefaultFluidConfig>;\n initialWidgetRegistry: SdkWidgetRegistry;\n widgetPackageLoadPromise?: Promise<WidgetPackageLoadSummary>;\n builderOrigin: string | null;\n}): React.JSX.Element {\n const [runtimeWidgetState, setRuntimeWidgetState] = useState(() => ({\n widgetRegistry: initialWidgetRegistry,\n packageLoadSettled: !widgetPackageLoadPromise,\n }));\n\n useEffect(() => {\n if (!widgetPackageLoadPromise) {\n setRuntimeWidgetState({\n widgetRegistry: initialWidgetRegistry,\n packageLoadSettled: true,\n });\n return;\n }\n\n let cancelled = false;\n setRuntimeWidgetState({\n widgetRegistry: initialWidgetRegistry,\n packageLoadSettled: false,\n });\n\n void widgetPackageLoadPromise\n .then((summary) => {\n if (cancelled) return;\n setRuntimeWidgetState({\n widgetRegistry: mergeRegisteredWidgetsIntoRegistry(\n initialWidgetRegistry,\n summary.registeredWidgets,\n ),\n packageLoadSettled: true,\n });\n })\n .catch((error: unknown) => {\n if (cancelled) return;\n console.error(\n \"[portal] Failed to load preview widget packages:\",\n error,\n );\n setRuntimeWidgetState({\n widgetRegistry: initialWidgetRegistry,\n packageLoadSettled: true,\n });\n });\n\n return () => {\n cancelled = true;\n };\n }, [initialWidgetRegistry, widgetPackageLoadPromise]);\n\n return (\n <FluidProvider\n config={fluidConfig}\n widgetRegistry={runtimeWidgetState.widgetRegistry}\n >\n {runtimeWidgetState.packageLoadSettled ? (\n <PreviewRenderer\n widgetRegistry={runtimeWidgetState.widgetRegistry}\n onAuth={() => {}}\n builderOrigin={builderOrigin}\n skipReady\n />\n ) : null}\n </FluidProvider>\n );\n}\n"],"mappings":"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"}
@@ -2,7 +2,6 @@ import { i as projectRemoteWidgetPackages } from "../build-widget-registry-DR6xE
2
2
  import "../transforms-D8tNbtFA.mjs";
3
3
  import "../use-account-vIWrrngf.mjs";
4
4
  import "../use-store-BH0GPS-f.mjs";
5
- import "../AppNavigationContext-sWuBzay1.mjs";
6
5
  import { o as isPropertyFieldType } from "../registries-B_LFR-8_.mjs";
7
6
  //#region src/utils/build-remote-widget-plugin-manifests.ts
8
7
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"build-remote-widget-plugin-manifests.mjs","names":[],"sources":["../../src/utils/build-remote-widget-plugin-manifests.ts"],"sourcesContent":["import type { ComponentType } from \"react\";\nimport {\n isPropertyFieldType,\n type PropertyField,\n type WidgetManifest,\n} from \"@fluid-app/portal-core/registries\";\nimport type {\n RemoteDomWidgetDefinition,\n RemoteDomWidgetPackageDescriptor,\n} from \"@fluid-app/portal-core/remote-dom-widget-package\";\nimport {\n projectRemoteWidgetPackages,\n type RemoteDomWidgetRegistryOptions,\n type WidgetRegistryWarningSink,\n} from \"./build-widget-registry\";\n\nexport interface RemoteWidgetPluginHostOptions extends RemoteDomWidgetRegistryOptions {\n readonly warningSink?: WidgetRegistryWarningSink;\n}\n\n/**\n * Projects accepted canonical packages into builder manifests using the same\n * package-atomic acceptance pass as the runtime registry.\n */\nexport function buildRemoteWidgetPluginManifests(\n packages: readonly RemoteDomWidgetPackageDescriptor[],\n hostOptions: RemoteWidgetPluginHostOptions = {},\n): WidgetManifest[] {\n const { warningSink, ...registryOptions } = hostOptions;\n const projection = projectRemoteWidgetPackages(\n {},\n packages,\n registryOptions,\n warningSink ?? console.warn,\n );\n return projection.acceptedWidgets.map(({ widget, component }) =>\n toWidgetManifest(widget, component),\n );\n}\n\nfunction toWidgetManifest(\n widget: RemoteDomWidgetDefinition,\n component: ComponentType<Record<string, unknown>>,\n): WidgetManifest {\n const resizable = toResizable(widget.resizable);\n return {\n manifestVersion: 1,\n type: widget.type,\n component,\n displayName: widget.displayName,\n description: widget.description,\n icon: widget.icon,\n category: widget.category,\n propertySchema: normalizePropertySchema(widget),\n defaultProps: { ...widget.defaultProps },\n minSdkVersion: widget.minSdkVersion,\n ...(resizable === undefined ? {} : { resizable }),\n };\n}\n\nfunction normalizePropertySchema(\n widget: RemoteDomWidgetDefinition,\n): WidgetManifest[\"propertySchema\"] {\n const fields = Array.isArray(widget.propertySchema.fields)\n ? widget.propertySchema.fields.filter(isPropertyField)\n : [];\n return {\n ...widget.propertySchema,\n widgetType: widget.type,\n displayName:\n typeof widget.propertySchema.displayName === \"string\"\n ? widget.propertySchema.displayName\n : widget.displayName,\n fields,\n } as WidgetManifest[\"propertySchema\"];\n}\n\nfunction toResizable(\n resizable: RemoteDomWidgetDefinition[\"resizable\"],\n): WidgetManifest[\"resizable\"] {\n if (resizable === false) return undefined;\n if (resizable === true || resizable === \"both\") {\n return { horizontal: true, vertical: true };\n }\n return resizable === \"horizontal\" ? { horizontal: true } : { vertical: true };\n}\n\nfunction isPropertyField(value: unknown): value is PropertyField {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"key\" in value &&\n typeof value.key === \"string\" &&\n \"label\" in value &&\n typeof value.label === \"string\" &&\n \"type\" in value &&\n typeof value.type === \"string\" &&\n isPropertyFieldType(value.type)\n );\n}\n"],"mappings":";;;;;;;;;;;AAwBA,SAAgB,iCACd,UACA,cAA6C,EAAE,EAC7B;CAClB,MAAM,EAAE,aAAa,GAAG,oBAAoB;AAO5C,QANmB,4BACjB,EAAE,EACF,UACA,iBACA,eAAe,QAAQ,KACxB,CACiB,gBAAgB,KAAK,EAAE,QAAQ,gBAC/C,iBAAiB,QAAQ,UAAU,CACpC;;AAGH,SAAS,iBACP,QACA,WACgB;CAChB,MAAM,YAAY,YAAY,OAAO,UAAU;AAC/C,QAAO;EACL,iBAAiB;EACjB,MAAM,OAAO;EACb;EACA,aAAa,OAAO;EACpB,aAAa,OAAO;EACpB,MAAM,OAAO;EACb,UAAU,OAAO;EACjB,gBAAgB,wBAAwB,OAAO;EAC/C,cAAc,EAAE,GAAG,OAAO,cAAc;EACxC,eAAe,OAAO;EACtB,GAAI,cAAc,KAAA,IAAY,EAAE,GAAG,EAAE,WAAW;EACjD;;AAGH,SAAS,wBACP,QACkC;CAClC,MAAM,SAAS,MAAM,QAAQ,OAAO,eAAe,OAAO,GACtD,OAAO,eAAe,OAAO,OAAO,gBAAgB,GACpD,EAAE;AACN,QAAO;EACL,GAAG,OAAO;EACV,YAAY,OAAO;EACnB,aACE,OAAO,OAAO,eAAe,gBAAgB,WACzC,OAAO,eAAe,cACtB,OAAO;EACb;EACD;;AAGH,SAAS,YACP,WAC6B;AAC7B,KAAI,cAAc,MAAO,QAAO,KAAA;AAChC,KAAI,cAAc,QAAQ,cAAc,OACtC,QAAO;EAAE,YAAY;EAAM,UAAU;EAAM;AAE7C,QAAO,cAAc,eAAe,EAAE,YAAY,MAAM,GAAG,EAAE,UAAU,MAAM;;AAG/E,SAAS,gBAAgB,OAAwC;AAC/D,QACE,OAAO,UAAU,YACjB,UAAU,QACV,SAAS,SACT,OAAO,MAAM,QAAQ,YACrB,WAAW,SACX,OAAO,MAAM,UAAU,YACvB,UAAU,SACV,OAAO,MAAM,SAAS,YACtB,oBAAoB,MAAM,KAAK"}
1
+ {"version":3,"file":"build-remote-widget-plugin-manifests.mjs","names":[],"sources":["../../src/utils/build-remote-widget-plugin-manifests.ts"],"sourcesContent":["import type { ComponentType } from \"react\";\nimport {\n isPropertyFieldType,\n type PropertyField,\n type WidgetManifest,\n} from \"@fluid-app/portal-core/registries\";\nimport type {\n RemoteDomWidgetDefinition,\n RemoteDomWidgetPackageDescriptor,\n} from \"@fluid-app/portal-core/remote-dom-widget-package\";\nimport {\n projectRemoteWidgetPackages,\n type RemoteDomWidgetRegistryOptions,\n type WidgetRegistryWarningSink,\n} from \"./build-widget-registry\";\n\nexport interface RemoteWidgetPluginHostOptions extends RemoteDomWidgetRegistryOptions {\n readonly warningSink?: WidgetRegistryWarningSink;\n}\n\n/**\n * Projects accepted canonical packages into builder manifests using the same\n * package-atomic acceptance pass as the runtime registry.\n */\nexport function buildRemoteWidgetPluginManifests(\n packages: readonly RemoteDomWidgetPackageDescriptor[],\n hostOptions: RemoteWidgetPluginHostOptions = {},\n): WidgetManifest[] {\n const { warningSink, ...registryOptions } = hostOptions;\n const projection = projectRemoteWidgetPackages(\n {},\n packages,\n registryOptions,\n warningSink ?? console.warn,\n );\n return projection.acceptedWidgets.map(({ widget, component }) =>\n toWidgetManifest(widget, component),\n );\n}\n\nfunction toWidgetManifest(\n widget: RemoteDomWidgetDefinition,\n component: ComponentType<Record<string, unknown>>,\n): WidgetManifest {\n const resizable = toResizable(widget.resizable);\n return {\n manifestVersion: 1,\n type: widget.type,\n component,\n displayName: widget.displayName,\n description: widget.description,\n icon: widget.icon,\n category: widget.category,\n propertySchema: normalizePropertySchema(widget),\n defaultProps: { ...widget.defaultProps },\n minSdkVersion: widget.minSdkVersion,\n ...(resizable === undefined ? {} : { resizable }),\n };\n}\n\nfunction normalizePropertySchema(\n widget: RemoteDomWidgetDefinition,\n): WidgetManifest[\"propertySchema\"] {\n const fields = Array.isArray(widget.propertySchema.fields)\n ? widget.propertySchema.fields.filter(isPropertyField)\n : [];\n return {\n ...widget.propertySchema,\n widgetType: widget.type,\n displayName:\n typeof widget.propertySchema.displayName === \"string\"\n ? widget.propertySchema.displayName\n : widget.displayName,\n fields,\n } as WidgetManifest[\"propertySchema\"];\n}\n\nfunction toResizable(\n resizable: RemoteDomWidgetDefinition[\"resizable\"],\n): WidgetManifest[\"resizable\"] {\n if (resizable === false) return undefined;\n if (resizable === true || resizable === \"both\") {\n return { horizontal: true, vertical: true };\n }\n return resizable === \"horizontal\" ? { horizontal: true } : { vertical: true };\n}\n\nfunction isPropertyField(value: unknown): value is PropertyField {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"key\" in value &&\n typeof value.key === \"string\" &&\n \"label\" in value &&\n typeof value.label === \"string\" &&\n \"type\" in value &&\n typeof value.type === \"string\" &&\n isPropertyFieldType(value.type)\n );\n}\n"],"mappings":";;;;;;;;;;AAwBA,SAAgB,iCACd,UACA,cAA6C,EAAE,EAC7B;CAClB,MAAM,EAAE,aAAa,GAAG,oBAAoB;AAO5C,QANmB,4BACjB,EAAE,EACF,UACA,iBACA,eAAe,QAAQ,KACxB,CACiB,gBAAgB,KAAK,EAAE,QAAQ,gBAC/C,iBAAiB,QAAQ,UAAU,CACpC;;AAGH,SAAS,iBACP,QACA,WACgB;CAChB,MAAM,YAAY,YAAY,OAAO,UAAU;AAC/C,QAAO;EACL,iBAAiB;EACjB,MAAM,OAAO;EACb;EACA,aAAa,OAAO;EACpB,aAAa,OAAO;EACpB,MAAM,OAAO;EACb,UAAU,OAAO;EACjB,gBAAgB,wBAAwB,OAAO;EAC/C,cAAc,EAAE,GAAG,OAAO,cAAc;EACxC,eAAe,OAAO;EACtB,GAAI,cAAc,KAAA,IAAY,EAAE,GAAG,EAAE,WAAW;EACjD;;AAGH,SAAS,wBACP,QACkC;CAClC,MAAM,SAAS,MAAM,QAAQ,OAAO,eAAe,OAAO,GACtD,OAAO,eAAe,OAAO,OAAO,gBAAgB,GACpD,EAAE;AACN,QAAO;EACL,GAAG,OAAO;EACV,YAAY,OAAO;EACnB,aACE,OAAO,OAAO,eAAe,gBAAgB,WACzC,OAAO,eAAe,cACtB,OAAO;EACb;EACD;;AAGH,SAAS,YACP,WAC6B;AAC7B,KAAI,cAAc,MAAO,QAAO,KAAA;AAChC,KAAI,cAAc,QAAQ,cAAc,OACtC,QAAO;EAAE,YAAY;EAAM,UAAU;EAAM;AAE7C,QAAO,cAAc,eAAe,EAAE,YAAY,MAAM,GAAG,EAAE,UAAU,MAAM;;AAG/E,SAAS,gBAAgB,OAAwC;AAC/D,QACE,OAAO,UAAU,YACjB,UAAU,QACV,SAAS,SACT,OAAO,MAAM,QAAQ,YACrB,WAAW,SACX,OAAO,MAAM,UAAU,YACvB,UAAU,SACV,OAAO,MAAM,SAAS,YACtB,oBAAoB,MAAM,KAAK"}
@@ -5,7 +5,7 @@ import { t as useAccount } from "./use-account-vIWrrngf.mjs";
5
5
  import { t as useStore } from "./use-store-BH0GPS-f.mjs";
6
6
  import { n as usePortalTenantClient } from "./PortalTenantClientProvider-Bms_pfpm.mjs";
7
7
  import { n as useAppNavigation, t as AppNavigationProvider } from "./AppNavigationContext-sWuBzay1.mjs";
8
- import { a as isErrorResult, c as selectProperty, d as useAppDefinition, l as useFluidTheme, n as getProperty, o as isIdle, r as hasData, s as isLoading, u as APP_DEFINITION_QUERY_KEY } from "./property-schema-types-DJgFKHAG.mjs";
8
+ import { a as isErrorResult, c as selectProperty, d as useAppDefinition, l as useFluidTheme, n as getProperty, o as isIdle, r as hasData, s as isLoading, u as APP_DEFINITION_QUERY_KEY } from "./hook-types-D5HQXU4S.mjs";
9
9
  import { a as groupPropertyFields, i as extractPropertyValues, n as PROPERTY_FIELD_TYPES, o as isPropertyFieldType, r as applyPropertyValues } from "./registries-B_LFR-8_.mjs";
10
10
  import { c as sourceWidgetPackagesToManifests, i as isSourceWidgetPackage, n as defineWidgetPackage, t as defineWidget } from "./authoring-CqJmyKw5.mjs";
11
11
  import * as React$1 from "react";
@@ -765,4 +765,4 @@ function dedupeWidgetPackagesByPackageIdAndVersion(widgetPackages) {
765
765
  //#endregion
766
766
  export { loadWidgetPackages as a, getDefaultWidgetPackageManifestUrl as c, useFluidProfile as d, createDefaultFluidConfig as f, installAndReadFluidWidgetsRegistry as i, useCompanySwitch as l, resolveHostedWidgetPackageManifestUrl as n, subscribeToFluidWidgetsRegistry as o, useLogout as p, fetchWidgetPackageDescriptors as r, DEFAULT_WIDGET_PACKAGE_MANIFEST_URL as s, dedupeWidgetPackagesByPackageIdAndVersion as t, PROFILE_QUERY_KEY as u };
767
767
 
768
- //# sourceMappingURL=widget-package-utils-4Jm9jLbe.mjs.map
768
+ //# sourceMappingURL=widget-package-utils-DjEsgclS.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"widget-package-utils-4Jm9jLbe.mjs","names":[],"sources":["../src/utils/reset-fairshare-sdk.ts","../src/hooks/use-logout.ts","../src/config/env.ts","../src/config/defaults.ts","../src/hooks/use-fluid-profile.ts","../src/hooks/use-company-switch.ts","../src/widgets/runtime-sdk.ts","../src/utils/widget-package-manifest-url.ts","../src/utils/load-widget-packages.ts","../src/utils/widget-package-utils.ts"],"sourcesContent":["/**\n * The FairShare SDK identity cookies that must be dropped when the portal\n * member changes. `fluid_session` maps to the server-side `session_uuid` (the\n * backend inherits a new cart's customer from the most recent cart in the same\n * session — so a stale session re-binds carts to the previous member), and\n * `fluid_authenticated_jwt` (+ its timestamp) is Bearer-synced onto carts. We\n * deliberately do NOT touch `fluid_visitor`, locale, or `fluid_affiliate`\n * (attribution is meant to persist).\n */\nconst SDK_IDENTITY_COOKIES = [\n \"fluid_session\",\n \"fluid_authenticated_jwt\",\n \"fluid_authenticated_at\",\n];\n\n/** SDK localStorage keys tied to the member's cart/identity. */\nconst SDK_IDENTITY_STORAGE = [\"fs_cart\", \"fs_authenticated_user\"];\n\n/**\n * Resets the FairShare SDK's persisted shopping identity so the next portal\n * member starts a clean session (CURRENT-2180).\n *\n * The SDK owns this state (localStorage + cookies), and its own `reset()` is\n * insufficient: it sets identity cookies WITH a `domain=` attribute (via\n * `setCookieOnApexAndSubdomains`, using the tenant host as the cookie domain)\n * but deletes them WITHOUT one, so the domain-scoped cookies survive — which is\n * exactly `fluid_session` / `fluid_authenticated_jwt`. So we call `reset()` for\n * its localStorage + in-memory teardown, then expire the identity cookies here\n * with the matching domain.\n *\n * Called on portal logout and company switch. No-op-safe when the SDK global\n * isn't present (e.g. a page that never mounted the cart script, or SSR).\n */\nexport function resetFairShareSdk(): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\n\n const sdk = (window as unknown as { FairShareSDK?: { reset?: () => void } })\n .FairShareSDK;\n\n if (typeof sdk?.reset === \"function\") {\n try {\n sdk.reset();\n } catch (error) {\n // Best-effort: never let SDK teardown block the logout/switch flow.\n console.error(\"[resetFairShareSdk] SDK reset failed:\", error);\n }\n }\n\n // Expire the identity cookies across every domain they could have been set\n // under. A browser only deletes a cookie when the delete's domain matches how\n // it was written, and these cookies are written under different scopes:\n // `fluid_session` under the tenant host (BFF cookieDomain), but\n // `fluid_authenticated_jwt` can come from a non-BFF login on a broader apex\n // (e.g. `.fluid.app` / `.portal.fluid.app`). So walk up every parent domain\n // of the current host (with and without a leading dot) plus host-only.\n const host = window.location.hostname;\n const labels = host.split(\".\");\n const domainVariants = [\"\"]; // host-only (no domain attribute)\n for (let i = 0; i < labels.length - 1; i++) {\n const domain = labels.slice(i).join(\".\");\n domainVariants.push(`; domain=${domain}`, `; domain=.${domain}`);\n }\n\n for (const name of SDK_IDENTITY_COOKIES) {\n for (const domain of domainVariants) {\n document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT${domain}`;\n }\n }\n\n for (const key of SDK_IDENTITY_STORAGE) {\n try {\n window.localStorage.removeItem(key);\n } catch {\n // localStorage may be unavailable in some contexts\n }\n }\n}\n","/**\n * useLogout Hook\n *\n * Provides a complete logout function that:\n * 1. Clears all client-side state (query cache, IndexedDB, sessionStorage)\n * 2. Navigates to GET /logout — Rails clears the session and redirects to the Hub\n */\n\nimport { useCallback } from \"react\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { deleteDatabase } from \"@fluid-app/query-persister\";\nimport { resetFairShareSdk } from \"../utils/reset-fairshare-sdk\";\n\nexport interface UseLogoutOptions {\n /** URL to redirect to after logout. Triggers a full page navigation. */\n redirectUrl?: string;\n /** Callback invoked after all state is cleared. Ignored when `redirectUrl` is set. */\n onLogout?: () => void;\n}\n\n/**\n * Hook that returns a `logout` function which clears the server session\n * via `DELETE /logout` and all cached/persisted client state.\n *\n * @example\n * ```tsx\n * const logout = useLogout({ redirectUrl: \"/login\" });\n * <button onClick={logout}>Log out</button>\n * ```\n *\n * @example\n * ```tsx\n * const logout = useLogout({\n * onLogout: () => navigate(\"/signed-out\"),\n * });\n * ```\n */\nexport function useLogout({\n redirectUrl,\n onLogout,\n}: UseLogoutOptions = {}): () => Promise<void> {\n const queryClient = useQueryClient();\n\n const logout = useCallback(async () => {\n // 1. Cancel in-flight requests to prevent cache repopulation\n await queryClient.cancelQueries();\n\n // 2. Clear in-memory TanStack Query cache\n queryClient.clear();\n\n // 3. Clear IndexedDB persisted query cache\n try {\n await deleteDatabase();\n } catch (error) {\n // Non-fatal — continue with logout even if IndexedDB cleanup fails\n console.error(\"[useLogout] Failed to clear IndexedDB cache:\", error);\n }\n\n // 4. Clear sessionStorage — intentionally clears all keys because the\n // portal app owns the full page (not embedded as a micro-frontend).\n try {\n sessionStorage.clear();\n } catch {\n // sessionStorage may be unavailable in some contexts\n }\n\n // 5. Reset the FairShare SDK's shopping identity (cart, session, auth\n // cookies). These live in the SDK's own localStorage/cookies, which the\n // steps above don't touch — leaving them lets the next member inherit\n // the previous member's cart/customer (CURRENT-2180).\n resetFairShareSdk();\n\n // 6. Navigate to the server logout endpoint. Rails clears the session\n // cookie and redirects to the Portal Hub company list. A full page\n // navigation lets the browser follow the redirect naturally — no\n // fetch or JSON parsing needed.\n if (redirectUrl) {\n window.location.href = redirectUrl;\n } else if (onLogout) {\n onLogout();\n } else {\n window.location.href = \"/logout\";\n }\n }, [queryClient, redirectUrl, onLogout]);\n\n return logout;\n}\n","/**\n * Read Vite environment variables at runtime.\n *\n * Isolated into its own module so that Jest tests can mock it\n * (import.meta is a syntax-level construct that CJS/Jest cannot parse).\n */\nexport function getViteEnv(key: string): string | undefined {\n const env = import.meta.env as unknown as Record<string, string> | undefined;\n return env?.[key];\n}\n","import type { FluidSDKConfig } from \"../types/config\";\nimport { getViteEnv } from \"./env\";\n\n/**\n * Creates a FluidSDKConfig with sensible defaults.\n *\n * Default behavior:\n * - baseUrl: reads from `VITE_API_URL` env var, falls back to `\"\"` (same-origin relative)\n * - Auth: relies on session cookies sent via `credentials: 'include'`\n *\n * Pass overrides to customize any field:\n * ```ts\n * const config = createDefaultFluidConfig({ baseUrl: \"https://my-api.example.com\" });\n * ```\n */\nexport function createDefaultFluidConfig(\n overrides?: Partial<FluidSDKConfig>,\n): FluidSDKConfig {\n return {\n baseUrl: getViteEnv(\"VITE_API_URL\") ?? \"\",\n smartyEmbeddedKey: getViteEnv(\"NEXT_PUBLIC_SMARTY_EMBEDDED_KEY\"),\n googleMapsApiKey: getViteEnv(\"NEXT_PUBLIC_GOOGLE_MAPS_API_KEY\"),\n ...overrides,\n };\n}\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useAppDefinitionApi } from \"@fluid-app/portal-core/app-definition-api-context\";\nimport { useCompanyScopedQueryKey } from \"./query-keys\";\nimport { transformManifestToRepAppData } from \"../transforms\";\nimport type { RawManifestResponse } from \"../transforms\";\nimport type { Profile } from \"../types/profile\";\nimport { APP_DATA_QUERY_KEY, fetchRawManifest } from \"./use-fluid-app\";\n\n/**\n * Base query key for profile data.\n * Kept for backwards compatibility — the runtime key used by the hook\n * includes a company prefix via {@link useCompanyScopedQueryKey}.\n *\n * @deprecated Use {@link APP_DATA_QUERY_KEY} with `useFluidApp` instead.\n */\nexport const PROFILE_QUERY_KEY = [\"fluid\", \"profile\"] as const;\n\n/**\n * Hook to fetch the portal profile (themes, navigation, screens).\n *\n * Internally fetches from the fluidos API (same data source as\n * {@link useFluidApp}) and selects the `profile` slice, so no\n * legacy `/api/rep_app/manifest` call is made.\n *\n * @deprecated Use `useFluidApp()` instead — it returns the full\n * `RepAppData` including `profile`, `screens`, and more.\n *\n * @example\n * ```tsx\n * function Navigation() {\n * const { data: profile, isLoading } = useFluidProfile();\n *\n * if (isLoading) return <Spinner />;\n *\n * return (\n * <nav>\n * {profile?.navigation.navigation_items.map(item => (\n * <NavItem key={item.id} item={item} />\n * ))}\n * </nav>\n * );\n * }\n * ```\n */\nexport function useFluidProfile(): UseQueryResult<Profile> {\n const appDefinitionApi = useAppDefinitionApi();\n const { scopeKey } = useCompanyScopedQueryKey();\n\n return useQuery<\n RawManifestResponse,\n Error,\n Profile,\n readonly (string | number)[]\n >({\n queryKey: scopeKey(APP_DATA_QUERY_KEY),\n queryFn: () => fetchRawManifest(appDefinitionApi),\n select: (raw) => {\n const appData = transformManifestToRepAppData(raw);\n return appData.profile;\n },\n });\n}\n","import { useCallback, useRef } from \"react\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { deleteDatabase } from \"@fluid-app/query-persister\";\nimport { usePortalTenantClient } from \"../providers/PortalTenantClientProvider\";\nimport { resetFairShareSdk } from \"../utils/reset-fairshare-sdk\";\n\nexport interface UseCompanySwitchOptions {\n // Preserved for API compatibility; no longer used internally.\n}\n\n/**\n * Hook that handles company switching for the portal.\n *\n * Calls PUT /api/authentication/company/{id}/switch, clears the TanStack\n * Query cache and IndexedDB persisted cache, then reloads the page so all\n * queries re-fetch with the new company context.\n *\n * The server updates the session cookie automatically via the switch\n * endpoint response; no client-side token storage is needed.\n */\nexport function useCompanySwitch(_options?: UseCompanySwitchOptions): {\n switchCompany: (companyId: number) => void;\n isPending: boolean;\n isError: boolean;\n error: Error | null;\n} {\n const client = usePortalTenantClient();\n const queryClient = useQueryClient();\n const switchingRef = useRef(false);\n\n const { mutate, isPending, isError, error } = useMutation({\n mutationFn: (companyId: number) =>\n client.put(`/api/authentication/company/${companyId}/switch`),\n onSuccess: async () => {\n // Clear in-memory cache and IndexedDB to prevent stale data\n // rehydration after reload.\n queryClient.clear();\n try {\n await deleteDatabase(\"company-switch\");\n } catch {\n // Non-fatal: company-scoped query keys prevent cross-company reads\n // even if the IDB delete fails.\n }\n // Reset the FairShare SDK's shopping identity so the new company's\n // session doesn't inherit the previous membership's cart/customer\n // (the SDK's cart/session cookies aren't company-scoped) — CURRENT-2180.\n resetFairShareSdk();\n\n // Session cookie is updated server-side by the switch endpoint.\n // Reload to re-fetch all queries with the new company context.\n window.location.reload();\n },\n onError: () => {\n switchingRef.current = false;\n },\n });\n\n const switchCompany = useCallback(\n (companyId: number) => {\n if (switchingRef.current) return;\n switchingRef.current = true;\n mutate(companyId);\n },\n [mutate],\n );\n\n return {\n switchCompany: switchCompany,\n isPending: isPending,\n isError: isError,\n error: error,\n };\n}\n","export { useAppDefinitionApi } from \"@fluid-app/portal-core/app-definition-api-context\";\n\nexport { usePortalTenantClient } from \"../providers/PortalTenantClientProvider\";\nexport { APP_DATA_QUERY_KEY, useFluidApp } from \"../hooks/use-fluid-app\";\nexport {\n APP_DEFINITION_QUERY_KEY,\n useAppDefinition,\n} from \"../hooks/use-app-definition\";\nexport { PROFILE_QUERY_KEY, useFluidProfile } from \"../hooks/use-fluid-profile\";\nexport {\n useFluidTheme,\n type UseFluidThemeResult,\n} from \"../hooks/use-fluid-theme\";\nexport {\n createCompanyQueryKey,\n useCompanyScopedQueryKey,\n} from \"../hooks/query-keys\";\nexport { useAccount } from \"../hooks/use-account\";\nexport {\n useCompanySwitch,\n type UseCompanySwitchOptions,\n} from \"../hooks/use-company-switch\";\nexport { useLogout, type UseLogoutOptions } from \"../hooks/use-logout\";\nexport { useStore } from \"../hooks/use-store\";\nexport {\n getProperty,\n hasData,\n isErrorResult,\n isIdle,\n isLoading,\n selectProperty,\n type ListQueryResult,\n type QueryResult,\n type QueryResultNullable,\n type UseListResourceHook,\n type UseSingleResourceHook,\n type ValueListQueryResult,\n type WithData,\n} from \"../hooks/hook-types\";\nexport {\n AppNavigationProvider,\n useAppNavigation,\n type AppNavigationContextValue,\n type AppNavigationProviderProps,\n} from \"../shell/AppNavigationContext\";\nexport {\n applyPropertyValues,\n extractPropertyValues,\n groupPropertyFields,\n isPropertyFieldType,\n PROPERTY_FIELD_TYPES,\n type PropertyFieldSchema,\n type PropertyFieldType,\n type TabConfig,\n} from \"../registries/property-schema-types\";\nexport {\n buildThemeDefinition,\n getActiveThemeId,\n normalizeComponentTree,\n toNavigationItem,\n toRawManifest,\n toScreenDefinition,\n transformManifestToRepAppData,\n transformThemes,\n type RawApiNavigationItem,\n type RawApiScreen,\n type RawApiTheme,\n type RawManifestResponse,\n} from \"../transforms\";\nexport {\n defineWidget,\n defineWidgetPackage,\n isSourceWidgetPackage,\n sourceWidgetPackagesToManifests,\n type DefineWidgetOptions,\n type DefineWidgetPackageOptions,\n type SourceWidget,\n type SourceWidgetPackage,\n} from \"./authoring\";\n\nexport type { AccountRep } from \"@fluid-app/portal-core/account-types\";\nexport type { AppDefinitionApi } from \"@fluid-app/portal-core/app-definition-api\";\nexport type {\n AppDefinitionResponse,\n AppFluidOsDefinition,\n AppManifest,\n AppManifestProfile,\n AppManifestResponse,\n} from \"@fluid-app/portal-core/app-definition-types\";\nexport type { WidgetManifest } from \"@fluid-app/portal-core/registries\";\nexport type { Store } from \"@fluid-app/portal-core/store-types\";\nexport type {\n RepAppData,\n RepAppManifest,\n RepAppProfile,\n} from \"@fluid-app/portal-core/types\";\nexport type { UserType } from \"@fluid-app/auth\";\nexport type { UseQueryResult } from \"@tanstack/react-query\";\nexport type { FetchClient as PortalTenantFetchClient } from \"@fluid-app/portal-tenant-api-client\";\nexport type { FluidSDKConfig } from \"../types/config\";\nexport type {\n AlignOptions,\n BackgroundType,\n BackgroundValue,\n BorderRadiusOptions,\n ButtonSizeOptions,\n ColorOptions,\n FontSizeOptions,\n GapOptions,\n PaddingOptions,\n SectionLayoutType,\n} from \"../types/field-types\";\nexport type {\n Navigation,\n NavigationItem,\n ScreenDefinition,\n} from \"../types/navigation\";\nexport type { Profile } from \"../types/profile\";\nexport type {\n ResolvedTheme,\n ThemeColorInput,\n ThemeDefinition,\n ThemePayload,\n} from \"../types/theme\";\nexport type {\n WidgetDefaultProps,\n WidgetDescriptor,\n WidgetPackageDescriptor,\n WidgetPackageType,\n WidgetRuntimeContainer,\n WidgetRuntimeResizable,\n} from \"../types/widget-package-descriptor\";\nexport type {\n TypedWidgetSchema,\n WidgetPath,\n WidgetRegistry,\n WidgetSchema,\n WidgetType,\n WidgetTypeName,\n} from \"../types/widget-schema\";\nexport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\n","export const DEFAULT_WIDGET_PACKAGE_MANIFEST_URL = \"/api/app/widget-packages\";\nexport const VITE_DEV_WIDGET_PACKAGE_MANIFEST_URL = \"/__widget-packages__\";\n\nconst VITE_DEV_MODULE_PATH_SEGMENTS = [\n \"/@fs/\",\n \"/src/\",\n \"/node_modules/.vite/\",\n \"/@id/\",\n] as const;\n\nexport interface DefaultWidgetPackageManifestUrlOptions {\n readonly isViteDev?: boolean;\n}\n\nexport function resolveDefaultWidgetPackageManifestUrl(\n moduleUrl: string | undefined,\n options: DefaultWidgetPackageManifestUrlOptions = {},\n): string | false {\n const parsedModuleUrl = parseModuleUrl(moduleUrl);\n if (!parsedModuleUrl) return false;\n\n if (options.isViteDev && isViteDevModulePath(parsedModuleUrl.pathname)) {\n return new URL(VITE_DEV_WIDGET_PACKAGE_MANIFEST_URL, parsedModuleUrl.origin)\n .href;\n }\n\n return DEFAULT_WIDGET_PACKAGE_MANIFEST_URL;\n}\n\nfunction parseModuleUrl(moduleUrl: string | undefined): URL | null {\n if (!moduleUrl) return null;\n\n try {\n return new URL(moduleUrl);\n } catch {\n return null;\n }\n}\n\nfunction isViteDevModulePath(pathname: string): boolean {\n return VITE_DEV_MODULE_PATH_SEGMENTS.some((segment) =>\n pathname.includes(segment),\n );\n}\n\nexport function getDefaultWidgetPackageManifestUrl(): string | false {\n return resolveDefaultWidgetPackageManifestUrl(import.meta.url, {\n isViteDev: isViteDevRuntime(),\n });\n}\n\nfunction isViteDevRuntime(): boolean {\n return (\n (import.meta.env as { readonly DEV?: boolean } | undefined)?.DEV === true\n );\n}\n","import * as React from \"react\";\nimport * as ReactDOM from \"react-dom\";\nimport * as ReactJsxRuntime from \"react/jsx-runtime\";\nimport {\n createWidgetRuntimeLoader,\n ensureFluidWidgetsGlobal,\n installFluidWidgetRuntimeHostGlobals,\n validateWidgetPackageDescriptor,\n type FluidWidgetsGlobalRegistryEvent,\n type FluidWidgetsGlobalUnsubscribe,\n type GlobalRegisteredWidget,\n type RegisteredWidget,\n type WidgetPackageDescriptor as RuntimeWidgetPackageDescriptor,\n} from \"@fluid-app/widget-runtime\";\nimport * as PortalSdk from \"../widgets/runtime-sdk\";\nimport {\n DEFAULT_MIN_SDK_VERSION,\n DEFAULT_WIDGET_CATEGORY,\n DEFAULT_WIDGET_ICON,\n SHARED_WIDGET_ARTIFACT_URL_PREFIX,\n hasControlCharacter,\n normalizeWidgetPackageType,\n normalizeWidgetRuntimeContainer,\n normalizeWidgetRuntimeResizable,\n readNonEmptyString,\n} from \"@fluid-app/widget-runtime/catalog\";\nimport type {\n WidgetDescriptor,\n WidgetPackageDescriptor,\n WidgetPackageType,\n WidgetRuntimeContainer,\n WidgetRuntimeResizable,\n} from \"../types/widget-package-descriptor\";\nimport { DEFAULT_WIDGET_PACKAGE_MANIFEST_URL } from \"./widget-package-manifest-url\";\n\nexport { DEFAULT_WIDGET_PACKAGE_MANIFEST_URL } from \"./widget-package-manifest-url\";\n\nconst WIDGET_PACKAGE_DESCRIPTOR_MANIFEST_VERSION =\n 1 satisfies WidgetPackageDescriptor[\"manifestVersion\"];\nconst WIDGET_PACKAGE_DESCRIPTOR_FETCH_TIMEOUT_MS = 5000;\n\nexport interface WidgetPackageLoadSummary {\n readonly registeredWidgets: readonly RegisteredWidget[];\n readonly hasFailures: boolean;\n}\n\nexport interface LoadWidgetPackagesOptions {\n /**\n * Do not reuse packages that were already present on `window.FluidWidgets`\n * before this load began. Packages loaded by this portal instance may still\n * register the same packageId/version/type and be returned.\n */\n readonly ignoreExistingGlobalRegistrations?: boolean;\n}\n\nexport interface WidgetPackageDescriptorFetchOptions {\n /** Additional trusted origins for fetched descriptor assets. */\n readonly allowedOrigins?: readonly string[];\n /** Credentials mode used for the descriptor catalog fetch. */\n readonly credentials?: RequestCredentials;\n /**\n * Trust catalog-authenticated runtime assets from the shared artifact bucket.\n * When enabled, descriptor/API-origin assets must still be listed explicitly\n * in allowedOrigins.\n */\n readonly trustCatalogAssetOrigins?: boolean;\n}\n\nexport interface FluidWidgetsRegistrySubscription {\n readonly registeredWidgets: readonly GlobalRegisteredWidget[];\n readonly unsubscribe: FluidWidgetsGlobalUnsubscribe;\n}\n\nexport type FluidWidgetsRegistryChangeListener = (\n registeredWidgets: readonly GlobalRegisteredWidget[],\n event: FluidWidgetsGlobalRegistryEvent,\n) => void;\n\nexport function installAndReadFluidWidgetsRegistry(): readonly GlobalRegisteredWidget[] {\n if (typeof window === \"undefined\") return [];\n\n return installAndReadFluidWidgetsGlobal().getRegisteredWidgets();\n}\n\nexport function subscribeToFluidWidgetsRegistry(\n listener: FluidWidgetsRegistryChangeListener,\n): FluidWidgetsRegistrySubscription {\n if (typeof window === \"undefined\") {\n return { registeredWidgets: [], unsubscribe: () => undefined };\n }\n\n const fluidWidgets = installAndReadFluidWidgetsGlobal();\n const unsubscribe = fluidWidgets.subscribe((event) => {\n listener(fluidWidgets.getRegisteredWidgets(), event);\n });\n\n return {\n registeredWidgets: fluidWidgets.getRegisteredWidgets(),\n unsubscribe,\n };\n}\n\nfunction installAndReadFluidWidgetsGlobal() {\n installFluidWidgetRuntimeHostGlobals({\n React,\n ReactDOM,\n ReactJsxRuntime,\n PortalSdk,\n });\n\n return ensureFluidWidgetsGlobal();\n}\n\nexport async function fetchWidgetPackageDescriptors(\n descriptorEndpointUrl: string = DEFAULT_WIDGET_PACKAGE_MANIFEST_URL,\n options: WidgetPackageDescriptorFetchOptions = {},\n): Promise<WidgetPackageDescriptor[]> {\n const abortSignal = createTimeoutAbortSignal(\n WIDGET_PACKAGE_DESCRIPTOR_FETCH_TIMEOUT_MS,\n );\n\n try {\n const response = await fetch(descriptorEndpointUrl, {\n credentials: options.credentials ?? \"same-origin\",\n signal: abortSignal.signal,\n });\n\n if (!response.ok) {\n throw new Error(\n `[portal] Failed to fetch widget package descriptors from ${descriptorEndpointUrl}: ${response.status} ${response.statusText}`,\n );\n }\n\n const data: unknown = await response.json();\n return parseWidgetPackageDescriptorResponse(\n data,\n descriptorEndpointUrl,\n options,\n );\n } finally {\n abortSignal.cleanup();\n }\n}\n\nexport function parseWidgetPackageDescriptorResponse(\n data: unknown,\n descriptorEndpointUrl: string = DEFAULT_WIDGET_PACKAGE_MANIFEST_URL,\n options: WidgetPackageDescriptorFetchOptions = {},\n): WidgetPackageDescriptor[] {\n const descriptorEndpointBaseUrl = normalizeDescriptorEndpointUrl(\n descriptorEndpointUrl,\n );\n if (!descriptorEndpointBaseUrl) return [];\n\n const entries = getWidgetPackageDescriptorEntries(data);\n if (!entries) return [];\n\n const assetOriginPolicy = createRuntimeAssetOriginPolicy(\n descriptorEndpointBaseUrl,\n options.allowedOrigins ?? [],\n options.trustCatalogAssetOrigins === true,\n );\n\n const descriptors: WidgetPackageDescriptor[] = [];\n for (const entry of entries) {\n const descriptor = normalizeWidgetPackageDescriptor(\n entry,\n descriptorEndpointBaseUrl,\n assetOriginPolicy,\n );\n if (descriptor) descriptors.push(descriptor);\n }\n\n return descriptors;\n}\n\nexport async function loadWidgetPackages(\n widgetPackages: readonly WidgetPackageDescriptor[],\n options: LoadWidgetPackagesOptions = {},\n): Promise<WidgetPackageLoadSummary> {\n if (widgetPackages.length === 0) {\n return { registeredWidgets: [], hasFailures: false };\n }\n\n // Installs React host globals; pre-registered widgets are merged by the caller.\n if (options.ignoreExistingGlobalRegistrations) {\n installAndReadFluidWidgetsGlobal();\n } else {\n installAndReadFluidWidgetsRegistry();\n }\n\n const loader = createWidgetRuntimeLoader({\n ignorePreRegisteredPackages:\n options.ignoreExistingGlobalRegistrations === true,\n });\n const runtimeWidgetPackages: readonly RuntimeWidgetPackageDescriptor[] =\n widgetPackages;\n const results = await loader.loadPackages(runtimeWidgetPackages);\n let hasFailures = false;\n\n for (const result of results) {\n if (!result.ok) {\n hasFailures = true;\n console.error(\n `[portal] Failed to load widget package ${result.packageId}@${result.version}:`,\n result.error,\n );\n }\n }\n\n return {\n registeredWidgets: loader.getRegisteredWidgets(),\n hasFailures,\n };\n}\n\nfunction createTimeoutAbortSignal(timeoutMs: number): {\n readonly signal: AbortSignal;\n readonly cleanup: () => void;\n} {\n if (typeof AbortSignal.timeout === \"function\") {\n return {\n signal: AbortSignal.timeout(timeoutMs),\n cleanup: () => undefined,\n };\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n return {\n signal: controller.signal,\n cleanup: () => clearTimeout(timeoutId),\n };\n}\n\nfunction getWidgetPackageDescriptorEntries(\n data: unknown,\n): readonly unknown[] | null {\n if (Array.isArray(data)) return data;\n if (!isRecord(data)) return null;\n\n const wrappedEntries =\n getArrayProperty(data, \"packages\") ??\n getArrayProperty(data, \"packageDescriptors\") ??\n getArrayProperty(data, \"widgetPackages\");\n\n if (wrappedEntries) return wrappedEntries;\n return looksLikeWidgetPackageEntry(data) ? [data] : null;\n}\n\nfunction normalizeWidgetPackageDescriptor(\n entry: unknown,\n descriptorEndpointUrl: URL,\n assetOriginPolicy: RuntimeAssetOriginPolicy,\n): WidgetPackageDescriptor | null {\n const candidate = createWidgetPackageDescriptorCandidate(entry);\n if (candidate.kind === \"skipped\") return null;\n if (candidate.kind === \"invalid\") {\n warnRejectedMalformedWidgetPackageEntry(\n entry,\n candidate.reason,\n descriptorEndpointUrl,\n );\n return null;\n }\n\n const remoteEntryUrl = normalizeRuntimeAssetUrl(\n candidate.descriptor.remoteEntryUrl,\n descriptorEndpointUrl,\n assetOriginPolicy,\n );\n if (!remoteEntryUrl.ok) {\n warnRejectedWidgetPackageDescriptor(\n candidate.descriptor,\n \"remoteEntryUrl\",\n remoteEntryUrl.failure,\n descriptorEndpointUrl,\n );\n return null;\n }\n\n const cssUrls: string[] = [];\n for (const cssUrl of candidate.descriptor.cssUrls) {\n const normalizedCssUrl = normalizeRuntimeAssetUrl(\n cssUrl,\n descriptorEndpointUrl,\n assetOriginPolicy,\n );\n if (!normalizedCssUrl.ok) {\n warnRejectedWidgetPackageDescriptor(\n candidate.descriptor,\n \"cssUrls\",\n normalizedCssUrl.failure,\n descriptorEndpointUrl,\n );\n return null;\n }\n cssUrls.push(normalizedCssUrl.url);\n }\n\n const descriptor = {\n ...candidate.descriptor,\n remoteEntryUrl: remoteEntryUrl.url,\n cssUrls,\n };\n\n const validationError = validateDescriptorCandidate(descriptor);\n if (validationError) {\n warnRejectedMalformedWidgetPackageEntry(\n entry,\n validationError,\n descriptorEndpointUrl,\n );\n return null;\n }\n\n return descriptor;\n}\n\ninterface DescriptorCandidateSuccess {\n readonly kind: \"valid\";\n readonly descriptor: WidgetPackageDescriptor;\n}\n\ninterface DescriptorCandidateInvalid {\n readonly kind: \"invalid\";\n readonly reason: string;\n}\n\ninterface DescriptorCandidateSkipped {\n readonly kind: \"skipped\";\n}\n\ntype DescriptorCandidateResult =\n | DescriptorCandidateSuccess\n | DescriptorCandidateInvalid\n | DescriptorCandidateSkipped;\n\nfunction createWidgetPackageDescriptorCandidate(\n entry: unknown,\n): DescriptorCandidateResult {\n if (!isRecord(entry)) {\n return { kind: \"invalid\", reason: \"entry must be an object\" };\n }\n const manifest = isRecord(entry.manifest) ? entry.manifest : undefined;\n if (entry.disabled === true || entry.enabled === false) {\n return { kind: \"skipped\" };\n }\n\n if (isRuntimeWidgetPackageDescriptorAttempt(entry)) {\n return createStrictRuntimeWidgetPackageDescriptorCandidate(entry);\n }\n\n if (isCatalogWidgetPackageEntry(entry, manifest)) {\n return createCatalogWidgetPackageDescriptorCandidate(entry, manifest);\n }\n\n return createRuntimeWidgetPackageDescriptorCandidate(entry, manifest);\n}\n\nfunction createStrictRuntimeWidgetPackageDescriptorCandidate(\n entry: Readonly<Record<string, unknown>>,\n): DescriptorCandidateResult {\n return createDescriptorCandidate({\n manifestVersion: normalizeManifestVersion(entry.manifestVersion, {\n defaultWhenMissing: false,\n }),\n packageId: readNonEmptyString(entry.packageId),\n packageType: normalizeWidgetPackageType(entry.packageType),\n version: readNonEmptyString(entry.version),\n remoteEntryUrl: readNonEmptyString(entry.remoteEntryUrl),\n cssUrls: normalizeRuntimeCssUrls(entry.cssUrls),\n widgets: normalizeRuntimeWidgets(entry.widgets),\n packageTypeError:\n \"packageType must be one of fluid, company, droplet, partner, or internal\",\n widgetsError: \"manifest.widgets/widgets must be a non-empty array\",\n });\n}\n\nfunction createRuntimeWidgetPackageDescriptorCandidate(\n entry: Readonly<Record<string, unknown>>,\n manifest: Readonly<Record<string, unknown>> | undefined,\n): DescriptorCandidateResult {\n const packageId =\n readNonEmptyString(entry.packageId) ??\n readNonEmptyString(manifest?.packageId);\n const version =\n readNonEmptyString(entry.version) ?? readNonEmptyString(manifest?.version);\n const remoteEntryUrl =\n readNonEmptyString(entry.remoteEntryUrl) ??\n readNonEmptyString(manifest?.remoteEntryUrl);\n const packageType =\n normalizeWidgetPackageType(entry.packageType) ??\n normalizeWidgetPackageType(manifest?.packageType);\n const manifestVersion = normalizeManifestVersion(\n entry.manifestVersion ?? manifest?.manifestVersion,\n { defaultWhenMissing: false },\n );\n const cssUrls = normalizeRuntimeCssUrls(entry.cssUrls ?? manifest?.cssUrls);\n const widgets = normalizeRuntimeWidgets(manifest?.widgets ?? entry.widgets);\n\n return createDescriptorCandidate({\n manifestVersion,\n packageId,\n packageType,\n version,\n remoteEntryUrl,\n cssUrls,\n widgets,\n packageTypeError:\n \"packageType must be one of fluid, company, droplet, partner, or internal\",\n widgetsError: \"manifest.widgets/widgets must be a non-empty array\",\n });\n}\n\nfunction createCatalogWidgetPackageDescriptorCandidate(\n entry: Readonly<Record<string, unknown>>,\n manifest: Readonly<Record<string, unknown>> | undefined,\n): DescriptorCandidateResult {\n const packageId =\n readNonEmptyString(entry.packageId) ??\n readNonEmptyString(manifest?.packageId);\n const version =\n readNonEmptyString(entry.version) ?? readNonEmptyString(manifest?.version);\n const remoteEntryUrl =\n readNonEmptyString(entry.scriptUrl) ??\n readNonEmptyString(entry.remoteEntryUrl) ??\n readNonEmptyString(manifest?.scriptUrl) ??\n readNonEmptyString(manifest?.remoteEntryUrl);\n const packageType =\n normalizeWidgetPackageType(entry.source) ??\n normalizeWidgetPackageType(entry.packageType) ??\n normalizeWidgetPackageType(manifest?.source) ??\n normalizeWidgetPackageType(manifest?.packageType) ??\n \"company\";\n const manifestVersion = normalizeManifestVersion(\n entry.manifestVersion ?? manifest?.manifestVersion,\n { defaultWhenMissing: true },\n );\n const cssUrls = normalizeCssUrls(entry.cssUrls ?? manifest?.cssUrls);\n const rawWidgets = manifest?.widgets ?? entry.widgets;\n const widgets = Array.isArray(rawWidgets)\n ? normalizeCatalogWidgets(rawWidgets, packageId)\n : null;\n\n return createDescriptorCandidate({\n manifestVersion,\n packageId,\n packageType,\n version,\n remoteEntryUrl,\n cssUrls,\n widgets,\n packageTypeError:\n \"packageType/source must be one of fluid, company, droplet, partner, internal, or omitted for company catalog packages\",\n widgetsError:\n \"manifest.widgets/widgets must contain at least one valid widget\",\n });\n}\n\ninterface DescriptorCandidateFields {\n readonly manifestVersion: WidgetPackageDescriptor[\"manifestVersion\"] | null;\n readonly packageId: string | undefined;\n readonly packageType: WidgetPackageType | null;\n readonly version: string | undefined;\n readonly remoteEntryUrl: string | undefined;\n readonly cssUrls: string[] | null;\n readonly widgets: WidgetDescriptor[] | null;\n readonly packageTypeError: string;\n readonly widgetsError: string;\n}\n\nfunction createDescriptorCandidate(\n fields: DescriptorCandidateFields,\n): DescriptorCandidateResult {\n if (!fields.manifestVersion) {\n return { kind: \"invalid\", reason: \"unsupported manifestVersion\" };\n }\n if (!fields.packageId) {\n return { kind: \"invalid\", reason: \"packageId must be a non-empty string\" };\n }\n if (!fields.version) {\n return { kind: \"invalid\", reason: \"version must be a non-empty string\" };\n }\n if (!fields.packageType) {\n return { kind: \"invalid\", reason: fields.packageTypeError };\n }\n if (!fields.remoteEntryUrl) {\n return {\n kind: \"invalid\",\n reason: \"remoteEntryUrl/scriptUrl must be a non-empty string\",\n };\n }\n if (!fields.cssUrls) {\n return { kind: \"invalid\", reason: \"cssUrls must be an array of strings\" };\n }\n if (!fields.widgets || fields.widgets.length === 0) {\n return { kind: \"invalid\", reason: fields.widgetsError };\n }\n\n return {\n kind: \"valid\",\n descriptor: {\n manifestVersion: fields.manifestVersion,\n packageId: fields.packageId,\n packageType: fields.packageType,\n version: fields.version,\n remoteEntryUrl: fields.remoteEntryUrl,\n cssUrls: fields.cssUrls,\n widgets: fields.widgets,\n },\n };\n}\n\nfunction validateDescriptorCandidate(\n descriptor: WidgetPackageDescriptor,\n): string | null {\n try {\n validateWidgetPackageDescriptor(\n descriptor as RuntimeWidgetPackageDescriptor,\n );\n return null;\n } catch (error) {\n return error instanceof Error ? error.message : String(error);\n }\n}\n\nfunction isRuntimeWidgetPackageDescriptorAttempt(\n entry: Readonly<Record<string, unknown>>,\n): boolean {\n return (\n entry.manifestVersion !== undefined &&\n entry.packageId !== undefined &&\n entry.packageType !== undefined &&\n entry.version !== undefined &&\n entry.remoteEntryUrl !== undefined &&\n entry.cssUrls !== undefined &&\n entry.widgets !== undefined\n );\n}\n\nfunction isCatalogWidgetPackageEntry(\n entry: Readonly<Record<string, unknown>>,\n manifest: Readonly<Record<string, unknown>> | undefined,\n): boolean {\n return (\n entry.source !== undefined ||\n entry.scriptUrl !== undefined ||\n entry.manifestUrl !== undefined ||\n manifest?.source !== undefined ||\n manifest?.scriptUrl !== undefined ||\n manifest?.manifestUrl !== undefined ||\n manifest?.widgets !== undefined\n );\n}\n\nfunction normalizeManifestVersion(\n value: unknown,\n options: { readonly defaultWhenMissing: boolean },\n): WidgetPackageDescriptor[\"manifestVersion\"] | null {\n if (value === WIDGET_PACKAGE_DESCRIPTOR_MANIFEST_VERSION) {\n return WIDGET_PACKAGE_DESCRIPTOR_MANIFEST_VERSION;\n }\n\n if (value === undefined && options.defaultWhenMissing) {\n return WIDGET_PACKAGE_DESCRIPTOR_MANIFEST_VERSION;\n }\n\n return null;\n}\n\nfunction normalizeCssUrls(value: unknown): string[] | null {\n return normalizeCssUrlList(value, { undefinedValue: [] });\n}\n\nfunction normalizeRuntimeCssUrls(value: unknown): string[] | null {\n return normalizeCssUrlList(value, { undefinedValue: null });\n}\n\nfunction normalizeCssUrlList(\n value: unknown,\n options: { readonly undefinedValue: string[] | null },\n): string[] | null {\n if (value === undefined) return options.undefinedValue;\n if (!Array.isArray(value)) return null;\n\n const cssUrls: string[] = [];\n for (const cssUrl of value) {\n const normalizedCssUrl = readNonEmptyString(cssUrl);\n if (!normalizedCssUrl) return null;\n cssUrls.push(normalizedCssUrl);\n }\n return cssUrls;\n}\n\nfunction normalizeRuntimeWidgets(value: unknown): WidgetDescriptor[] | null {\n if (!Array.isArray(value)) return null;\n return value as WidgetDescriptor[];\n}\n\nfunction normalizeCatalogWidgets(\n value: readonly unknown[],\n packageId: string | undefined,\n): WidgetDescriptor[] {\n const widgets: WidgetDescriptor[] = [];\n value.forEach((widget, index) => {\n const normalizedWidget = normalizeWidgetDescriptor(widget, {\n fallbackType:\n packageId === undefined\n ? undefined\n : `${packageId}-widget-${index + 1}`,\n overwritePropertySchemaWidgetType: true,\n });\n if (normalizedWidget) widgets.push(normalizedWidget);\n });\n return widgets;\n}\n\nfunction normalizeWidgetDescriptor(\n widget: unknown,\n options: {\n readonly fallbackType?: string;\n readonly overwritePropertySchemaWidgetType?: boolean;\n } = {},\n): WidgetDescriptor | null {\n if (!isRecord(widget)) return null;\n\n const type =\n readNonEmptyString(widget.type) ??\n readNonEmptyString(widget.name) ??\n options.fallbackType;\n if (!type) return null;\n\n const name = readNonEmptyString(widget.name) ?? type;\n const displayName = readNonEmptyString(widget.displayName) ?? name;\n\n return {\n type,\n name,\n displayName,\n description:\n readNonEmptyString(widget.description) ?? `Custom widget ${displayName}`,\n icon: readNonEmptyString(widget.icon) ?? DEFAULT_WIDGET_ICON,\n category: readNonEmptyString(widget.category) ?? DEFAULT_WIDGET_CATEGORY,\n propertySchema: normalizePropertySchema(widget.propertySchema, type, {\n overwriteWidgetType: options.overwritePropertySchemaWidgetType === true,\n }),\n defaultProps: isRecord(widget.defaultProps) ? widget.defaultProps : {},\n container: normalizeContainer(widget.container),\n minSdkVersion:\n readNonEmptyString(widget.minSdkVersion) ?? DEFAULT_MIN_SDK_VERSION,\n resizable: normalizeResizable(widget.resizable),\n };\n}\n\nfunction normalizePropertySchema(\n value: unknown,\n widgetType: string,\n options: { readonly overwriteWidgetType: boolean } = {\n overwriteWidgetType: false,\n },\n): WidgetDescriptor[\"propertySchema\"] {\n if (!isRecord(value)) return { widgetType };\n if (options.overwriteWidgetType || value.widgetType === undefined) {\n return { ...value, widgetType };\n }\n return value;\n}\n\nfunction normalizeContainer(value: unknown): WidgetRuntimeContainer {\n return normalizeWidgetRuntimeContainer(value);\n}\n\nfunction normalizeResizable(value: unknown): WidgetRuntimeResizable {\n return normalizeWidgetRuntimeResizable(value);\n}\n\ninterface RuntimeAssetUrlNormalizationFailure {\n readonly inputUrl: string;\n readonly resolvedUrl?: string;\n readonly reason: \"invalid-url\" | \"unsupported-protocol\" | \"untrusted-origin\";\n}\n\ntype RuntimeAssetUrlNormalizationResult =\n | { readonly ok: true; readonly url: string }\n | {\n readonly ok: false;\n readonly failure: RuntimeAssetUrlNormalizationFailure;\n };\n\ninterface RuntimeAssetOriginPolicy {\n readonly allowSharedWidgetArtifactBucket: boolean;\n readonly allowedOrigins: ReadonlySet<string>;\n}\n\nfunction normalizeRuntimeAssetUrl(\n url: string,\n baseUrl: URL,\n assetOriginPolicy: RuntimeAssetOriginPolicy,\n): RuntimeAssetUrlNormalizationResult {\n if (hasUnsafeRawRuntimeAssetUrlSyntax(url)) {\n return {\n ok: false,\n failure: {\n inputUrl: url,\n reason: \"invalid-url\",\n },\n };\n }\n\n try {\n const parsedUrl = new URL(url, baseUrl);\n if (parsedUrl.protocol !== \"http:\" && parsedUrl.protocol !== \"https:\") {\n return {\n ok: false,\n failure: {\n inputUrl: url,\n resolvedUrl: parsedUrl.href,\n reason: \"unsupported-protocol\",\n },\n };\n }\n if (assetOriginPolicy.allowedOrigins.has(parsedUrl.origin)) {\n return { ok: true, url: parsedUrl.href };\n }\n\n if (\n assetOriginPolicy.allowSharedWidgetArtifactBucket &&\n parsedUrl.href.startsWith(SHARED_WIDGET_ARTIFACT_URL_PREFIX)\n ) {\n return { ok: true, url: parsedUrl.href };\n }\n\n return {\n ok: false,\n failure: {\n inputUrl: url,\n resolvedUrl: parsedUrl.href,\n reason: \"untrusted-origin\",\n },\n };\n } catch {\n return {\n ok: false,\n failure: {\n inputUrl: url,\n reason: \"invalid-url\",\n },\n };\n }\n}\n\nfunction warnRejectedWidgetPackageDescriptor(\n descriptor: WidgetPackageDescriptor,\n assetField: \"remoteEntryUrl\" | \"cssUrls\",\n failure: RuntimeAssetUrlNormalizationFailure,\n descriptorEndpointUrl: URL,\n): void {\n console.warn(\n `[portal] Dropping widget package descriptor ${descriptor.packageId}@${descriptor.version} from ${descriptorEndpointUrl.href}: ${assetField} \"${failure.inputUrl}\" ${formatRuntimeAssetUrlFailure(failure)}.`,\n );\n}\n\nfunction warnRejectedMalformedWidgetPackageEntry(\n entry: unknown,\n reason: string,\n descriptorEndpointUrl: URL,\n): void {\n console.warn(\n `[portal] Dropping widget package catalog entry ${formatWidgetPackageEntryIdentity(entry)} from ${descriptorEndpointUrl.href}: ${reason}.`,\n );\n}\n\nfunction formatWidgetPackageEntryIdentity(entry: unknown): string {\n if (!isRecord(entry)) return \"<unknown>\";\n\n const packageId = readNonEmptyString(entry.packageId);\n const version = readNonEmptyString(entry.version);\n if (packageId && version) return `${packageId}@${version}`;\n if (packageId) return packageId;\n return \"<unknown>\";\n}\n\nfunction formatRuntimeAssetUrlFailure(\n failure: RuntimeAssetUrlNormalizationFailure,\n): string {\n const resolvedUrl = failure.resolvedUrl\n ? ` (resolved as ${failure.resolvedUrl})`\n : \"\";\n\n switch (failure.reason) {\n case \"invalid-url\":\n return \"is not a valid URL\";\n case \"unsupported-protocol\":\n return `uses an unsupported protocol${resolvedUrl}`;\n case \"untrusted-origin\":\n return `resolved to an untrusted origin${resolvedUrl}`;\n }\n}\n\nfunction createRuntimeAssetOriginPolicy(\n descriptorEndpointUrl: URL,\n allowedOrigins: readonly string[],\n trustCatalogAssetOrigins: boolean,\n): RuntimeAssetOriginPolicy {\n const origins = new Set<string>();\n if (!trustCatalogAssetOrigins) {\n origins.add(descriptorEndpointUrl.origin);\n }\n\n for (const origin of allowedOrigins) {\n const normalizedOrigin = normalizeAllowedOrigin(origin);\n if (normalizedOrigin) {\n origins.add(normalizedOrigin);\n } else {\n console.warn(\n `[portal] Ignoring invalid widget package allowed origin \"${origin}\". Expected an http(s) absolute URL origin.`,\n );\n }\n }\n\n return {\n allowSharedWidgetArtifactBucket: trustCatalogAssetOrigins,\n allowedOrigins: origins,\n };\n}\n\nfunction hasUnsafeRawRuntimeAssetUrlSyntax(url: string): boolean {\n return (\n url.trim() !== url ||\n hasControlCharacter(url) ||\n url.includes(\"\\\\\") ||\n url.startsWith(\"//\") ||\n hasAmbiguousHttpScheme(url)\n );\n}\n\nfunction hasAmbiguousHttpScheme(url: string): boolean {\n return /^https?:(?!\\/\\/)/i.test(url);\n}\n\nfunction normalizeAllowedOrigin(origin: string): string | null {\n try {\n const parsedOrigin = new URL(origin);\n if (\n parsedOrigin.protocol !== \"http:\" &&\n parsedOrigin.protocol !== \"https:\"\n ) {\n return null;\n }\n\n return parsedOrigin.origin;\n } catch {\n return null;\n }\n}\n\nfunction normalizeDescriptorEndpointUrl(url: string): URL | null {\n try {\n return new URL(url, getWindowLocationHref());\n } catch {\n return null;\n }\n}\n\nfunction getWindowLocationHref(): string | undefined {\n return typeof window === \"undefined\" ? undefined : window.location.href;\n}\n\nfunction looksLikeWidgetPackageEntry(\n value: Readonly<Record<string, unknown>>,\n): boolean {\n return (\n value.packageId !== undefined ||\n value.remoteEntryUrl !== undefined ||\n value.scriptUrl !== undefined ||\n value.manifest !== undefined ||\n value.widgets !== undefined\n );\n}\n\nfunction getArrayProperty(\n value: Readonly<Record<string, unknown>>,\n key: string,\n): readonly unknown[] | undefined {\n const property = value[key];\n return Array.isArray(property) ? property : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import type { WidgetPackageDescriptor } from \"../types/widget-package-descriptor\";\n\nexport function resolveHostedWidgetPackageManifestUrl(\n descriptorEndpointUrl: string,\n baseUrl: string,\n): string {\n try {\n return new URL(descriptorEndpointUrl, baseUrl).href;\n } catch {\n return descriptorEndpointUrl;\n }\n}\n\nexport function dedupeWidgetPackagesByPackageIdAndVersion(\n widgetPackages: readonly WidgetPackageDescriptor[],\n): WidgetPackageDescriptor[] {\n const widgetPackagesByIdAndVersion = new Map<\n string,\n WidgetPackageDescriptor\n >();\n for (const widgetPackage of widgetPackages) {\n const key = JSON.stringify([\n widgetPackage.packageId,\n widgetPackage.version,\n ]);\n if (!widgetPackagesByIdAndVersion.has(key)) {\n widgetPackagesByIdAndVersion.set(key, widgetPackage);\n }\n }\n return Array.from(widgetPackagesByIdAndVersion.values());\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"widget-package-utils-DjEsgclS.mjs","names":[],"sources":["../src/utils/reset-fairshare-sdk.ts","../src/hooks/use-logout.ts","../src/config/env.ts","../src/config/defaults.ts","../src/hooks/use-fluid-profile.ts","../src/hooks/use-company-switch.ts","../src/widgets/runtime-sdk.ts","../src/utils/widget-package-manifest-url.ts","../src/utils/load-widget-packages.ts","../src/utils/widget-package-utils.ts"],"sourcesContent":["/**\n * The FairShare SDK identity cookies that must be dropped when the portal\n * member changes. `fluid_session` maps to the server-side `session_uuid` (the\n * backend inherits a new cart's customer from the most recent cart in the same\n * session — so a stale session re-binds carts to the previous member), and\n * `fluid_authenticated_jwt` (+ its timestamp) is Bearer-synced onto carts. We\n * deliberately do NOT touch `fluid_visitor`, locale, or `fluid_affiliate`\n * (attribution is meant to persist).\n */\nconst SDK_IDENTITY_COOKIES = [\n \"fluid_session\",\n \"fluid_authenticated_jwt\",\n \"fluid_authenticated_at\",\n];\n\n/** SDK localStorage keys tied to the member's cart/identity. */\nconst SDK_IDENTITY_STORAGE = [\"fs_cart\", \"fs_authenticated_user\"];\n\n/**\n * Resets the FairShare SDK's persisted shopping identity so the next portal\n * member starts a clean session (CURRENT-2180).\n *\n * The SDK owns this state (localStorage + cookies), and its own `reset()` is\n * insufficient: it sets identity cookies WITH a `domain=` attribute (via\n * `setCookieOnApexAndSubdomains`, using the tenant host as the cookie domain)\n * but deletes them WITHOUT one, so the domain-scoped cookies survive — which is\n * exactly `fluid_session` / `fluid_authenticated_jwt`. So we call `reset()` for\n * its localStorage + in-memory teardown, then expire the identity cookies here\n * with the matching domain.\n *\n * Called on portal logout and company switch. No-op-safe when the SDK global\n * isn't present (e.g. a page that never mounted the cart script, or SSR).\n */\nexport function resetFairShareSdk(): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\n\n const sdk = (window as unknown as { FairShareSDK?: { reset?: () => void } })\n .FairShareSDK;\n\n if (typeof sdk?.reset === \"function\") {\n try {\n sdk.reset();\n } catch (error) {\n // Best-effort: never let SDK teardown block the logout/switch flow.\n console.error(\"[resetFairShareSdk] SDK reset failed:\", error);\n }\n }\n\n // Expire the identity cookies across every domain they could have been set\n // under. A browser only deletes a cookie when the delete's domain matches how\n // it was written, and these cookies are written under different scopes:\n // `fluid_session` under the tenant host (BFF cookieDomain), but\n // `fluid_authenticated_jwt` can come from a non-BFF login on a broader apex\n // (e.g. `.fluid.app` / `.portal.fluid.app`). So walk up every parent domain\n // of the current host (with and without a leading dot) plus host-only.\n const host = window.location.hostname;\n const labels = host.split(\".\");\n const domainVariants = [\"\"]; // host-only (no domain attribute)\n for (let i = 0; i < labels.length - 1; i++) {\n const domain = labels.slice(i).join(\".\");\n domainVariants.push(`; domain=${domain}`, `; domain=.${domain}`);\n }\n\n for (const name of SDK_IDENTITY_COOKIES) {\n for (const domain of domainVariants) {\n document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT${domain}`;\n }\n }\n\n for (const key of SDK_IDENTITY_STORAGE) {\n try {\n window.localStorage.removeItem(key);\n } catch {\n // localStorage may be unavailable in some contexts\n }\n }\n}\n","/**\n * useLogout Hook\n *\n * Provides a complete logout function that:\n * 1. Clears all client-side state (query cache, IndexedDB, sessionStorage)\n * 2. Navigates to GET /logout — Rails clears the session and redirects to the Hub\n */\n\nimport { useCallback } from \"react\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { deleteDatabase } from \"@fluid-app/query-persister\";\nimport { resetFairShareSdk } from \"../utils/reset-fairshare-sdk\";\n\nexport interface UseLogoutOptions {\n /** URL to redirect to after logout. Triggers a full page navigation. */\n redirectUrl?: string;\n /** Callback invoked after all state is cleared. Ignored when `redirectUrl` is set. */\n onLogout?: () => void;\n}\n\n/**\n * Hook that returns a `logout` function which clears the server session\n * via `DELETE /logout` and all cached/persisted client state.\n *\n * @example\n * ```tsx\n * const logout = useLogout({ redirectUrl: \"/login\" });\n * <button onClick={logout}>Log out</button>\n * ```\n *\n * @example\n * ```tsx\n * const logout = useLogout({\n * onLogout: () => navigate(\"/signed-out\"),\n * });\n * ```\n */\nexport function useLogout({\n redirectUrl,\n onLogout,\n}: UseLogoutOptions = {}): () => Promise<void> {\n const queryClient = useQueryClient();\n\n const logout = useCallback(async () => {\n // 1. Cancel in-flight requests to prevent cache repopulation\n await queryClient.cancelQueries();\n\n // 2. Clear in-memory TanStack Query cache\n queryClient.clear();\n\n // 3. Clear IndexedDB persisted query cache\n try {\n await deleteDatabase();\n } catch (error) {\n // Non-fatal — continue with logout even if IndexedDB cleanup fails\n console.error(\"[useLogout] Failed to clear IndexedDB cache:\", error);\n }\n\n // 4. Clear sessionStorage — intentionally clears all keys because the\n // portal app owns the full page (not embedded as a micro-frontend).\n try {\n sessionStorage.clear();\n } catch {\n // sessionStorage may be unavailable in some contexts\n }\n\n // 5. Reset the FairShare SDK's shopping identity (cart, session, auth\n // cookies). These live in the SDK's own localStorage/cookies, which the\n // steps above don't touch — leaving them lets the next member inherit\n // the previous member's cart/customer (CURRENT-2180).\n resetFairShareSdk();\n\n // 6. Navigate to the server logout endpoint. Rails clears the session\n // cookie and redirects to the Portal Hub company list. A full page\n // navigation lets the browser follow the redirect naturally — no\n // fetch or JSON parsing needed.\n if (redirectUrl) {\n window.location.href = redirectUrl;\n } else if (onLogout) {\n onLogout();\n } else {\n window.location.href = \"/logout\";\n }\n }, [queryClient, redirectUrl, onLogout]);\n\n return logout;\n}\n","/**\n * Read Vite environment variables at runtime.\n *\n * Isolated into its own module so that Jest tests can mock it\n * (import.meta is a syntax-level construct that CJS/Jest cannot parse).\n */\nexport function getViteEnv(key: string): string | undefined {\n const env = import.meta.env as unknown as Record<string, string> | undefined;\n return env?.[key];\n}\n","import type { FluidSDKConfig } from \"../types/config\";\nimport { getViteEnv } from \"./env\";\n\n/**\n * Creates a FluidSDKConfig with sensible defaults.\n *\n * Default behavior:\n * - baseUrl: reads from `VITE_API_URL` env var, falls back to `\"\"` (same-origin relative)\n * - Auth: relies on session cookies sent via `credentials: 'include'`\n *\n * Pass overrides to customize any field:\n * ```ts\n * const config = createDefaultFluidConfig({ baseUrl: \"https://my-api.example.com\" });\n * ```\n */\nexport function createDefaultFluidConfig(\n overrides?: Partial<FluidSDKConfig>,\n): FluidSDKConfig {\n return {\n baseUrl: getViteEnv(\"VITE_API_URL\") ?? \"\",\n smartyEmbeddedKey: getViteEnv(\"NEXT_PUBLIC_SMARTY_EMBEDDED_KEY\"),\n googleMapsApiKey: getViteEnv(\"NEXT_PUBLIC_GOOGLE_MAPS_API_KEY\"),\n ...overrides,\n };\n}\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useAppDefinitionApi } from \"@fluid-app/portal-core/app-definition-api-context\";\nimport { useCompanyScopedQueryKey } from \"./query-keys\";\nimport { transformManifestToRepAppData } from \"../transforms\";\nimport type { RawManifestResponse } from \"../transforms\";\nimport type { Profile } from \"../types/profile\";\nimport { APP_DATA_QUERY_KEY, fetchRawManifest } from \"./use-fluid-app\";\n\n/**\n * Base query key for profile data.\n * Kept for backwards compatibility — the runtime key used by the hook\n * includes a company prefix via {@link useCompanyScopedQueryKey}.\n *\n * @deprecated Use {@link APP_DATA_QUERY_KEY} with `useFluidApp` instead.\n */\nexport const PROFILE_QUERY_KEY = [\"fluid\", \"profile\"] as const;\n\n/**\n * Hook to fetch the portal profile (themes, navigation, screens).\n *\n * Internally fetches from the fluidos API (same data source as\n * {@link useFluidApp}) and selects the `profile` slice, so no\n * legacy `/api/rep_app/manifest` call is made.\n *\n * @deprecated Use `useFluidApp()` instead — it returns the full\n * `RepAppData` including `profile`, `screens`, and more.\n *\n * @example\n * ```tsx\n * function Navigation() {\n * const { data: profile, isLoading } = useFluidProfile();\n *\n * if (isLoading) return <Spinner />;\n *\n * return (\n * <nav>\n * {profile?.navigation.navigation_items.map(item => (\n * <NavItem key={item.id} item={item} />\n * ))}\n * </nav>\n * );\n * }\n * ```\n */\nexport function useFluidProfile(): UseQueryResult<Profile> {\n const appDefinitionApi = useAppDefinitionApi();\n const { scopeKey } = useCompanyScopedQueryKey();\n\n return useQuery<\n RawManifestResponse,\n Error,\n Profile,\n readonly (string | number)[]\n >({\n queryKey: scopeKey(APP_DATA_QUERY_KEY),\n queryFn: () => fetchRawManifest(appDefinitionApi),\n select: (raw) => {\n const appData = transformManifestToRepAppData(raw);\n return appData.profile;\n },\n });\n}\n","import { useCallback, useRef } from \"react\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { deleteDatabase } from \"@fluid-app/query-persister\";\nimport { usePortalTenantClient } from \"../providers/PortalTenantClientProvider\";\nimport { resetFairShareSdk } from \"../utils/reset-fairshare-sdk\";\n\nexport interface UseCompanySwitchOptions {\n // Preserved for API compatibility; no longer used internally.\n}\n\n/**\n * Hook that handles company switching for the portal.\n *\n * Calls PUT /api/authentication/company/{id}/switch, clears the TanStack\n * Query cache and IndexedDB persisted cache, then reloads the page so all\n * queries re-fetch with the new company context.\n *\n * The server updates the session cookie automatically via the switch\n * endpoint response; no client-side token storage is needed.\n */\nexport function useCompanySwitch(_options?: UseCompanySwitchOptions): {\n switchCompany: (companyId: number) => void;\n isPending: boolean;\n isError: boolean;\n error: Error | null;\n} {\n const client = usePortalTenantClient();\n const queryClient = useQueryClient();\n const switchingRef = useRef(false);\n\n const { mutate, isPending, isError, error } = useMutation({\n mutationFn: (companyId: number) =>\n client.put(`/api/authentication/company/${companyId}/switch`),\n onSuccess: async () => {\n // Clear in-memory cache and IndexedDB to prevent stale data\n // rehydration after reload.\n queryClient.clear();\n try {\n await deleteDatabase(\"company-switch\");\n } catch {\n // Non-fatal: company-scoped query keys prevent cross-company reads\n // even if the IDB delete fails.\n }\n // Reset the FairShare SDK's shopping identity so the new company's\n // session doesn't inherit the previous membership's cart/customer\n // (the SDK's cart/session cookies aren't company-scoped) — CURRENT-2180.\n resetFairShareSdk();\n\n // Session cookie is updated server-side by the switch endpoint.\n // Reload to re-fetch all queries with the new company context.\n window.location.reload();\n },\n onError: () => {\n switchingRef.current = false;\n },\n });\n\n const switchCompany = useCallback(\n (companyId: number) => {\n if (switchingRef.current) return;\n switchingRef.current = true;\n mutate(companyId);\n },\n [mutate],\n );\n\n return {\n switchCompany: switchCompany,\n isPending: isPending,\n isError: isError,\n error: error,\n };\n}\n","export { useAppDefinitionApi } from \"@fluid-app/portal-core/app-definition-api-context\";\n\nexport { usePortalTenantClient } from \"../providers/PortalTenantClientProvider\";\nexport { APP_DATA_QUERY_KEY, useFluidApp } from \"../hooks/use-fluid-app\";\nexport {\n APP_DEFINITION_QUERY_KEY,\n useAppDefinition,\n} from \"../hooks/use-app-definition\";\nexport { PROFILE_QUERY_KEY, useFluidProfile } from \"../hooks/use-fluid-profile\";\nexport {\n useFluidTheme,\n type UseFluidThemeResult,\n} from \"../hooks/use-fluid-theme\";\nexport {\n createCompanyQueryKey,\n useCompanyScopedQueryKey,\n} from \"../hooks/query-keys\";\nexport { useAccount } from \"../hooks/use-account\";\nexport {\n useCompanySwitch,\n type UseCompanySwitchOptions,\n} from \"../hooks/use-company-switch\";\nexport { useLogout, type UseLogoutOptions } from \"../hooks/use-logout\";\nexport { useStore } from \"../hooks/use-store\";\nexport {\n getProperty,\n hasData,\n isErrorResult,\n isIdle,\n isLoading,\n selectProperty,\n type ListQueryResult,\n type QueryResult,\n type QueryResultNullable,\n type UseListResourceHook,\n type UseSingleResourceHook,\n type ValueListQueryResult,\n type WithData,\n} from \"../hooks/hook-types\";\nexport {\n AppNavigationProvider,\n useAppNavigation,\n type AppNavigationContextValue,\n type AppNavigationProviderProps,\n} from \"../shell/AppNavigationContext\";\nexport {\n applyPropertyValues,\n extractPropertyValues,\n groupPropertyFields,\n isPropertyFieldType,\n PROPERTY_FIELD_TYPES,\n type PropertyFieldSchema,\n type PropertyFieldType,\n type TabConfig,\n} from \"../registries/property-schema-types\";\nexport {\n buildThemeDefinition,\n getActiveThemeId,\n normalizeComponentTree,\n toNavigationItem,\n toRawManifest,\n toScreenDefinition,\n transformManifestToRepAppData,\n transformThemes,\n type RawApiNavigationItem,\n type RawApiScreen,\n type RawApiTheme,\n type RawManifestResponse,\n} from \"../transforms\";\nexport {\n defineWidget,\n defineWidgetPackage,\n isSourceWidgetPackage,\n sourceWidgetPackagesToManifests,\n type DefineWidgetOptions,\n type DefineWidgetPackageOptions,\n type SourceWidget,\n type SourceWidgetPackage,\n} from \"./authoring\";\n\nexport type { AccountRep } from \"@fluid-app/portal-core/account-types\";\nexport type { AppDefinitionApi } from \"@fluid-app/portal-core/app-definition-api\";\nexport type {\n AppDefinitionResponse,\n AppFluidOsDefinition,\n AppManifest,\n AppManifestProfile,\n AppManifestResponse,\n} from \"@fluid-app/portal-core/app-definition-types\";\nexport type { WidgetManifest } from \"@fluid-app/portal-core/registries\";\nexport type { Store } from \"@fluid-app/portal-core/store-types\";\nexport type {\n RepAppData,\n RepAppManifest,\n RepAppProfile,\n} from \"@fluid-app/portal-core/types\";\nexport type { UserType } from \"@fluid-app/auth\";\nexport type { UseQueryResult } from \"@tanstack/react-query\";\nexport type { FetchClient as PortalTenantFetchClient } from \"@fluid-app/portal-tenant-api-client\";\nexport type { FluidSDKConfig } from \"../types/config\";\nexport type {\n AlignOptions,\n BackgroundType,\n BackgroundValue,\n BorderRadiusOptions,\n ButtonSizeOptions,\n ColorOptions,\n FontSizeOptions,\n GapOptions,\n PaddingOptions,\n SectionLayoutType,\n} from \"../types/field-types\";\nexport type {\n Navigation,\n NavigationItem,\n ScreenDefinition,\n} from \"../types/navigation\";\nexport type { Profile } from \"../types/profile\";\nexport type {\n ResolvedTheme,\n ThemeColorInput,\n ThemeDefinition,\n ThemePayload,\n} from \"../types/theme\";\nexport type {\n WidgetDefaultProps,\n WidgetDescriptor,\n WidgetPackageDescriptor,\n WidgetPackageType,\n WidgetRuntimeContainer,\n WidgetRuntimeResizable,\n} from \"../types/widget-package-descriptor\";\nexport type {\n TypedWidgetSchema,\n WidgetPath,\n WidgetRegistry,\n WidgetSchema,\n WidgetType,\n WidgetTypeName,\n} from \"../types/widget-schema\";\nexport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\n","export const DEFAULT_WIDGET_PACKAGE_MANIFEST_URL = \"/api/app/widget-packages\";\nexport const VITE_DEV_WIDGET_PACKAGE_MANIFEST_URL = \"/__widget-packages__\";\n\nconst VITE_DEV_MODULE_PATH_SEGMENTS = [\n \"/@fs/\",\n \"/src/\",\n \"/node_modules/.vite/\",\n \"/@id/\",\n] as const;\n\nexport interface DefaultWidgetPackageManifestUrlOptions {\n readonly isViteDev?: boolean;\n}\n\nexport function resolveDefaultWidgetPackageManifestUrl(\n moduleUrl: string | undefined,\n options: DefaultWidgetPackageManifestUrlOptions = {},\n): string | false {\n const parsedModuleUrl = parseModuleUrl(moduleUrl);\n if (!parsedModuleUrl) return false;\n\n if (options.isViteDev && isViteDevModulePath(parsedModuleUrl.pathname)) {\n return new URL(VITE_DEV_WIDGET_PACKAGE_MANIFEST_URL, parsedModuleUrl.origin)\n .href;\n }\n\n return DEFAULT_WIDGET_PACKAGE_MANIFEST_URL;\n}\n\nfunction parseModuleUrl(moduleUrl: string | undefined): URL | null {\n if (!moduleUrl) return null;\n\n try {\n return new URL(moduleUrl);\n } catch {\n return null;\n }\n}\n\nfunction isViteDevModulePath(pathname: string): boolean {\n return VITE_DEV_MODULE_PATH_SEGMENTS.some((segment) =>\n pathname.includes(segment),\n );\n}\n\nexport function getDefaultWidgetPackageManifestUrl(): string | false {\n return resolveDefaultWidgetPackageManifestUrl(import.meta.url, {\n isViteDev: isViteDevRuntime(),\n });\n}\n\nfunction isViteDevRuntime(): boolean {\n return (\n (import.meta.env as { readonly DEV?: boolean } | undefined)?.DEV === true\n );\n}\n","import * as React from \"react\";\nimport * as ReactDOM from \"react-dom\";\nimport * as ReactJsxRuntime from \"react/jsx-runtime\";\nimport {\n createWidgetRuntimeLoader,\n ensureFluidWidgetsGlobal,\n installFluidWidgetRuntimeHostGlobals,\n validateWidgetPackageDescriptor,\n type FluidWidgetsGlobalRegistryEvent,\n type FluidWidgetsGlobalUnsubscribe,\n type GlobalRegisteredWidget,\n type RegisteredWidget,\n type WidgetPackageDescriptor as RuntimeWidgetPackageDescriptor,\n} from \"@fluid-app/widget-runtime\";\nimport * as PortalSdk from \"../widgets/runtime-sdk\";\nimport {\n DEFAULT_MIN_SDK_VERSION,\n DEFAULT_WIDGET_CATEGORY,\n DEFAULT_WIDGET_ICON,\n SHARED_WIDGET_ARTIFACT_URL_PREFIX,\n hasControlCharacter,\n normalizeWidgetPackageType,\n normalizeWidgetRuntimeContainer,\n normalizeWidgetRuntimeResizable,\n readNonEmptyString,\n} from \"@fluid-app/widget-runtime/catalog\";\nimport type {\n WidgetDescriptor,\n WidgetPackageDescriptor,\n WidgetPackageType,\n WidgetRuntimeContainer,\n WidgetRuntimeResizable,\n} from \"../types/widget-package-descriptor\";\nimport { DEFAULT_WIDGET_PACKAGE_MANIFEST_URL } from \"./widget-package-manifest-url\";\n\nexport { DEFAULT_WIDGET_PACKAGE_MANIFEST_URL } from \"./widget-package-manifest-url\";\n\nconst WIDGET_PACKAGE_DESCRIPTOR_MANIFEST_VERSION =\n 1 satisfies WidgetPackageDescriptor[\"manifestVersion\"];\nconst WIDGET_PACKAGE_DESCRIPTOR_FETCH_TIMEOUT_MS = 5000;\n\nexport interface WidgetPackageLoadSummary {\n readonly registeredWidgets: readonly RegisteredWidget[];\n readonly hasFailures: boolean;\n}\n\nexport interface LoadWidgetPackagesOptions {\n /**\n * Do not reuse packages that were already present on `window.FluidWidgets`\n * before this load began. Packages loaded by this portal instance may still\n * register the same packageId/version/type and be returned.\n */\n readonly ignoreExistingGlobalRegistrations?: boolean;\n}\n\nexport interface WidgetPackageDescriptorFetchOptions {\n /** Additional trusted origins for fetched descriptor assets. */\n readonly allowedOrigins?: readonly string[];\n /** Credentials mode used for the descriptor catalog fetch. */\n readonly credentials?: RequestCredentials;\n /**\n * Trust catalog-authenticated runtime assets from the shared artifact bucket.\n * When enabled, descriptor/API-origin assets must still be listed explicitly\n * in allowedOrigins.\n */\n readonly trustCatalogAssetOrigins?: boolean;\n}\n\nexport interface FluidWidgetsRegistrySubscription {\n readonly registeredWidgets: readonly GlobalRegisteredWidget[];\n readonly unsubscribe: FluidWidgetsGlobalUnsubscribe;\n}\n\nexport type FluidWidgetsRegistryChangeListener = (\n registeredWidgets: readonly GlobalRegisteredWidget[],\n event: FluidWidgetsGlobalRegistryEvent,\n) => void;\n\nexport function installAndReadFluidWidgetsRegistry(): readonly GlobalRegisteredWidget[] {\n if (typeof window === \"undefined\") return [];\n\n return installAndReadFluidWidgetsGlobal().getRegisteredWidgets();\n}\n\nexport function subscribeToFluidWidgetsRegistry(\n listener: FluidWidgetsRegistryChangeListener,\n): FluidWidgetsRegistrySubscription {\n if (typeof window === \"undefined\") {\n return { registeredWidgets: [], unsubscribe: () => undefined };\n }\n\n const fluidWidgets = installAndReadFluidWidgetsGlobal();\n const unsubscribe = fluidWidgets.subscribe((event) => {\n listener(fluidWidgets.getRegisteredWidgets(), event);\n });\n\n return {\n registeredWidgets: fluidWidgets.getRegisteredWidgets(),\n unsubscribe,\n };\n}\n\nfunction installAndReadFluidWidgetsGlobal() {\n installFluidWidgetRuntimeHostGlobals({\n React,\n ReactDOM,\n ReactJsxRuntime,\n PortalSdk,\n });\n\n return ensureFluidWidgetsGlobal();\n}\n\nexport async function fetchWidgetPackageDescriptors(\n descriptorEndpointUrl: string = DEFAULT_WIDGET_PACKAGE_MANIFEST_URL,\n options: WidgetPackageDescriptorFetchOptions = {},\n): Promise<WidgetPackageDescriptor[]> {\n const abortSignal = createTimeoutAbortSignal(\n WIDGET_PACKAGE_DESCRIPTOR_FETCH_TIMEOUT_MS,\n );\n\n try {\n const response = await fetch(descriptorEndpointUrl, {\n credentials: options.credentials ?? \"same-origin\",\n signal: abortSignal.signal,\n });\n\n if (!response.ok) {\n throw new Error(\n `[portal] Failed to fetch widget package descriptors from ${descriptorEndpointUrl}: ${response.status} ${response.statusText}`,\n );\n }\n\n const data: unknown = await response.json();\n return parseWidgetPackageDescriptorResponse(\n data,\n descriptorEndpointUrl,\n options,\n );\n } finally {\n abortSignal.cleanup();\n }\n}\n\nexport function parseWidgetPackageDescriptorResponse(\n data: unknown,\n descriptorEndpointUrl: string = DEFAULT_WIDGET_PACKAGE_MANIFEST_URL,\n options: WidgetPackageDescriptorFetchOptions = {},\n): WidgetPackageDescriptor[] {\n const descriptorEndpointBaseUrl = normalizeDescriptorEndpointUrl(\n descriptorEndpointUrl,\n );\n if (!descriptorEndpointBaseUrl) return [];\n\n const entries = getWidgetPackageDescriptorEntries(data);\n if (!entries) return [];\n\n const assetOriginPolicy = createRuntimeAssetOriginPolicy(\n descriptorEndpointBaseUrl,\n options.allowedOrigins ?? [],\n options.trustCatalogAssetOrigins === true,\n );\n\n const descriptors: WidgetPackageDescriptor[] = [];\n for (const entry of entries) {\n const descriptor = normalizeWidgetPackageDescriptor(\n entry,\n descriptorEndpointBaseUrl,\n assetOriginPolicy,\n );\n if (descriptor) descriptors.push(descriptor);\n }\n\n return descriptors;\n}\n\nexport async function loadWidgetPackages(\n widgetPackages: readonly WidgetPackageDescriptor[],\n options: LoadWidgetPackagesOptions = {},\n): Promise<WidgetPackageLoadSummary> {\n if (widgetPackages.length === 0) {\n return { registeredWidgets: [], hasFailures: false };\n }\n\n // Installs React host globals; pre-registered widgets are merged by the caller.\n if (options.ignoreExistingGlobalRegistrations) {\n installAndReadFluidWidgetsGlobal();\n } else {\n installAndReadFluidWidgetsRegistry();\n }\n\n const loader = createWidgetRuntimeLoader({\n ignorePreRegisteredPackages:\n options.ignoreExistingGlobalRegistrations === true,\n });\n const runtimeWidgetPackages: readonly RuntimeWidgetPackageDescriptor[] =\n widgetPackages;\n const results = await loader.loadPackages(runtimeWidgetPackages);\n let hasFailures = false;\n\n for (const result of results) {\n if (!result.ok) {\n hasFailures = true;\n console.error(\n `[portal] Failed to load widget package ${result.packageId}@${result.version}:`,\n result.error,\n );\n }\n }\n\n return {\n registeredWidgets: loader.getRegisteredWidgets(),\n hasFailures,\n };\n}\n\nfunction createTimeoutAbortSignal(timeoutMs: number): {\n readonly signal: AbortSignal;\n readonly cleanup: () => void;\n} {\n if (typeof AbortSignal.timeout === \"function\") {\n return {\n signal: AbortSignal.timeout(timeoutMs),\n cleanup: () => undefined,\n };\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n return {\n signal: controller.signal,\n cleanup: () => clearTimeout(timeoutId),\n };\n}\n\nfunction getWidgetPackageDescriptorEntries(\n data: unknown,\n): readonly unknown[] | null {\n if (Array.isArray(data)) return data;\n if (!isRecord(data)) return null;\n\n const wrappedEntries =\n getArrayProperty(data, \"packages\") ??\n getArrayProperty(data, \"packageDescriptors\") ??\n getArrayProperty(data, \"widgetPackages\");\n\n if (wrappedEntries) return wrappedEntries;\n return looksLikeWidgetPackageEntry(data) ? [data] : null;\n}\n\nfunction normalizeWidgetPackageDescriptor(\n entry: unknown,\n descriptorEndpointUrl: URL,\n assetOriginPolicy: RuntimeAssetOriginPolicy,\n): WidgetPackageDescriptor | null {\n const candidate = createWidgetPackageDescriptorCandidate(entry);\n if (candidate.kind === \"skipped\") return null;\n if (candidate.kind === \"invalid\") {\n warnRejectedMalformedWidgetPackageEntry(\n entry,\n candidate.reason,\n descriptorEndpointUrl,\n );\n return null;\n }\n\n const remoteEntryUrl = normalizeRuntimeAssetUrl(\n candidate.descriptor.remoteEntryUrl,\n descriptorEndpointUrl,\n assetOriginPolicy,\n );\n if (!remoteEntryUrl.ok) {\n warnRejectedWidgetPackageDescriptor(\n candidate.descriptor,\n \"remoteEntryUrl\",\n remoteEntryUrl.failure,\n descriptorEndpointUrl,\n );\n return null;\n }\n\n const cssUrls: string[] = [];\n for (const cssUrl of candidate.descriptor.cssUrls) {\n const normalizedCssUrl = normalizeRuntimeAssetUrl(\n cssUrl,\n descriptorEndpointUrl,\n assetOriginPolicy,\n );\n if (!normalizedCssUrl.ok) {\n warnRejectedWidgetPackageDescriptor(\n candidate.descriptor,\n \"cssUrls\",\n normalizedCssUrl.failure,\n descriptorEndpointUrl,\n );\n return null;\n }\n cssUrls.push(normalizedCssUrl.url);\n }\n\n const descriptor = {\n ...candidate.descriptor,\n remoteEntryUrl: remoteEntryUrl.url,\n cssUrls,\n };\n\n const validationError = validateDescriptorCandidate(descriptor);\n if (validationError) {\n warnRejectedMalformedWidgetPackageEntry(\n entry,\n validationError,\n descriptorEndpointUrl,\n );\n return null;\n }\n\n return descriptor;\n}\n\ninterface DescriptorCandidateSuccess {\n readonly kind: \"valid\";\n readonly descriptor: WidgetPackageDescriptor;\n}\n\ninterface DescriptorCandidateInvalid {\n readonly kind: \"invalid\";\n readonly reason: string;\n}\n\ninterface DescriptorCandidateSkipped {\n readonly kind: \"skipped\";\n}\n\ntype DescriptorCandidateResult =\n | DescriptorCandidateSuccess\n | DescriptorCandidateInvalid\n | DescriptorCandidateSkipped;\n\nfunction createWidgetPackageDescriptorCandidate(\n entry: unknown,\n): DescriptorCandidateResult {\n if (!isRecord(entry)) {\n return { kind: \"invalid\", reason: \"entry must be an object\" };\n }\n const manifest = isRecord(entry.manifest) ? entry.manifest : undefined;\n if (entry.disabled === true || entry.enabled === false) {\n return { kind: \"skipped\" };\n }\n\n if (isRuntimeWidgetPackageDescriptorAttempt(entry)) {\n return createStrictRuntimeWidgetPackageDescriptorCandidate(entry);\n }\n\n if (isCatalogWidgetPackageEntry(entry, manifest)) {\n return createCatalogWidgetPackageDescriptorCandidate(entry, manifest);\n }\n\n return createRuntimeWidgetPackageDescriptorCandidate(entry, manifest);\n}\n\nfunction createStrictRuntimeWidgetPackageDescriptorCandidate(\n entry: Readonly<Record<string, unknown>>,\n): DescriptorCandidateResult {\n return createDescriptorCandidate({\n manifestVersion: normalizeManifestVersion(entry.manifestVersion, {\n defaultWhenMissing: false,\n }),\n packageId: readNonEmptyString(entry.packageId),\n packageType: normalizeWidgetPackageType(entry.packageType),\n version: readNonEmptyString(entry.version),\n remoteEntryUrl: readNonEmptyString(entry.remoteEntryUrl),\n cssUrls: normalizeRuntimeCssUrls(entry.cssUrls),\n widgets: normalizeRuntimeWidgets(entry.widgets),\n packageTypeError:\n \"packageType must be one of fluid, company, droplet, partner, or internal\",\n widgetsError: \"manifest.widgets/widgets must be a non-empty array\",\n });\n}\n\nfunction createRuntimeWidgetPackageDescriptorCandidate(\n entry: Readonly<Record<string, unknown>>,\n manifest: Readonly<Record<string, unknown>> | undefined,\n): DescriptorCandidateResult {\n const packageId =\n readNonEmptyString(entry.packageId) ??\n readNonEmptyString(manifest?.packageId);\n const version =\n readNonEmptyString(entry.version) ?? readNonEmptyString(manifest?.version);\n const remoteEntryUrl =\n readNonEmptyString(entry.remoteEntryUrl) ??\n readNonEmptyString(manifest?.remoteEntryUrl);\n const packageType =\n normalizeWidgetPackageType(entry.packageType) ??\n normalizeWidgetPackageType(manifest?.packageType);\n const manifestVersion = normalizeManifestVersion(\n entry.manifestVersion ?? manifest?.manifestVersion,\n { defaultWhenMissing: false },\n );\n const cssUrls = normalizeRuntimeCssUrls(entry.cssUrls ?? manifest?.cssUrls);\n const widgets = normalizeRuntimeWidgets(manifest?.widgets ?? entry.widgets);\n\n return createDescriptorCandidate({\n manifestVersion,\n packageId,\n packageType,\n version,\n remoteEntryUrl,\n cssUrls,\n widgets,\n packageTypeError:\n \"packageType must be one of fluid, company, droplet, partner, or internal\",\n widgetsError: \"manifest.widgets/widgets must be a non-empty array\",\n });\n}\n\nfunction createCatalogWidgetPackageDescriptorCandidate(\n entry: Readonly<Record<string, unknown>>,\n manifest: Readonly<Record<string, unknown>> | undefined,\n): DescriptorCandidateResult {\n const packageId =\n readNonEmptyString(entry.packageId) ??\n readNonEmptyString(manifest?.packageId);\n const version =\n readNonEmptyString(entry.version) ?? readNonEmptyString(manifest?.version);\n const remoteEntryUrl =\n readNonEmptyString(entry.scriptUrl) ??\n readNonEmptyString(entry.remoteEntryUrl) ??\n readNonEmptyString(manifest?.scriptUrl) ??\n readNonEmptyString(manifest?.remoteEntryUrl);\n const packageType =\n normalizeWidgetPackageType(entry.source) ??\n normalizeWidgetPackageType(entry.packageType) ??\n normalizeWidgetPackageType(manifest?.source) ??\n normalizeWidgetPackageType(manifest?.packageType) ??\n \"company\";\n const manifestVersion = normalizeManifestVersion(\n entry.manifestVersion ?? manifest?.manifestVersion,\n { defaultWhenMissing: true },\n );\n const cssUrls = normalizeCssUrls(entry.cssUrls ?? manifest?.cssUrls);\n const rawWidgets = manifest?.widgets ?? entry.widgets;\n const widgets = Array.isArray(rawWidgets)\n ? normalizeCatalogWidgets(rawWidgets, packageId)\n : null;\n\n return createDescriptorCandidate({\n manifestVersion,\n packageId,\n packageType,\n version,\n remoteEntryUrl,\n cssUrls,\n widgets,\n packageTypeError:\n \"packageType/source must be one of fluid, company, droplet, partner, internal, or omitted for company catalog packages\",\n widgetsError:\n \"manifest.widgets/widgets must contain at least one valid widget\",\n });\n}\n\ninterface DescriptorCandidateFields {\n readonly manifestVersion: WidgetPackageDescriptor[\"manifestVersion\"] | null;\n readonly packageId: string | undefined;\n readonly packageType: WidgetPackageType | null;\n readonly version: string | undefined;\n readonly remoteEntryUrl: string | undefined;\n readonly cssUrls: string[] | null;\n readonly widgets: WidgetDescriptor[] | null;\n readonly packageTypeError: string;\n readonly widgetsError: string;\n}\n\nfunction createDescriptorCandidate(\n fields: DescriptorCandidateFields,\n): DescriptorCandidateResult {\n if (!fields.manifestVersion) {\n return { kind: \"invalid\", reason: \"unsupported manifestVersion\" };\n }\n if (!fields.packageId) {\n return { kind: \"invalid\", reason: \"packageId must be a non-empty string\" };\n }\n if (!fields.version) {\n return { kind: \"invalid\", reason: \"version must be a non-empty string\" };\n }\n if (!fields.packageType) {\n return { kind: \"invalid\", reason: fields.packageTypeError };\n }\n if (!fields.remoteEntryUrl) {\n return {\n kind: \"invalid\",\n reason: \"remoteEntryUrl/scriptUrl must be a non-empty string\",\n };\n }\n if (!fields.cssUrls) {\n return { kind: \"invalid\", reason: \"cssUrls must be an array of strings\" };\n }\n if (!fields.widgets || fields.widgets.length === 0) {\n return { kind: \"invalid\", reason: fields.widgetsError };\n }\n\n return {\n kind: \"valid\",\n descriptor: {\n manifestVersion: fields.manifestVersion,\n packageId: fields.packageId,\n packageType: fields.packageType,\n version: fields.version,\n remoteEntryUrl: fields.remoteEntryUrl,\n cssUrls: fields.cssUrls,\n widgets: fields.widgets,\n },\n };\n}\n\nfunction validateDescriptorCandidate(\n descriptor: WidgetPackageDescriptor,\n): string | null {\n try {\n validateWidgetPackageDescriptor(\n descriptor as RuntimeWidgetPackageDescriptor,\n );\n return null;\n } catch (error) {\n return error instanceof Error ? error.message : String(error);\n }\n}\n\nfunction isRuntimeWidgetPackageDescriptorAttempt(\n entry: Readonly<Record<string, unknown>>,\n): boolean {\n return (\n entry.manifestVersion !== undefined &&\n entry.packageId !== undefined &&\n entry.packageType !== undefined &&\n entry.version !== undefined &&\n entry.remoteEntryUrl !== undefined &&\n entry.cssUrls !== undefined &&\n entry.widgets !== undefined\n );\n}\n\nfunction isCatalogWidgetPackageEntry(\n entry: Readonly<Record<string, unknown>>,\n manifest: Readonly<Record<string, unknown>> | undefined,\n): boolean {\n return (\n entry.source !== undefined ||\n entry.scriptUrl !== undefined ||\n entry.manifestUrl !== undefined ||\n manifest?.source !== undefined ||\n manifest?.scriptUrl !== undefined ||\n manifest?.manifestUrl !== undefined ||\n manifest?.widgets !== undefined\n );\n}\n\nfunction normalizeManifestVersion(\n value: unknown,\n options: { readonly defaultWhenMissing: boolean },\n): WidgetPackageDescriptor[\"manifestVersion\"] | null {\n if (value === WIDGET_PACKAGE_DESCRIPTOR_MANIFEST_VERSION) {\n return WIDGET_PACKAGE_DESCRIPTOR_MANIFEST_VERSION;\n }\n\n if (value === undefined && options.defaultWhenMissing) {\n return WIDGET_PACKAGE_DESCRIPTOR_MANIFEST_VERSION;\n }\n\n return null;\n}\n\nfunction normalizeCssUrls(value: unknown): string[] | null {\n return normalizeCssUrlList(value, { undefinedValue: [] });\n}\n\nfunction normalizeRuntimeCssUrls(value: unknown): string[] | null {\n return normalizeCssUrlList(value, { undefinedValue: null });\n}\n\nfunction normalizeCssUrlList(\n value: unknown,\n options: { readonly undefinedValue: string[] | null },\n): string[] | null {\n if (value === undefined) return options.undefinedValue;\n if (!Array.isArray(value)) return null;\n\n const cssUrls: string[] = [];\n for (const cssUrl of value) {\n const normalizedCssUrl = readNonEmptyString(cssUrl);\n if (!normalizedCssUrl) return null;\n cssUrls.push(normalizedCssUrl);\n }\n return cssUrls;\n}\n\nfunction normalizeRuntimeWidgets(value: unknown): WidgetDescriptor[] | null {\n if (!Array.isArray(value)) return null;\n return value as WidgetDescriptor[];\n}\n\nfunction normalizeCatalogWidgets(\n value: readonly unknown[],\n packageId: string | undefined,\n): WidgetDescriptor[] {\n const widgets: WidgetDescriptor[] = [];\n value.forEach((widget, index) => {\n const normalizedWidget = normalizeWidgetDescriptor(widget, {\n fallbackType:\n packageId === undefined\n ? undefined\n : `${packageId}-widget-${index + 1}`,\n overwritePropertySchemaWidgetType: true,\n });\n if (normalizedWidget) widgets.push(normalizedWidget);\n });\n return widgets;\n}\n\nfunction normalizeWidgetDescriptor(\n widget: unknown,\n options: {\n readonly fallbackType?: string;\n readonly overwritePropertySchemaWidgetType?: boolean;\n } = {},\n): WidgetDescriptor | null {\n if (!isRecord(widget)) return null;\n\n const type =\n readNonEmptyString(widget.type) ??\n readNonEmptyString(widget.name) ??\n options.fallbackType;\n if (!type) return null;\n\n const name = readNonEmptyString(widget.name) ?? type;\n const displayName = readNonEmptyString(widget.displayName) ?? name;\n\n return {\n type,\n name,\n displayName,\n description:\n readNonEmptyString(widget.description) ?? `Custom widget ${displayName}`,\n icon: readNonEmptyString(widget.icon) ?? DEFAULT_WIDGET_ICON,\n category: readNonEmptyString(widget.category) ?? DEFAULT_WIDGET_CATEGORY,\n propertySchema: normalizePropertySchema(widget.propertySchema, type, {\n overwriteWidgetType: options.overwritePropertySchemaWidgetType === true,\n }),\n defaultProps: isRecord(widget.defaultProps) ? widget.defaultProps : {},\n container: normalizeContainer(widget.container),\n minSdkVersion:\n readNonEmptyString(widget.minSdkVersion) ?? DEFAULT_MIN_SDK_VERSION,\n resizable: normalizeResizable(widget.resizable),\n };\n}\n\nfunction normalizePropertySchema(\n value: unknown,\n widgetType: string,\n options: { readonly overwriteWidgetType: boolean } = {\n overwriteWidgetType: false,\n },\n): WidgetDescriptor[\"propertySchema\"] {\n if (!isRecord(value)) return { widgetType };\n if (options.overwriteWidgetType || value.widgetType === undefined) {\n return { ...value, widgetType };\n }\n return value;\n}\n\nfunction normalizeContainer(value: unknown): WidgetRuntimeContainer {\n return normalizeWidgetRuntimeContainer(value);\n}\n\nfunction normalizeResizable(value: unknown): WidgetRuntimeResizable {\n return normalizeWidgetRuntimeResizable(value);\n}\n\ninterface RuntimeAssetUrlNormalizationFailure {\n readonly inputUrl: string;\n readonly resolvedUrl?: string;\n readonly reason: \"invalid-url\" | \"unsupported-protocol\" | \"untrusted-origin\";\n}\n\ntype RuntimeAssetUrlNormalizationResult =\n | { readonly ok: true; readonly url: string }\n | {\n readonly ok: false;\n readonly failure: RuntimeAssetUrlNormalizationFailure;\n };\n\ninterface RuntimeAssetOriginPolicy {\n readonly allowSharedWidgetArtifactBucket: boolean;\n readonly allowedOrigins: ReadonlySet<string>;\n}\n\nfunction normalizeRuntimeAssetUrl(\n url: string,\n baseUrl: URL,\n assetOriginPolicy: RuntimeAssetOriginPolicy,\n): RuntimeAssetUrlNormalizationResult {\n if (hasUnsafeRawRuntimeAssetUrlSyntax(url)) {\n return {\n ok: false,\n failure: {\n inputUrl: url,\n reason: \"invalid-url\",\n },\n };\n }\n\n try {\n const parsedUrl = new URL(url, baseUrl);\n if (parsedUrl.protocol !== \"http:\" && parsedUrl.protocol !== \"https:\") {\n return {\n ok: false,\n failure: {\n inputUrl: url,\n resolvedUrl: parsedUrl.href,\n reason: \"unsupported-protocol\",\n },\n };\n }\n if (assetOriginPolicy.allowedOrigins.has(parsedUrl.origin)) {\n return { ok: true, url: parsedUrl.href };\n }\n\n if (\n assetOriginPolicy.allowSharedWidgetArtifactBucket &&\n parsedUrl.href.startsWith(SHARED_WIDGET_ARTIFACT_URL_PREFIX)\n ) {\n return { ok: true, url: parsedUrl.href };\n }\n\n return {\n ok: false,\n failure: {\n inputUrl: url,\n resolvedUrl: parsedUrl.href,\n reason: \"untrusted-origin\",\n },\n };\n } catch {\n return {\n ok: false,\n failure: {\n inputUrl: url,\n reason: \"invalid-url\",\n },\n };\n }\n}\n\nfunction warnRejectedWidgetPackageDescriptor(\n descriptor: WidgetPackageDescriptor,\n assetField: \"remoteEntryUrl\" | \"cssUrls\",\n failure: RuntimeAssetUrlNormalizationFailure,\n descriptorEndpointUrl: URL,\n): void {\n console.warn(\n `[portal] Dropping widget package descriptor ${descriptor.packageId}@${descriptor.version} from ${descriptorEndpointUrl.href}: ${assetField} \"${failure.inputUrl}\" ${formatRuntimeAssetUrlFailure(failure)}.`,\n );\n}\n\nfunction warnRejectedMalformedWidgetPackageEntry(\n entry: unknown,\n reason: string,\n descriptorEndpointUrl: URL,\n): void {\n console.warn(\n `[portal] Dropping widget package catalog entry ${formatWidgetPackageEntryIdentity(entry)} from ${descriptorEndpointUrl.href}: ${reason}.`,\n );\n}\n\nfunction formatWidgetPackageEntryIdentity(entry: unknown): string {\n if (!isRecord(entry)) return \"<unknown>\";\n\n const packageId = readNonEmptyString(entry.packageId);\n const version = readNonEmptyString(entry.version);\n if (packageId && version) return `${packageId}@${version}`;\n if (packageId) return packageId;\n return \"<unknown>\";\n}\n\nfunction formatRuntimeAssetUrlFailure(\n failure: RuntimeAssetUrlNormalizationFailure,\n): string {\n const resolvedUrl = failure.resolvedUrl\n ? ` (resolved as ${failure.resolvedUrl})`\n : \"\";\n\n switch (failure.reason) {\n case \"invalid-url\":\n return \"is not a valid URL\";\n case \"unsupported-protocol\":\n return `uses an unsupported protocol${resolvedUrl}`;\n case \"untrusted-origin\":\n return `resolved to an untrusted origin${resolvedUrl}`;\n }\n}\n\nfunction createRuntimeAssetOriginPolicy(\n descriptorEndpointUrl: URL,\n allowedOrigins: readonly string[],\n trustCatalogAssetOrigins: boolean,\n): RuntimeAssetOriginPolicy {\n const origins = new Set<string>();\n if (!trustCatalogAssetOrigins) {\n origins.add(descriptorEndpointUrl.origin);\n }\n\n for (const origin of allowedOrigins) {\n const normalizedOrigin = normalizeAllowedOrigin(origin);\n if (normalizedOrigin) {\n origins.add(normalizedOrigin);\n } else {\n console.warn(\n `[portal] Ignoring invalid widget package allowed origin \"${origin}\". Expected an http(s) absolute URL origin.`,\n );\n }\n }\n\n return {\n allowSharedWidgetArtifactBucket: trustCatalogAssetOrigins,\n allowedOrigins: origins,\n };\n}\n\nfunction hasUnsafeRawRuntimeAssetUrlSyntax(url: string): boolean {\n return (\n url.trim() !== url ||\n hasControlCharacter(url) ||\n url.includes(\"\\\\\") ||\n url.startsWith(\"//\") ||\n hasAmbiguousHttpScheme(url)\n );\n}\n\nfunction hasAmbiguousHttpScheme(url: string): boolean {\n return /^https?:(?!\\/\\/)/i.test(url);\n}\n\nfunction normalizeAllowedOrigin(origin: string): string | null {\n try {\n const parsedOrigin = new URL(origin);\n if (\n parsedOrigin.protocol !== \"http:\" &&\n parsedOrigin.protocol !== \"https:\"\n ) {\n return null;\n }\n\n return parsedOrigin.origin;\n } catch {\n return null;\n }\n}\n\nfunction normalizeDescriptorEndpointUrl(url: string): URL | null {\n try {\n return new URL(url, getWindowLocationHref());\n } catch {\n return null;\n }\n}\n\nfunction getWindowLocationHref(): string | undefined {\n return typeof window === \"undefined\" ? undefined : window.location.href;\n}\n\nfunction looksLikeWidgetPackageEntry(\n value: Readonly<Record<string, unknown>>,\n): boolean {\n return (\n value.packageId !== undefined ||\n value.remoteEntryUrl !== undefined ||\n value.scriptUrl !== undefined ||\n value.manifest !== undefined ||\n value.widgets !== undefined\n );\n}\n\nfunction getArrayProperty(\n value: Readonly<Record<string, unknown>>,\n key: string,\n): readonly unknown[] | undefined {\n const property = value[key];\n return Array.isArray(property) ? property : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import type { WidgetPackageDescriptor } from \"../types/widget-package-descriptor\";\n\nexport function resolveHostedWidgetPackageManifestUrl(\n descriptorEndpointUrl: string,\n baseUrl: string,\n): string {\n try {\n return new URL(descriptorEndpointUrl, baseUrl).href;\n } catch {\n return descriptorEndpointUrl;\n }\n}\n\nexport function dedupeWidgetPackagesByPackageIdAndVersion(\n widgetPackages: readonly WidgetPackageDescriptor[],\n): WidgetPackageDescriptor[] {\n const widgetPackagesByIdAndVersion = new Map<\n string,\n WidgetPackageDescriptor\n >();\n for (const widgetPackage of widgetPackages) {\n const key = JSON.stringify([\n widgetPackage.packageId,\n widgetPackage.version,\n ]);\n if (!widgetPackagesByIdAndVersion.has(key)) {\n widgetPackagesByIdAndVersion.set(key, widgetPackage);\n }\n }\n return Array.from(widgetPackagesByIdAndVersion.values());\n}\n"],"mappings":"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"}
@@ -1,10 +1,9 @@
1
1
  import { F as useAppDefinitionApi, a as toScreenDefinition, d as buildThemeDefinition, f as getActiveThemeId, i as normalizeComponentTree, n as transformManifestToRepAppData, o as createCompanyQueryKey, p as transformThemes, r as toNavigationItem, s as useCompanyScopedQueryKey, t as toRawManifest } from "../transforms-D8tNbtFA.mjs";
2
2
  import { t as useAccount } from "../use-account-vIWrrngf.mjs";
3
3
  import { t as useStore } from "../use-store-BH0GPS-f.mjs";
4
- import "../FluidThemeProvider-mIKhJTlY.mjs";
5
4
  import { n as usePortalTenantClient } from "../PortalTenantClientProvider-Bms_pfpm.mjs";
6
5
  import { n as useAppNavigation, t as AppNavigationProvider } from "../AppNavigationContext-sWuBzay1.mjs";
7
- import { a as isErrorResult, c as selectProperty, d as useAppDefinition, l as useFluidTheme, n as getProperty, o as isIdle, r as hasData, s as isLoading, u as APP_DEFINITION_QUERY_KEY } from "../property-schema-types-DJgFKHAG.mjs";
6
+ import { a as isErrorResult, c as selectProperty, d as useAppDefinition, l as useFluidTheme, n as getProperty, o as isIdle, r as hasData, s as isLoading, u as APP_DEFINITION_QUERY_KEY } from "../hook-types-D5HQXU4S.mjs";
8
7
  import { a as groupPropertyFields, i as extractPropertyValues, n as PROPERTY_FIELD_TYPES, o as isPropertyFieldType, r as applyPropertyValues } from "../registries-B_LFR-8_.mjs";
9
8
  import { c as sourceWidgetPackagesToManifests, i as isSourceWidgetPackage, n as defineWidgetPackage, t as defineWidget } from "../authoring-CqJmyKw5.mjs";
10
9
  //#region src/widgets/authoring-condition.ts
@@ -1 +1 @@
1
- {"version":3,"file":"authoring-condition.mjs","names":[],"sources":["../../src/widgets/authoring-condition.ts"],"sourcesContent":["import type { useCompanySwitch as runtimeUseCompanySwitch } from \"../hooks/use-company-switch\";\nimport type { useFluidApp as runtimeUseFluidApp } from \"../hooks/use-fluid-app\";\nimport type { useFluidProfile as runtimeUseFluidProfile } from \"../hooks/use-fluid-profile\";\nimport type { useLogout as runtimeUseLogout } from \"../hooks/use-logout\";\n\nexport { useAppDefinitionApi } from \"@fluid-app/portal-core/app-definition-api-context\";\nexport type { AccountRep } from \"@fluid-app/portal-core/account-types\";\nexport type { AppDefinitionApi } from \"@fluid-app/portal-core/app-definition-api\";\nexport type {\n AppDefinitionResponse,\n AppFluidOsDefinition,\n AppManifest,\n AppManifestProfile,\n AppManifestResponse,\n} from \"@fluid-app/portal-core/app-definition-types\";\nexport type { WidgetManifest } from \"@fluid-app/portal-core/registries\";\nexport type { Store } from \"@fluid-app/portal-core/store-types\";\nexport type {\n RepAppData,\n RepAppManifest,\n RepAppProfile,\n} from \"@fluid-app/portal-core/types\";\nexport type { UserType } from \"@fluid-app/auth\";\nexport type { UseQueryResult } from \"@tanstack/react-query\";\nexport type { FetchClient as PortalTenantFetchClient } from \"@fluid-app/portal-tenant-api-client\";\n\nexport { usePortalTenantClient } from \"../providers/PortalTenantClientProvider\";\nexport {\n APP_DEFINITION_QUERY_KEY,\n useAppDefinition,\n} from \"../hooks/use-app-definition\";\nexport {\n useFluidTheme,\n type UseFluidThemeResult,\n} from \"../hooks/use-fluid-theme\";\nexport {\n createCompanyQueryKey,\n useCompanyScopedQueryKey,\n} from \"../hooks/query-keys\";\nexport { useAccount } from \"../hooks/use-account\";\nexport { useStore } from \"../hooks/use-store\";\nexport {\n getProperty,\n hasData,\n isErrorResult,\n isIdle,\n isLoading,\n selectProperty,\n type ListQueryResult,\n type QueryResult,\n type QueryResultNullable,\n type UseListResourceHook,\n type UseSingleResourceHook,\n type ValueListQueryResult,\n type WithData,\n} from \"../hooks/hook-types\";\nexport {\n AppNavigationProvider,\n useAppNavigation,\n type AppNavigationContextValue,\n type AppNavigationProviderProps,\n} from \"../shell/AppNavigationContext\";\nexport {\n applyPropertyValues,\n extractPropertyValues,\n groupPropertyFields,\n isPropertyFieldType,\n PROPERTY_FIELD_TYPES,\n type PropertyFieldSchema,\n type PropertyFieldType,\n type TabConfig,\n} from \"../registries/property-schema-types\";\nexport type { FluidSDKConfig } from \"../types/config\";\nexport type {\n AlignOptions,\n BackgroundType,\n BackgroundValue,\n BorderRadiusOptions,\n ButtonSizeOptions,\n ColorOptions,\n FontSizeOptions,\n GapOptions,\n PaddingOptions,\n SectionLayoutType,\n} from \"../types/field-types\";\nexport type {\n Navigation,\n NavigationItem,\n ScreenDefinition,\n} from \"../types/navigation\";\nexport type { Profile } from \"../types/profile\";\nexport type {\n ResolvedTheme,\n ThemeColorInput,\n ThemeDefinition,\n ThemePayload,\n} from \"../types/theme\";\nexport type {\n WidgetDefaultProps,\n WidgetDescriptor,\n WidgetPackageDescriptor,\n WidgetPackageType,\n WidgetRuntimeContainer,\n WidgetRuntimeResizable,\n} from \"../types/widget-package-descriptor\";\nexport type {\n TypedWidgetSchema,\n WidgetPath,\n WidgetRegistry,\n WidgetSchema,\n WidgetType,\n WidgetTypeName,\n} from \"../types/widget-schema\";\nexport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nexport {\n buildThemeDefinition,\n getActiveThemeId,\n normalizeComponentTree,\n toNavigationItem,\n toRawManifest,\n toScreenDefinition,\n transformManifestToRepAppData,\n transformThemes,\n type RawApiNavigationItem,\n type RawApiScreen,\n type RawApiTheme,\n type RawManifestResponse,\n} from \"../transforms\";\nexport {\n defineWidget,\n defineWidgetPackage,\n isSourceWidgetPackage,\n sourceWidgetPackagesToManifests,\n type DefineWidgetOptions,\n type DefineWidgetPackageOptions,\n type SourceWidget,\n type SourceWidgetPackage,\n} from \"./authoring\";\n\nexport const APP_DATA_QUERY_KEY = [\"fluid\", \"app\"] as const;\nexport const PROFILE_QUERY_KEY = [\"fluid\", \"profile\"] as const;\n\n// The widget-authoring condition is loaded by Node/Vite SSR while extracting\n// manifests. These exports exist so component modules that import the root SDK\n// can link during extraction without loading browser-only persister modules.\n// Runtime widget bundles do not execute these placeholders; they import the\n// host SDK singleton from window.FluidShared.PortalSdk.\nexport const useFluidApp: typeof runtimeUseFluidApp =\n createRuntimeOnlyExport(\"useFluidApp\");\nexport const useFluidProfile: typeof runtimeUseFluidProfile =\n createRuntimeOnlyExport(\"useFluidProfile\");\nexport const useLogout: typeof runtimeUseLogout =\n createRuntimeOnlyExport(\"useLogout\");\nexport const useCompanySwitch: typeof runtimeUseCompanySwitch =\n createRuntimeOnlyExport(\"useCompanySwitch\");\n\nfunction createRuntimeOnlyExport<Name extends string>(name: Name): never {\n return function runtimeOnlyPortalSdkExport() {\n throw new Error(\n `${name} cannot run while the Fluid widget authoring condition is active. Runtime widget bundles receive the host SDK from window.FluidShared.PortalSdk.`,\n );\n } as never;\n}\n"],"mappings":";;;;;;;;;;AA2IA,MAAa,qBAAqB,CAAC,SAAS,MAAM;AAClD,MAAa,oBAAoB,CAAC,SAAS,UAAU;AAOrD,MAAa,cACX,wBAAwB,cAAc;AACxC,MAAa,kBACX,wBAAwB,kBAAkB;AAC5C,MAAa,YACX,wBAAwB,YAAY;AACtC,MAAa,mBACX,wBAAwB,mBAAmB;AAE7C,SAAS,wBAA6C,MAAmB;AACvE,QAAO,SAAS,6BAA6B;AAC3C,QAAM,IAAI,MACR,GAAG,KAAK,kJACT"}
1
+ {"version":3,"file":"authoring-condition.mjs","names":[],"sources":["../../src/widgets/authoring-condition.ts"],"sourcesContent":["import type { useCompanySwitch as runtimeUseCompanySwitch } from \"../hooks/use-company-switch\";\nimport type { useFluidApp as runtimeUseFluidApp } from \"../hooks/use-fluid-app\";\nimport type { useFluidProfile as runtimeUseFluidProfile } from \"../hooks/use-fluid-profile\";\nimport type { useLogout as runtimeUseLogout } from \"../hooks/use-logout\";\n\nexport { useAppDefinitionApi } from \"@fluid-app/portal-core/app-definition-api-context\";\nexport type { AccountRep } from \"@fluid-app/portal-core/account-types\";\nexport type { AppDefinitionApi } from \"@fluid-app/portal-core/app-definition-api\";\nexport type {\n AppDefinitionResponse,\n AppFluidOsDefinition,\n AppManifest,\n AppManifestProfile,\n AppManifestResponse,\n} from \"@fluid-app/portal-core/app-definition-types\";\nexport type { WidgetManifest } from \"@fluid-app/portal-core/registries\";\nexport type { Store } from \"@fluid-app/portal-core/store-types\";\nexport type {\n RepAppData,\n RepAppManifest,\n RepAppProfile,\n} from \"@fluid-app/portal-core/types\";\nexport type { UserType } from \"@fluid-app/auth\";\nexport type { UseQueryResult } from \"@tanstack/react-query\";\nexport type { FetchClient as PortalTenantFetchClient } from \"@fluid-app/portal-tenant-api-client\";\n\nexport { usePortalTenantClient } from \"../providers/PortalTenantClientProvider\";\nexport {\n APP_DEFINITION_QUERY_KEY,\n useAppDefinition,\n} from \"../hooks/use-app-definition\";\nexport {\n useFluidTheme,\n type UseFluidThemeResult,\n} from \"../hooks/use-fluid-theme\";\nexport {\n createCompanyQueryKey,\n useCompanyScopedQueryKey,\n} from \"../hooks/query-keys\";\nexport { useAccount } from \"../hooks/use-account\";\nexport { useStore } from \"../hooks/use-store\";\nexport {\n getProperty,\n hasData,\n isErrorResult,\n isIdle,\n isLoading,\n selectProperty,\n type ListQueryResult,\n type QueryResult,\n type QueryResultNullable,\n type UseListResourceHook,\n type UseSingleResourceHook,\n type ValueListQueryResult,\n type WithData,\n} from \"../hooks/hook-types\";\nexport {\n AppNavigationProvider,\n useAppNavigation,\n type AppNavigationContextValue,\n type AppNavigationProviderProps,\n} from \"../shell/AppNavigationContext\";\nexport {\n applyPropertyValues,\n extractPropertyValues,\n groupPropertyFields,\n isPropertyFieldType,\n PROPERTY_FIELD_TYPES,\n type PropertyFieldSchema,\n type PropertyFieldType,\n type TabConfig,\n} from \"../registries/property-schema-types\";\nexport type { FluidSDKConfig } from \"../types/config\";\nexport type {\n AlignOptions,\n BackgroundType,\n BackgroundValue,\n BorderRadiusOptions,\n ButtonSizeOptions,\n ColorOptions,\n FontSizeOptions,\n GapOptions,\n PaddingOptions,\n SectionLayoutType,\n} from \"../types/field-types\";\nexport type {\n Navigation,\n NavigationItem,\n ScreenDefinition,\n} from \"../types/navigation\";\nexport type { Profile } from \"../types/profile\";\nexport type {\n ResolvedTheme,\n ThemeColorInput,\n ThemeDefinition,\n ThemePayload,\n} from \"../types/theme\";\nexport type {\n WidgetDefaultProps,\n WidgetDescriptor,\n WidgetPackageDescriptor,\n WidgetPackageType,\n WidgetRuntimeContainer,\n WidgetRuntimeResizable,\n} from \"../types/widget-package-descriptor\";\nexport type {\n TypedWidgetSchema,\n WidgetPath,\n WidgetRegistry,\n WidgetSchema,\n WidgetType,\n WidgetTypeName,\n} from \"../types/widget-schema\";\nexport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nexport {\n buildThemeDefinition,\n getActiveThemeId,\n normalizeComponentTree,\n toNavigationItem,\n toRawManifest,\n toScreenDefinition,\n transformManifestToRepAppData,\n transformThemes,\n type RawApiNavigationItem,\n type RawApiScreen,\n type RawApiTheme,\n type RawManifestResponse,\n} from \"../transforms\";\nexport {\n defineWidget,\n defineWidgetPackage,\n isSourceWidgetPackage,\n sourceWidgetPackagesToManifests,\n type DefineWidgetOptions,\n type DefineWidgetPackageOptions,\n type SourceWidget,\n type SourceWidgetPackage,\n} from \"./authoring\";\n\nexport const APP_DATA_QUERY_KEY = [\"fluid\", \"app\"] as const;\nexport const PROFILE_QUERY_KEY = [\"fluid\", \"profile\"] as const;\n\n// The widget-authoring condition is loaded by Node/Vite SSR while extracting\n// manifests. These exports exist so component modules that import the root SDK\n// can link during extraction without loading browser-only persister modules.\n// Runtime widget bundles do not execute these placeholders; they import the\n// host SDK singleton from window.FluidShared.PortalSdk.\nexport const useFluidApp: typeof runtimeUseFluidApp =\n createRuntimeOnlyExport(\"useFluidApp\");\nexport const useFluidProfile: typeof runtimeUseFluidProfile =\n createRuntimeOnlyExport(\"useFluidProfile\");\nexport const useLogout: typeof runtimeUseLogout =\n createRuntimeOnlyExport(\"useLogout\");\nexport const useCompanySwitch: typeof runtimeUseCompanySwitch =\n createRuntimeOnlyExport(\"useCompanySwitch\");\n\nfunction createRuntimeOnlyExport<Name extends string>(name: Name): never {\n return function runtimeOnlyPortalSdkExport() {\n throw new Error(\n `${name} cannot run while the Fluid widget authoring condition is active. Runtime widget bundles receive the host SDK from window.FluidShared.PortalSdk.`,\n );\n } as never;\n}\n"],"mappings":";;;;;;;;;AA2IA,MAAa,qBAAqB,CAAC,SAAS,MAAM;AAClD,MAAa,oBAAoB,CAAC,SAAS,UAAU;AAOrD,MAAa,cACX,wBAAwB,cAAc;AACxC,MAAa,kBACX,wBAAwB,kBAAkB;AAC5C,MAAa,YACX,wBAAwB,YAAY;AACtC,MAAa,mBACX,wBAAwB,mBAAmB;AAE7C,SAAS,wBAA6C,MAAmB;AACvE,QAAO,SAAS,6BAA6B;AAC3C,QAAM,IAAI,MACR,GAAG,KAAK,kJACT"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluid-app/portal-sdk",
3
- "version": "0.1.437",
3
+ "version": "0.1.438",
4
4
  "description": "SDK for building custom Fluid portals",
5
5
  "files": [
6
6
  "dist",
@@ -100,51 +100,51 @@
100
100
  "typescript": "^5",
101
101
  "vite": "^6.4.1",
102
102
  "zod": "4.3.5",
103
+ "@fluid-app/address-autocomplete": "0.1.0",
103
104
  "@fluid-app/address-fields": "0.1.0",
104
105
  "@fluid-app/api-client-core": "0.1.0",
105
- "@fluid-app/cart-ui": "0.1.25",
106
106
  "@fluid-app/auth": "0.1.0",
107
- "@fluid-app/address-autocomplete": "0.1.0",
108
- "@fluid-app/contacts-core": "0.1.0",
107
+ "@fluid-app/cart-ui": "0.1.25",
109
108
  "@fluid-app/company-switcher-ui": "0.1.0",
109
+ "@fluid-app/contacts-core": "0.1.0",
110
+ "@fluid-app/file-picker-api-client": "0.1.0",
110
111
  "@fluid-app/contacts-ui": "0.1.0",
111
112
  "@fluid-app/file-picker-core": "0.1.0",
112
113
  "@fluid-app/fluid-pay-core": "0.1.0",
113
- "@fluid-app/file-picker-api-client": "0.1.0",
114
- "@fluid-app/i18n": "0.1.0",
115
114
  "@fluid-app/fluid-pay-ui": "0.1.0",
116
115
  "@fluid-app/messaging-api-client": "0.1.0",
117
- "@fluid-app/messaging-ui": "0.1.0",
116
+ "@fluid-app/i18n": "0.1.0",
118
117
  "@fluid-app/messaging-core": "0.1.0",
119
118
  "@fluid-app/mysite-core": "0.1.0",
120
- "@fluid-app/orders-core": "0.1.0",
121
119
  "@fluid-app/mysite-ui": "0.1.0",
120
+ "@fluid-app/messaging-ui": "0.1.0",
121
+ "@fluid-app/orders-core": "0.1.0",
122
+ "@fluid-app/orders-ui": "0.1.0",
122
123
  "@fluid-app/payments-display": "0.1.0",
123
124
  "@fluid-app/portal-app-download-ui": "0.1.0",
124
- "@fluid-app/portal-core": "0.1.23",
125
125
  "@fluid-app/portal-preview": "0.1.0",
126
- "@fluid-app/orders-ui": "0.1.0",
126
+ "@fluid-app/portal-core": "0.1.23",
127
127
  "@fluid-app/portal-react": "0.1.0",
128
128
  "@fluid-app/portal-tenant-api-client": "0.1.0",
129
- "@fluid-app/portal-tenant-mysite-api-client": "0.1.0",
130
129
  "@fluid-app/portal-tenant-contacts-api-client": "0.1.0",
131
- "@fluid-app/portal-tenant-pay-api-client": "0.1.0",
132
130
  "@fluid-app/portal-tenant-content-api-client": "0.1.0",
131
+ "@fluid-app/portal-tenant-mysite-api-client": "0.1.0",
132
+ "@fluid-app/portal-tenant-pay-api-client": "0.1.0",
133
133
  "@fluid-app/portal-tenant-store-api-client": "0.1.0",
134
- "@fluid-app/products-core": "0.1.0",
135
134
  "@fluid-app/products-api-client": "0.1.0",
135
+ "@fluid-app/products-core": "0.1.0",
136
136
  "@fluid-app/profile-core": "0.1.0",
137
137
  "@fluid-app/profile-ui": "0.1.0",
138
138
  "@fluid-app/query-persister": "0.1.0",
139
- "@fluid-app/shareables-core": "0.1.0",
140
139
  "@fluid-app/shareables-ui": "0.1.0",
140
+ "@fluid-app/shareables-core": "0.1.0",
141
141
  "@fluid-app/shop-core": "0.1.0",
142
142
  "@fluid-app/shop-ui": "0.1.0",
143
- "@fluid-app/subscriptions-core": "0.1.0",
144
143
  "@fluid-app/store-core": "0.1.0",
144
+ "@fluid-app/subscriptions-core": "0.1.0",
145
+ "@fluid-app/subscriptions-ui": "0.1.0",
145
146
  "@fluid-app/ui-primitives": "0.1.13",
146
- "@fluid-app/typescript-config": "0.0.0",
147
- "@fluid-app/subscriptions-ui": "0.1.0"
147
+ "@fluid-app/typescript-config": "0.0.0"
148
148
  },
149
149
  "peerDependencies": {
150
150
  "@hookform/resolvers": "^5.2.2",
@@ -1 +0,0 @@
1
- {"version":3,"file":"property-schema-types-DJgFKHAG.mjs","names":[],"sources":["../src/hooks/use-app-definition.ts","../src/hooks/use-fluid-theme.ts","../src/hooks/hook-types.ts"],"sourcesContent":["import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useAppDefinitionApi } from \"@fluid-app/portal-core/app-definition-api-context\";\nimport type { AppDefinitionResponse } from \"@fluid-app/portal-core/app-definition-types\";\nimport { useCompanyScopedQueryKey } from \"./query-keys\";\n\n/**\n * Base query key for the app definition endpoint.\n * The runtime key includes a company prefix via {@link useCompanyScopedQueryKey}.\n */\nexport const APP_DEFINITION_QUERY_KEY = [\"fluid\", \"app-definition\"] as const;\n\n/**\n * Hook to fetch the active app manifest from the portal-tenant BFF.\n *\n * Returns the manifest metadata (id, name, version)\n * sourced from `GET /api/app/manifest` via the {@link AppDefinitionApi} port.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { data } = useAppDefinition();\n * const definitionId = data?.definition.id ?? 0;\n * }\n * ```\n */\nexport function useAppDefinition(options?: {\n enabled?: boolean;\n}): UseQueryResult<AppDefinitionResponse> {\n const api = useAppDefinitionApi();\n const { scopeKey } = useCompanyScopedQueryKey();\n\n return useQuery({\n queryKey: scopeKey(APP_DEFINITION_QUERY_KEY),\n queryFn: () => api.fetchDefinition(),\n ...(options?.enabled !== undefined && { enabled: options.enabled }),\n });\n}\n","import { useThemeContext } from \"../providers/FluidThemeProvider\";\nimport type { ThemeDefinition } from \"../types/theme\";\n\n/**\n * Result of useFluidTheme hook\n */\nexport interface UseFluidThemeResult {\n /** Currently active theme */\n currentTheme: ThemeDefinition | null;\n /** Switch to a different theme */\n setTheme: (theme: ThemeDefinition) => void;\n /** Switch between light and dark mode */\n setThemeMode: (mode: \"light\" | \"dark\") => void;\n /** Current theme mode (convenience accessor) */\n mode: \"light\" | \"dark\" | undefined;\n}\n\n/**\n * Hook to access and control theme settings\n *\n * @example\n * ```tsx\n * function ThemeSwitcher({ themes }: { themes: ThemeDefinition[] }) {\n * const { currentTheme, setTheme, setThemeMode, mode } = useFluidTheme();\n *\n * return (\n * <div>\n * <select\n * value={currentTheme?.name}\n * onChange={(e) => {\n * const theme = themes.find(t => t.name === e.target.value);\n * if (theme) setTheme(theme);\n * }}\n * >\n * {themes.map(theme => (\n * <option key={theme.name} value={theme.name}>\n * {theme.name}\n * </option>\n * ))}\n * </select>\n *\n * <button onClick={() => setThemeMode(mode === \"dark\" ? \"light\" : \"dark\")}>\n * Toggle {mode === \"dark\" ? \"Light\" : \"Dark\"} Mode\n * </button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useFluidTheme(): UseFluidThemeResult {\n const { currentTheme, setTheme, setThemeMode, mode } = useThemeContext();\n\n return {\n currentTheme,\n setTheme,\n setThemeMode,\n mode,\n };\n}\n","/**\n * Hook type utilities and type predicates.\n *\n * This module provides:\n * - Generic hook result types with default type parameters\n * - Type predicates for query state narrowing\n * - Reusable patterns for type-safe property access in hooks\n *\n * Following generics best practices:\n * - generics-default-type-parameters: Default E to Error for common case\n * - generics-type-predicates: Type predicates for result state narrowing\n * - generics-constrain-type-parameters: K extends keyof T for property access\n */\n\n// =============================================================================\n// Base Result Types with Default Type Parameters\n// =============================================================================\n\n/**\n * Base result type for query hooks with default error type.\n * Uses default type parameter for E (generics-default-type-parameters rule).\n *\n * @typeParam T - The data type\n * @typeParam E - The error type (defaults to Error)\n *\n * @example\n * // Error type defaults to Error\n * type UsersResult = QueryResult<User[]>;\n *\n * // Can override when needed\n * type CustomResult = QueryResult<User[], ApiError>;\n */\nexport interface QueryResult<T, E = Error> {\n readonly data: T;\n readonly isLoading: boolean;\n readonly isError: boolean;\n readonly error?: E | undefined;\n}\n\n/**\n * Result type for hooks that may not have data yet.\n * Extends QueryResult with nullable data.\n *\n * @typeParam T - The data type\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface QueryResultNullable<T, E = Error> {\n readonly data: T | null | undefined;\n readonly isLoading: boolean;\n readonly isError: boolean;\n readonly error?: E | undefined;\n}\n\n/**\n * Result type for list/collection hooks with aggregates.\n *\n * @typeParam T - The item type in the array\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface ListQueryResult<T, E = Error> extends QueryResult<T[], E> {\n readonly totalCount: number;\n}\n\n/**\n * Result type for list hooks with value aggregation (e.g., deals with total value).\n *\n * @typeParam T - The item type in the array\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface ValueListQueryResult<T, E = Error> extends ListQueryResult<\n T,\n E\n> {\n readonly totalValue: number;\n}\n\n// =============================================================================\n// Type Predicates for Query State Narrowing\n// =============================================================================\n\n/**\n * Type predicate to check if a query result has successfully loaded data.\n * Narrows the data type from T | null | undefined to T.\n *\n * @example\n * const result = useContact(id);\n * if (hasData(result)) {\n * // TypeScript knows result.data is Contact, not Contact | null\n * console.log(result.data.name);\n * }\n */\nexport function hasData<T, E = Error>(\n result: QueryResultNullable<T, E>,\n): result is QueryResultNullable<T, E> & { readonly data: T } {\n return result.data != null && !result.isLoading && !result.isError;\n}\n\n/**\n * Type predicate to check if a query result is in loading state.\n * Useful for conditional rendering.\n *\n * @example\n * if (isLoading(result)) {\n * return <Spinner />;\n * }\n */\nexport function isLoading<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): boolean {\n return result.isLoading;\n}\n\n/**\n * Type predicate to check if a query result has an error.\n * Narrows to include the error property.\n *\n * @example\n * if (isErrorResult(result)) {\n * console.error(result.error); // error is E, not undefined\n * }\n */\nexport function isErrorResult<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): result is (QueryResult<T, E> | QueryResultNullable<T, E>) & {\n readonly isError: true;\n readonly error: E;\n} {\n return result.isError && result.error !== undefined;\n}\n\n/**\n * Type predicate to check if a query result is in idle state (not loading, no error, has data).\n *\n * @example\n * if (isIdle(result)) {\n * // Safe to access and render data\n * }\n */\nexport function isIdle<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): boolean {\n return !result.isLoading && !result.isError;\n}\n\n// =============================================================================\n// Generic Property Selector Pattern\n// =============================================================================\n\n/**\n * Type-safe property selector for hook results.\n * Uses K extends keyof T constraint (generics-function-constraints rule).\n *\n * @typeParam T - The data object type\n * @typeParam K - Key of T (constrained to actual keys)\n *\n * @example\n * const users = [{ name: \"Alice\", age: 30 }];\n * const names = selectProperty(users, \"name\"); // string[]\n * const ages = selectProperty(users, \"age\"); // number[]\n * selectProperty(users, \"invalid\"); // Error: \"invalid\" not in \"name\" | \"age\"\n */\nexport function selectProperty<T, K extends keyof T>(\n items: readonly T[],\n key: K,\n): T[K][] {\n return items.map((item) => item[key]);\n}\n\n/**\n * Type-safe property getter for a single item.\n * Returns undefined if item is null/undefined.\n *\n * @typeParam T - The data object type\n * @typeParam K - Key of T (constrained to actual keys)\n */\nexport function getProperty<T, K extends keyof T>(\n item: T | null | undefined,\n key: K,\n): T[K] | undefined {\n return item?.[key];\n}\n\n// =============================================================================\n// Hook Factory Types\n// =============================================================================\n\n/**\n * Generic type for hooks that fetch a single resource by ID.\n * Useful for creating consistent API across different resource types.\n *\n * @typeParam T - The resource type\n * @typeParam E - The error type (defaults to Error)\n */\nexport type UseSingleResourceHook<T, E = Error> = (\n id: string,\n) => QueryResultNullable<T, E>;\n\n/**\n * Generic type for hooks that fetch a list of resources with optional params.\n * Uses generics-default-type-parameters for the params type.\n *\n * @typeParam T - The item type\n * @typeParam P - The params type (defaults to empty object)\n * @typeParam E - The error type (defaults to Error)\n */\nexport type UseListResourceHook<\n T,\n P extends Record<string, unknown> = Record<string, never>,\n E = Error,\n> = (params?: P) => ListQueryResult<T, E>;\n\n/**\n * Transforms a nullable result to a non-nullable one if data exists.\n * Useful when you've already checked hasData().\n */\nexport type WithData<R extends QueryResultNullable<unknown>> =\n R extends QueryResultNullable<infer T, infer E>\n ? QueryResultNullable<T, E> & { readonly data: T }\n : never;\n\n// =============================================================================\n// Domain Types\n// Shared between hook files and demo-data files to avoid circular imports.\n// =============================================================================\n\n/**\n * Activity slug constants as a const object.\n * Derive the ActivitySlug type from this single source of truth.\n */\nexport const ACTIVITY_SLUGS = {\n abandonedCart: \"abandoned_cart\",\n announcements: \"announcements\",\n cartItemsAdded: \"cart_items_added\",\n commentReply: \"comment_reply\",\n directMessage: \"direct_message\",\n fantasyPoint: \"fantasy_point\",\n newLead: \"new_lead\",\n orderPlaced: \"order_placed\",\n pageViews: \"page_views\",\n pageViewsContact: \"page_views_contact\",\n tasks: \"tasks\",\n upcomingEvent: \"upcoming_event\",\n video: \"video\",\n videoComplete: \"video_complete\",\n videoCompleteContact: \"video_complete_contact\",\n videoContact: \"video_contact\",\n messageReceived: \"message_received\",\n messageSent: \"message_sent\",\n newCartItemsAdded: \"new_cart_items_added\",\n smartLinkClicked: \"smart_link_clicked\",\n reviewLeft: \"review_left\",\n} as const;\n\n/** Activity slug union type derived from ACTIVITY_SLUGS constant. */\nexport type ActivitySlug = (typeof ACTIVITY_SLUGS)[keyof typeof ACTIVITY_SLUGS];\n\n/** Type predicate to check if a string is a valid ActivitySlug. */\nexport function isActivitySlug(value: string): value is ActivitySlug {\n return Object.values(ACTIVITY_SLUGS).includes(value as ActivitySlug);\n}\n\n/** Transformed activity for display. */\nexport interface Activity {\n readonly id: number;\n readonly userName: string;\n readonly avatarUrl: string | null;\n readonly activityType: string;\n readonly targetName: string;\n readonly timestamp: string;\n readonly slug: ActivitySlug;\n}\n\n/** Description/rich text metadata for a calendar event. */\nexport interface CalendarEventDescription {\n readonly id?: number | null;\n readonly name?: string | null;\n readonly body?: string | null;\n readonly record_type?: string | null;\n readonly record_id?: number | null;\n readonly created_at?: string | null;\n readonly updated_at?: string | null;\n readonly locale?: string | null;\n}\n\n/** Calendar event data from the API. */\nexport interface CalendarEvent {\n readonly id: number;\n readonly title: string;\n readonly description?: CalendarEventDescription | null;\n readonly color?: string | null;\n readonly url?: string | null;\n readonly start: string;\n readonly end: string;\n readonly active?: boolean | null;\n readonly time_zone?: string | null;\n readonly status?: string | null;\n readonly image_url?: string | null;\n readonly images?: readonly unknown[] | null;\n readonly venue?: string | null;\n readonly countries?: readonly string[] | null;\n readonly hasTomorrow?: boolean | null;\n readonly hasYesterday?: boolean | null;\n readonly isAllDay?: boolean;\n}\n\n/** Catch up suggestion data from the API. */\nexport interface CatchUp {\n readonly id: number;\n readonly suggestion_title: string;\n}\n\n/** MySite data returned by the hook. */\nexport interface MySiteData {\n readonly url: string | null;\n readonly views: number;\n readonly leads: number;\n readonly userName: string;\n}\n\n/** Transformed todo for display. */\nexport interface Todo {\n readonly id: number;\n readonly body: string;\n readonly dueAt: string | null;\n readonly completedAt: string | null;\n readonly createdAt: string;\n readonly contactName: string | null;\n}\n"],"mappings":";;;;;;;;AASA,MAAa,2BAA2B,CAAC,SAAS,iBAAiB;;;;;;;;;;;;;;;AAgBnE,SAAgB,iBAAiB,SAES;CACxC,MAAM,MAAM,qBAAqB;CACjC,MAAM,EAAE,aAAa,0BAA0B;AAE/C,QAAO,SAAS;EACd,UAAU,SAAS,yBAAyB;EAC5C,eAAe,IAAI,iBAAiB;EACpC,GAAI,SAAS,YAAY,KAAA,KAAa,EAAE,SAAS,QAAQ,SAAS;EACnE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACcJ,SAAgB,gBAAqC;CACnD,MAAM,EAAE,cAAc,UAAU,cAAc,SAAS,iBAAiB;AAExE,QAAO;EACL;EACA;EACA;EACA;EACD;;;;;;;;;;;;;;;ACkCH,SAAgB,QACd,QAC4D;AAC5D,QAAO,OAAO,QAAQ,QAAQ,CAAC,OAAO,aAAa,CAAC,OAAO;;;;;;;;;;;AAY7D,SAAgB,UACd,QACS;AACT,QAAO,OAAO;;;;;;;;;;;AAYhB,SAAgB,cACd,QAIA;AACA,QAAO,OAAO,WAAW,OAAO,UAAU,KAAA;;;;;;;;;;AAW5C,SAAgB,OACd,QACS;AACT,QAAO,CAAC,OAAO,aAAa,CAAC,OAAO;;;;;;;;;;;;;;;AAoBtC,SAAgB,eACd,OACA,KACQ;AACR,QAAO,MAAM,KAAK,SAAS,KAAK,KAAK;;;;;;;;;AAUvC,SAAgB,YACd,MACA,KACkB;AAClB,QAAO,OAAO;;;;;;AAkDhB,MAAa,iBAAiB;CAC5B,eAAe;CACf,eAAe;CACf,gBAAgB;CAChB,cAAc;CACd,eAAe;CACf,cAAc;CACd,SAAS;CACT,aAAa;CACb,WAAW;CACX,kBAAkB;CAClB,OAAO;CACP,eAAe;CACf,OAAO;CACP,eAAe;CACf,sBAAsB;CACtB,cAAc;CACd,iBAAiB;CACjB,aAAa;CACb,mBAAmB;CACnB,kBAAkB;CAClB,YAAY;CACb;;AAMD,SAAgB,eAAe,OAAsC;AACnE,QAAO,OAAO,OAAO,eAAe,CAAC,SAAS,MAAsB"}