@gears-frontx/react 0.2.0-alpha.4 → 0.2.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -585,32 +585,94 @@ declare function useMfeBridge(): ChildMfeBridge;
585
585
  /**
586
586
  * useSharedProperty Hook - Shared property subscription
587
587
  *
588
- * Subscribes to shared property updates from the host.
588
+ * Subscribes to shared property updates from the host, either through the
589
+ * ambient MfeProvider context or through an explicitly supplied bridge.
589
590
  *
590
591
  * React Layer: L3
591
592
  */
593
+
592
594
  /**
593
- * Hook for subscribing to a shared property.
594
- *
595
- * Subscribes to property updates from the host and returns the current value.
596
- * Must be used within a MfeProvider (i.e., inside an MFE component).
597
- *
598
- * NOTE: This hook provides the interface and uses useSyncExternalStore with a stub subscription.
599
- * Full bridge subscription should be implemented when bridge methods are available.
600
- *
601
- * @param _propertyTypeId - Type ID of the shared property to subscribe to (currently unused)
602
- * @returns Current property value
603
- *
604
- * @example
595
+ * Options for {@link useSharedProperty}.
596
+ */
597
+ interface UseSharedPropertyOptions<T> {
598
+ /**
599
+ * Bridge to read the property from, bypassing `MfeContext`.
600
+ *
601
+ * Pass this when the calling component never mounts `MfeProvider` (for
602
+ * example, a component that receives its bridge directly as a prop or from
603
+ * a non-React entry point). When omitted, the hook falls back to the
604
+ * bridge from `MfeContext` and throws if no `MfeProvider` is mounted,
605
+ * matching the zero-argument form.
606
+ *
607
+ * `bridge` and `propertyTypeId` identify the subscription: changing either
608
+ * one unsubscribes from the old source and subscribes to the new one, and
609
+ * the returned value reflects the new source on the same render.
610
+ */
611
+ bridge?: ChildMfeBridge;
612
+ /**
613
+ * Value returned while the host has not published the property yet
614
+ * (`bridge.getProperty()` returns `undefined`).
615
+ *
616
+ * Only the `undefined` sentinel is substituted: a published `null` is a
617
+ * real value and is returned as-is. The host validates every published
618
+ * value against the property's schema before it reaches any MFE
619
+ * (`TypeSystemPlugin.register()` throws on a schema mismatch and the
620
+ * update is never stored or delivered), so no consumer-side filtering is
621
+ * needed -- `T` is simply the caller's declaration of that schema.
622
+ *
623
+ * `fallback` may be passed inline on every render; it does not affect the
624
+ * host subscription.
625
+ */
626
+ fallback?: T;
627
+ }
628
+ /**
629
+ * Subscribe to a shared property from the host.
630
+ *
631
+ * Two forms:
632
+ *
633
+ * 1. **Context form** -- `useSharedProperty(propertyTypeId)`. Reads the
634
+ * bridge from `MfeContext` and throws if no `MfeProvider` is mounted.
635
+ * 2. **Options form** -- `useSharedProperty(propertyTypeId, options)`. Pass
636
+ * `options.bridge` for components that never mount `MfeProvider`, and/or
637
+ * `options.fallback` for a value to show until the host publishes one.
638
+ * `options.bridge` may be omitted, in which case the hook still falls
639
+ * back to `MfeContext`.
640
+ *
641
+ * Both forms read through `useSyncExternalStore`: the bridge is the store,
642
+ * `bridge.getProperty()` is the snapshot, so the first render already shows
643
+ * the host's current value, a publish between render and commit is picked
644
+ * up when React re-reads the snapshot after subscribing, and the value is
645
+ * tearing-protected under concurrent rendering.
646
+ *
647
+ * @param propertyTypeId - GTS type ID of the shared property to subscribe to
648
+ * @param options - Optional bridge override and fallback
649
+ * @returns The current property value; `T` when `options.fallback` is set,
650
+ * otherwise `T | undefined`
651
+ *
652
+ * @example Context form (inside MfeProvider)
605
653
  * ```tsx
606
- * function MyMfeComponent() {
607
- * const userData = useSharedProperty('gts.frontx.mfes.comm.shared_property.v1~myapp.user_data.v1');
654
+ * function ThemedComponent() {
655
+ * const theme = useSharedProperty<{ primaryColor: string }>(THEME_PROPERTY_ID);
656
+ * return <div style={{ color: theme?.primaryColor }} />;
657
+ * }
658
+ * ```
608
659
  *
609
- * return <div>User: {userData?.name}</div>;
660
+ * @example Explicit bridge with fallback (no MfeProvider)
661
+ * ```tsx
662
+ * function StandaloneComponent({ bridge }: { bridge: ChildMfeBridge }) {
663
+ * const label = useSharedProperty<string>(LABEL_PROPERTY_ID, {
664
+ * bridge,
665
+ * fallback: 'Untitled',
666
+ * });
667
+ * return <span>{label}</span>;
610
668
  * }
611
669
  * ```
612
670
  */
