@gears-frontx/react 0.2.0-alpha.5 → 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
@@ -997,4 +1059,4 @@ interface EventPayloadMap extends EventPayloadMap$1 {
997
1059
  */
998
1060
  declare const eventBus: EventBus<EventPayloadMap>;
999
1061
 
1000
- 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
@@ -997,4 +1059,4 @@ interface EventPayloadMap extends EventPayloadMap$1 {
997
1059
  */
998
1060
  declare const eventBus: EventBus<EventPayloadMap>;
999
1061
 
1000
- 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