613
671
  declare function useSharedProperty<T = unknown>(propertyTypeId: string): T | undefined;
672
+ declare function useSharedProperty<T>(propertyTypeId: string, options: UseSharedPropertyOptions<T> & {
673
+ fallback: T;
674
+ }): T;
675
+ declare function useSharedProperty<T = unknown>(propertyTypeId: string, options?: UseSharedPropertyOptions<T>): T | undefined;
614
676
 
615
677
  /**
616
678
  * useHostAction Hook - Host action requests
@@ -777,8 +839,17 @@ declare function useRegisteredPackages(): string[];
777
839
  * Styling strategy:
778
840
  * 1. adoptHostStylesIntoShadowRoot() clones all host <style> and <link> into the
779
841
  * front of the shadow root, bringing the full compiled Tailwind CSS (including
780
- * MFE utilities, since the host's content paths cover src/mfe_packages/**).
781
- * Front, not back, so the MFE's own stylesheets outrank them - see the method.
842
+ * MFE utilities, since the host's content paths cover src-app/mfe_packages/**) and
843
+ * the ui-kit component CSS (the host entry side-effect-imports
844
+ * @gears-frontx/ui-kit so that CSS exists in the host document; the kit's
845
+ * CSS-module class names are stable per kit version, so they match the
846
+ * classes MFE bundles reference only while host and MFE install the same
847
+ * kit version - the template-pin-drift policy check is what guarantees
848
+ * that; a drifted MFE renders its kit components unstyled with no error).
849
+ * Front, not back, so the MFE's own stylesheets
850
+ * outrank them - see the method. Design tokens must therefore be full CSS
851
+ * colors - both Tailwind utilities (var(--x)) and ui-kit component CSS
852
+ * consume them directly.
782
853
  * 2. injectBaseResets() adds box-model resets and :host defaults that aren't part
783
854
  * of Tailwind's compiled output but are needed for consistent rendering, and
784
855
  * positions them at the end of the shell-owned head - after the adopted block
@@ -988,4 +1059,4 @@ interface EventPayloadMap extends EventPayloadMap$1 {
988
1059
  */
989
1060
  declare const eventBus: EventBus<EventPayloadMap>;
990
1061
 
991
- export { type ApiInfiniteQueryOptions, type ApiInfiniteQueryPageContext, ApiInfiniteQueryResult, ApiMutationResult, type ApiQueryOverrides, ApiQueryResult, type ApiStreamOptions, type ApiStreamResult, ApiSuspenseInfiniteQueryResult, ApiSuspenseQueryResult, CanAccess, CanAccessProps, type EventPayloadMap, ExtensionDomainSlot, type ExtensionDomainSlotProps, FrontXContext, FrontXProvider, FrontXProviderProps, MfeContextValue, MfeProvider, type MfeProviderProps, MutationCallbackContext, QueryCache, ThemeAwareReactLifecycle, type UseApiMutationOptions, UseCanAccessResult, UseFormattersReturn, UseScreenTranslationsReturn, UseThemeReturn, UseTranslationReturn, eventBus, useApiInfiniteQuery, useApiMutation, useApiQuery, useApiStream, useApiSuspenseInfiniteQuery, useApiSuspenseQuery, useAppDispatch, useAppSelector, useCanAccess, useDomainExtensions, useFormatters, useFrontX, useHostAction, useMfeBridge, useMountedExtensions, useQueryCache, useRegisteredPackages, useScreenTranslations, useSharedProperty, useTheme, useTranslation };
1062
+ export { type ApiInfiniteQueryOptions, type ApiInfiniteQueryPageContext, ApiInfiniteQueryResult, ApiMutationResult, type ApiQueryOverrides, ApiQueryResult, type ApiStreamOptions, type ApiStreamResult, ApiSuspenseInfiniteQueryResult, ApiSuspenseQueryResult, CanAccess, CanAccessProps, type EventPayloadMap, ExtensionDomainSlot, type ExtensionDomainSlotProps, FrontXContext, FrontXProvider, FrontXProviderProps, MfeContextValue, MfeProvider, type MfeProviderProps, MutationCallbackContext, QueryCache, ThemeAwareReactLifecycle, type UseApiMutationOptions, UseCanAccessResult, UseFormattersReturn, UseScreenTranslationsReturn, type UseSharedPropertyOptions, UseThemeReturn, UseTranslationReturn, eventBus, useApiInfiniteQuery, useApiMutation, useApiQuery, useApiStream, useApiSuspenseInfiniteQuery, useApiSuspenseQuery, useAppDispatch, useAppSelector, useCanAccess, useDomainExtensions, useFormatters, useFrontX, useHostAction, useMfeBridge, useMountedExtensions, useQueryCache, useRegisteredPackages, useScreenTranslations, useSharedProperty, useTheme, useTranslation };
package/dist/index.d.ts CHANGED
@@ -585,32 +585,94 @@ declare function useMfeBridge(): ChildMfeBridge;
585
585
  /**
586
586
  * useSharedProperty Hook - Shared property subscription
587
587
  *
588
- * Subscribes to shared property updates from the host.
588
+ * Subscribes to shared property updates from the host, either through the
589
+ * ambient MfeProvider context or through an explicitly supplied bridge.
589
590
  *
590
591
  * React Layer: L3
591
592
  */
593
+
592
594
  /**
593
- * Hook for subscribing to a shared property.
594
- *
595
- * Subscribes to property updates from the host and returns the current value.
596
- * Must be used within a MfeProvider (i.e., inside an MFE component).
597
- *
598
- * NOTE: This hook provides the interface and uses useSyncExternalStore with a stub subscription.
599
- * Full bridge subscription should be implemented when bridge methods are available.
600
- *
601
- * @param _propertyTypeId - Type ID of the shared property to subscribe to (currently unused)
602
- * @returns Current property value
603
- *
604
- * @example
595
+ * Options for {@link useSharedProperty}.
596
+ */
597
+ interface UseSharedPropertyOptions<T> {
598
+ /**
599
+ * Bridge to read the property from, bypassing `MfeContext`.
600
+ *
601
+ * Pass this when the calling component never mounts `MfeProvider` (for
602
+ * example, a component that receives its bridge directly as a prop or from
603
+ * a non-React entry point). When omitted, the hook falls back to the
604
+ * bridge from `MfeContext` and throws if no `MfeProvider` is mounted,
605
+ * matching the zero-argument form.
606
+ *
607
+ * `bridge` and `propertyTypeId` identify the subscription: changing either
608
+ * one unsubscribes from the old source and subscribes to the new one, and
609
+ * the returned value reflects the new source on the same render.
610
+ */
611
+ bridge?: ChildMfeBridge;
612
+ /**
613
+ * Value returned while the host has not published the property yet
614
+ * (`bridge.getProperty()` returns `undefined`).
615
+ *
616
+ * Only the `undefined` sentinel is substituted: a published `null` is a
617
+ * real value and is returned as-is. The host validates every published
618
+ * value against the property's schema before it reaches any MFE
619
+ * (`TypeSystemPlugin.register()` throws on a schema mismatch and the
620
+ * update is never stored or delivered), so no consumer-side filtering is
621
+ * needed -- `T` is simply the caller's declaration of that schema.
622
+ *
623
+ * `fallback` may be passed inline on every render; it does not affect the
624
+ * host subscription.
625
+ */
626
+ fallback?: T;
627
+ }
628
+ /**
629
+ * Subscribe to a shared property from the host.
630
+ *
631
+ * Two forms:
632
+ *
633
+ * 1. **Context form** -- `useSharedProperty(propertyTypeId)`. Reads the
634
+ * bridge from `MfeContext` and throws if no `MfeProvider` is mounted.
635
+ * 2. **Options form** -- `useSharedProperty(propertyTypeId, options)`. Pass
636
+ * `options.bridge` for components that never mount `MfeProvider`, and/or
637
+ * `options.fallback` for a value to show until the host publishes one.
638
+ * `options.bridge` may be omitted, in which case the hook still falls
639
+ * back to `MfeContext`.
640
+ *
641
+ * Both forms read through `useSyncExternalStore`: the bridge is the store,
642
+ * `bridge.getProperty()` is the snapshot, so the first render already shows
643
+ * the host's current value, a publish between render and commit is picked
644
+ * up when React re-reads the snapshot after subscribing, and the value is
645
+ * tearing-protected under concurrent rendering.
646
+ *
647
+ * @param propertyTypeId - GTS type ID of the shared property to subscribe to
648
+ * @param options - Optional bridge override and fallback
649
+ * @returns The current property value; `T` when `options.fallback` is set,
650
+ * otherwise `T | undefined`
651
+ *
652
+ * @example Context form (inside MfeProvider)
605
653
  * ```tsx
606
- * function MyMfeComponent() {
607
- * const userData = useSharedProperty('gts.frontx.mfes.comm.shared_property.v1~myapp.user_data.v1');
654
+ * function ThemedComponent() {
655
+ * const theme = useSharedProperty<{ primaryColor: string }>(THEME_PROPERTY_ID);
656
+ * return <div style={{ color: theme?.primaryColor }} />;
657
+ * }
658
+ * ```
608
659
  *
609
- * return <div>User: {userData?.name}</div>;
660
+ * @example Explicit bridge with fallback (no MfeProvider)
661
+ * ```tsx
662
+ * function StandaloneComponent({ bridge }: { bridge: ChildMfeBridge }) {
663
+ * const label = useSharedProperty<string>(LABEL_PROPERTY_ID, {
664
+ * bridge,
665
+ * fallback: 'Untitled',
666
+ * });
667
+ * return <span>{label}</span>;
610
668
  * }
611
669
  * ```
612
670
  */
613
671
  declare function useSharedProperty<T = unknown>(propertyTypeId: string): T | undefined;
672
+ declare function useSharedProperty<T>(propertyTypeId: string, options: UseSharedPropertyOptions<T> & {
673
+ fallback: T;
674
+ }): T;
675
+ declare function useSharedProperty<T = unknown>(propertyTypeId: string, options?: UseSharedPropertyOptions<T>): T | undefined;
614
676
 
615
677
  /**
616
678
  * useHostAction Hook - Host action requests
@@ -777,8 +839,17 @@ declare function useRegisteredPackages(): string[];
777
839
  * Styling strategy:
778
840
  * 1. adoptHostStylesIntoShadowRoot() clones all host <style> and <link> into the
779
841
  * front of the shadow root, bringing the full compiled Tailwind CSS (including
780
- * MFE utilities, since the host's content paths cover src/mfe_packages/**).
781
- * Front, not back, so the MFE's own stylesheets outrank them - see the method.
842
+ * MFE utilities, since the host's content paths cover src-app/mfe_packages/**) and
843
+ * the ui-kit component CSS (the host entry side-effect-imports
844
+ * @gears-frontx/ui-kit so that CSS exists in the host document; the kit's
845
+ * CSS-module class names are stable per kit version, so they match the
846
+ * classes MFE bundles reference only while host and MFE install the same
847
+ * kit version - the template-pin-drift policy check is what guarantees
848
+ * that; a drifted MFE renders its kit components unstyled with no error).
849
+ * Front, not back, so the MFE's own stylesheets
850
+ * outrank them - see the method. Design tokens must therefore be full CSS
851
+ * colors - both Tailwind utilities (var(--x)) and ui-kit component CSS
852
+ * consume them directly.
782
853
  * 2. injectBaseResets() adds box-model resets and :host defaults that aren't part
783
854
  * of Tailwind's compiled output but are needed for consistent rendering, and
784
855
  * positions them at the end of the shell-owned head - after the adopted block
@@ -988,4 +1059,4 @@ interface EventPayloadMap extends EventPayloadMap$1 {
988
1059
  */
989
1060
  declare const eventBus: EventBus<EventPayloadMap>;
990
1061
 
991
- export { type ApiInfiniteQueryOptions, type ApiInfiniteQueryPageContext, ApiInfiniteQueryResult, ApiMutationResult, type ApiQueryOverrides, ApiQueryResult, type ApiStreamOptions, type ApiStreamResult, ApiSuspenseInfiniteQueryResult, ApiSuspenseQueryResult, CanAccess, CanAccessProps, type EventPayloadMap, ExtensionDomainSlot, type ExtensionDomainSlotProps, FrontXContext, FrontXProvider, FrontXProviderProps, MfeContextValue, MfeProvider, type MfeProviderProps, MutationCallbackContext, QueryCache, ThemeAwareReactLifecycle, type UseApiMutationOptions, UseCanAccessResult, UseFormattersReturn, UseScreenTranslationsReturn, UseThemeReturn, UseTranslationReturn, eventBus, useApiInfiniteQuery, useApiMutation, useApiQuery, useApiStream, useApiSuspenseInfiniteQuery, useApiSuspenseQuery, useAppDispatch, useAppSelector, useCanAccess, useDomainExtensions, useFormatters, useFrontX, useHostAction, useMfeBridge, useMountedExtensions, useQueryCache, useRegisteredPackages, useScreenTranslations, useSharedProperty, useTheme, useTranslation };
1062
+ export { type ApiInfiniteQueryOptions, type ApiInfiniteQueryPageContext, ApiInfiniteQueryResult, ApiMutationResult, type ApiQueryOverrides, ApiQueryResult, type ApiStreamOptions, type ApiStreamResult, ApiSuspenseInfiniteQueryResult, ApiSuspenseQueryResult, CanAccess, CanAccessProps, type EventPayloadMap, ExtensionDomainSlot, type ExtensionDomainSlotProps, FrontXContext, FrontXProvider, FrontXProviderProps, MfeContextValue, MfeProvider, type MfeProviderProps, MutationCallbackContext, QueryCache, ThemeAwareReactLifecycle, type UseApiMutationOptions, UseCanAccessResult, UseFormattersReturn, UseScreenTranslationsReturn, type UseSharedPropertyOptions, UseThemeReturn, UseTranslationReturn, eventBus, useApiInfiniteQuery, useApiMutation, useApiQuery, useApiStream, useApiSuspenseInfiniteQuery, useApiSuspenseQuery, useAppDispatch, useAppSelector, useCanAccess, useDomainExtensions, useFormatters, useFrontX, useHostAction, useMfeBridge, useMountedExtensions, useQueryCache, useRegisteredPackages, useScreenTranslations, useSharedProperty, useTheme, useTranslation };
package/dist/index.js CHANGED
@@ -964,20 +964,27 @@ function useMfeBridge() {
964
964
  }
965
965
 
966
966
  // src/mfe/hooks/useSharedProperty.ts
967
- import { useSyncExternalStore as useSyncExternalStore4, useCallback as useCallback5 } from "react";
968
- function useSharedProperty(propertyTypeId) {
969
- const { bridge } = useMfeContext();
970
- const subscribe = useCallback5((callback) => {
971
- return bridge.subscribeToProperty(propertyTypeId, () => {
972
- callback();
973
- });
974
- }, [bridge, propertyTypeId]);
967
+ import { useCallback as useCallback5, useContext as useContext4, useSyncExternalStore as useSyncExternalStore4 } from "react";
968
+ function useSharedProperty(propertyTypeId, options) {
969
+ const contextValue = useContext4(MfeContext);
970
+ const bridge = options?.bridge ?? contextValue?.bridge;
971
+ const fallback = options?.fallback;
972
+ if (!bridge) {
973
+ throw new Error(
974
+ "useSharedProperty must be used within a MfeProvider, or given an explicit `bridge` option, for components that never mount MfeProvider."
975
+ );
976
+ }
977
+ const subscribe = useCallback5(
978
+ (onStoreChange) => bridge.subscribeToProperty(propertyTypeId, () => {
979
+ onStoreChange();
980
+ }),
981
+ [bridge, propertyTypeId]
982
+ );
975
983
  const getSnapshot = useCallback5(() => {
976
- const property = bridge.getProperty(propertyTypeId);
977
- return property?.value;
978
- }, [bridge, propertyTypeId]);
979
- const value = useSyncExternalStore4(subscribe, getSnapshot, getSnapshot);
980
- return value;
984
+ const raw = bridge.getProperty(propertyTypeId)?.value;
985
+ return raw === void 0 ? fallback : raw;
986
+ }, [bridge, propertyTypeId, fallback]);
987
+ return useSyncExternalStore4(subscribe, getSnapshot, getSnapshot);
981
988
  }
982
989
 
983
990
  // src/mfe/hooks/useHostAction.ts
@@ -1261,8 +1268,8 @@ var ThemeAwareReactLifecycle = class {
1261
1268
  line-height: 1.5;
1262
1269
  -webkit-font-smoothing: antialiased;
1263
1270
  -moz-osx-font-smoothing: grayscale;
1264
- color: hsl(var(--foreground));
1265
- background-color: hsl(var(--background));
1271
+ color: var(--foreground);
1272
+ background-color: var(--background);
1266
1273
  }
1267
1274
  `;
1268
1275
  const shellOwnedHead = container.querySelectorAll(