@squide/firefly 9.3.1 → 9.3.3
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/CHANGELOG.md +19 -0
- package/dist/AppRouter.d.ts +8 -12
- package/dist/AppRouter.js +68 -8
- package/dist/AppRouter.js.map +1 -0
- package/dist/AppRouterContext.d.ts +5 -9
- package/dist/AppRouterContext.js +30 -1
- package/dist/AppRouterContext.js.map +1 -0
- package/dist/AppRouterReducer.d.ts +23 -26
- package/dist/AppRouterReducer.js +339 -4
- package/dist/AppRouterReducer.js.map +1 -0
- package/dist/FireflyRuntime.d.ts +5 -8
- package/dist/FireflyRuntime.js +66 -5
- package/dist/FireflyRuntime.js.map +1 -0
- package/dist/GlobalDataQueriesError.d.ts +2 -4
- package/dist/GlobalDataQueriesError.js +19 -1
- package/dist/GlobalDataQueriesError.js.map +1 -0
- package/dist/RootRoute.d.ts +1 -5
- package/dist/RootRoute.js +48 -3
- package/dist/RootRoute.js.map +1 -0
- package/dist/boostrap.d.ts +8 -13
- package/dist/boostrap.js +54 -1
- package/dist/boostrap.js.map +1 -0
- package/dist/index.d.ts +23 -28
- package/dist/index.js +51 -25
- package/dist/index.js.map +1 -0
- package/dist/useCanFetchProtectedData.d.ts +1 -3
- package/dist/useCanFetchProtectedData.js +17 -2
- package/dist/useCanFetchProtectedData.js.map +1 -0
- package/dist/useCanFetchPublicData.d.ts +1 -3
- package/dist/useCanFetchPublicData.js +17 -2
- package/dist/useCanFetchPublicData.js.map +1 -0
- package/dist/useCanRegisterDeferredRegistrations.d.ts +1 -3
- package/dist/useCanRegisterDeferredRegistrations.js +14 -2
- package/dist/useCanRegisterDeferredRegistrations.js.map +1 -0
- package/dist/useCanUpdateDeferredRegistrations.d.ts +1 -3
- package/dist/useCanUpdateDeferredRegistrations.js +16 -2
- package/dist/useCanUpdateDeferredRegistrations.js.map +1 -0
- package/dist/useDeferredRegistrations.d.ts +5 -8
- package/dist/useDeferredRegistrations.js +74 -6
- package/dist/useDeferredRegistrations.js.map +1 -0
- package/dist/useExecuteOnce.d.ts +1 -3
- package/dist/useExecuteOnce.js +29 -1
- package/dist/useExecuteOnce.js.map +1 -0
- package/dist/useIsActiveRouteProtected.d.ts +1 -3
- package/dist/useIsActiveRouteProtected.js +23 -1
- package/dist/useIsActiveRouteProtected.js.map +1 -0
- package/dist/useIsBootstrapping.d.ts +3 -7
- package/dist/useIsBootstrapping.js +22 -2
- package/dist/useIsBootstrapping.js.map +1 -0
- package/dist/useNavigationItems.d.ts +3 -7
- package/dist/useNavigationItems.js +26 -2
- package/dist/useNavigationItems.js.map +1 -0
- package/dist/useProtectedDataQueries.d.ts +6 -8
- package/dist/useProtectedDataQueries.js +126 -5
- package/dist/useProtectedDataQueries.js.map +1 -0
- package/dist/usePublicDataQueries.d.ts +5 -7
- package/dist/usePublicDataQueries.js +111 -5
- package/dist/usePublicDataQueries.js.map +1 -0
- package/dist/useRegisterDeferredRegistrations.d.ts +4 -9
- package/dist/useRegisterDeferredRegistrations.js +19 -1
- package/dist/useRegisterDeferredRegistrations.js.map +1 -0
- package/dist/useStrictRegistrationMode.d.ts +1 -3
- package/dist/useStrictRegistrationMode.js +40 -1
- package/dist/useStrictRegistrationMode.js.map +1 -0
- package/dist/useUpdateDeferredRegistrations.d.ts +4 -9
- package/dist/useUpdateDeferredRegistrations.js +30 -2
- package/dist/useUpdateDeferredRegistrations.js.map +1 -0
- package/package.json +29 -24
- package/src/AppRouter.tsx +63 -0
- package/src/AppRouterContext.ts +27 -0
- package/src/AppRouterReducer.ts +363 -0
- package/src/FireflyRuntime.tsx +71 -0
- package/src/GlobalDataQueriesError.ts +17 -0
- package/src/RootRoute.tsx +25 -0
- package/src/boostrap.ts +62 -0
- package/src/index.ts +28 -0
- package/src/useCanFetchProtectedData.ts +26 -0
- package/src/useCanFetchPublicData.ts +23 -0
- package/src/useCanRegisterDeferredRegistrations.ts +24 -0
- package/src/useCanUpdateDeferredRegistrations.ts +23 -0
- package/src/useDeferredRegistrations.ts +59 -0
- package/src/useExecuteOnce.ts +23 -0
- package/src/useIsActiveRouteProtected.ts +12 -0
- package/src/useIsBootstrapping.ts +45 -0
- package/src/useNavigationItems.ts +18 -0
- package/src/useProtectedDataQueries.ts +101 -0
- package/src/usePublicDataQueries.ts +88 -0
- package/src/useRegisterDeferredRegistrations.ts +9 -0
- package/src/useStrictRegistrationMode.ts +28 -0
- package/src/useUpdateDeferredRegistrations.ts +16 -0
- package/dist/chunk-4RUCDAUT.js +0 -18
- package/dist/chunk-BFHHJOJT.js +0 -41
- package/dist/chunk-CTLPFYLM.js +0 -65
- package/dist/chunk-G32YX2KR.js +0 -22
- package/dist/chunk-GXSW4CZS.js +0 -9
- package/dist/chunk-H2ILEMPE.js +0 -28
- package/dist/chunk-HE5HKFL3.js +0 -16
- package/dist/chunk-IOMSOUAL.js +0 -20
- package/dist/chunk-JFMSLZ74.js +0 -253
- package/dist/chunk-L44KFU57.js +0 -34
- package/dist/chunk-LC233OPR.js +0 -54
- package/dist/chunk-MKTGJHQR.js +0 -71
- package/dist/chunk-N2GOIQ5E.js +0 -14
- package/dist/chunk-PMVN3U47.js +0 -21
- package/dist/chunk-PP3MRMJJ.js +0 -11
- package/dist/chunk-ROE2YHN5.js +0 -44
- package/dist/chunk-TURKCH7J.js +0 -20
- package/dist/chunk-WOPD33CM.js +0 -25
- package/dist/chunk-WVRMJZV6.js +0 -18
- package/dist/chunk-XDWYPVAJ.js +0 -21
- package/dist/chunk-YRWFYWK4.js +0 -11
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { useRuntime, type ModuleRegistrationError } from "@squide/core";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
import { useCanRegisterDeferredRegistrations } from "./useCanRegisterDeferredRegistrations.ts";
|
|
4
|
+
import { useCanUpdateDeferredRegistrations } from "./useCanUpdateDeferredRegistrations.ts";
|
|
5
|
+
import { useRegisterDeferredRegistrations } from "./useRegisterDeferredRegistrations.ts";
|
|
6
|
+
import { useUpdateDeferredRegistrations } from "./useUpdateDeferredRegistrations.ts";
|
|
7
|
+
|
|
8
|
+
export interface DeferredRegistrationsErrorsObject {
|
|
9
|
+
localModuleErrors: ModuleRegistrationError[];
|
|
10
|
+
remoteModuleErrors: ModuleRegistrationError[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type DeferredRegistrationsErrorCallback = (errorsObject: DeferredRegistrationsErrorsObject) => void;
|
|
14
|
+
|
|
15
|
+
export interface UseDeferredRegistrationsOptions {
|
|
16
|
+
onError?: DeferredRegistrationsErrorCallback;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function hasError({ localModuleErrors, remoteModuleErrors }: DeferredRegistrationsErrorsObject) {
|
|
20
|
+
return localModuleErrors.length > 0 || remoteModuleErrors.length > 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function useDeferredRegistrations(data: unknown, { onError }: UseDeferredRegistrationsOptions = {}) {
|
|
24
|
+
const runtime = useRuntime();
|
|
25
|
+
|
|
26
|
+
const canRegisterDeferredRegistrations = useCanRegisterDeferredRegistrations();
|
|
27
|
+
const canUpdateDeferredRegistrations = useCanUpdateDeferredRegistrations();
|
|
28
|
+
|
|
29
|
+
const registerDeferredRegistrations = useRegisterDeferredRegistrations();
|
|
30
|
+
const updateDeferredRegistrations = useUpdateDeferredRegistrations();
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (canRegisterDeferredRegistrations) {
|
|
34
|
+
const register = async () => {
|
|
35
|
+
const errors = await registerDeferredRegistrations(data, runtime);
|
|
36
|
+
|
|
37
|
+
if (hasError(errors) && onError) {
|
|
38
|
+
onError(errors);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
register();
|
|
43
|
+
}
|
|
44
|
+
}, [canRegisterDeferredRegistrations, registerDeferredRegistrations, data, onError, runtime]);
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (canUpdateDeferredRegistrations) {
|
|
48
|
+
const update = async () => {
|
|
49
|
+
const errors = await updateDeferredRegistrations(data, runtime);
|
|
50
|
+
|
|
51
|
+
if (hasError(errors) && onError) {
|
|
52
|
+
onError(errors);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
update();
|
|
57
|
+
}
|
|
58
|
+
}, [canUpdateDeferredRegistrations, updateDeferredRegistrations, data, onError, runtime]);
|
|
59
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useCallback, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
// Not using useEffect or useLayoutEffect because this utility hook is often used to dispatch events
|
|
4
|
+
// and it messed with the events dispatch order.
|
|
5
|
+
export function useExecuteOnce(fct: () => boolean, inline: boolean = false) {
|
|
6
|
+
const triggered = useRef(false);
|
|
7
|
+
|
|
8
|
+
const onceFct = useCallback(() => {
|
|
9
|
+
if (!triggered.current) {
|
|
10
|
+
const result = fct();
|
|
11
|
+
|
|
12
|
+
if (result) {
|
|
13
|
+
triggered.current = true;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}, [fct]);
|
|
17
|
+
|
|
18
|
+
if (inline) {
|
|
19
|
+
onceFct();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return onceFct;
|
|
23
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useIsRouteProtected, useRouteMatch } from "@squide/react-router";
|
|
2
|
+
import { useLocation } from "react-router-dom";
|
|
3
|
+
|
|
4
|
+
export function useIsActiveRouteProtected(areModulesReady: boolean) {
|
|
5
|
+
// Using this hook instead of window.location to retrieve the current location because it triggers a re-render everytime the browser location change.
|
|
6
|
+
const location = useLocation();
|
|
7
|
+
|
|
8
|
+
// Only throw when there's no match if the modules are ready, otherwise it's expected that no route will be found since they are not all registered yet.
|
|
9
|
+
const activeRoute = useRouteMatch(location, { throwWhenThereIsNoMatch: areModulesReady });
|
|
10
|
+
|
|
11
|
+
return useIsRouteProtected(activeRoute);
|
|
12
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useAppRouterState } from "./AppRouterContext.ts";
|
|
2
|
+
import type { AppRouterState } from "./AppRouterReducer.ts";
|
|
3
|
+
|
|
4
|
+
export function useIsBootstrapping() {
|
|
5
|
+
const state = useAppRouterState();
|
|
6
|
+
|
|
7
|
+
return isApplicationBootstrapping(state);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function isApplicationBootstrapping(state: AppRouterState) {
|
|
11
|
+
const {
|
|
12
|
+
waitForMsw,
|
|
13
|
+
waitForPublicData,
|
|
14
|
+
waitForProtectedData,
|
|
15
|
+
areModulesReady,
|
|
16
|
+
isMswReady,
|
|
17
|
+
isPublicDataReady,
|
|
18
|
+
isProtectedDataReady,
|
|
19
|
+
activeRouteVisibility,
|
|
20
|
+
isUnauthorized
|
|
21
|
+
} = state;
|
|
22
|
+
|
|
23
|
+
const isAppReady = (
|
|
24
|
+
!isUnauthorized
|
|
25
|
+
// Wait until the modules has been registered and the deferred registrations has been registered if any.
|
|
26
|
+
&& areModulesReady
|
|
27
|
+
// Not required but can sometimes prevent a re-render when the state value is somehow updated after the initial data is ready.
|
|
28
|
+
&& (!waitForMsw || isMswReady)
|
|
29
|
+
// Wait for the initial data to be ready.
|
|
30
|
+
&& (!waitForPublicData || isPublicDataReady)
|
|
31
|
+
&& (!waitForProtectedData || activeRouteVisibility === "public" || isProtectedDataReady)
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// When an API request returns a 401, the bootstrapping should be bypassed to render the login page.
|
|
35
|
+
const flush = (
|
|
36
|
+
// Only applicable when there's a unauthorized request while fetching the initial data.
|
|
37
|
+
isUnauthorized
|
|
38
|
+
// Not required but can sometimes prevent a re-render when the state value is somehow updated after the public data is ready.
|
|
39
|
+
&& (!waitForMsw || isMswReady)
|
|
40
|
+
// If the application is loading public data, we want to wait for this data to be ready to prevent a re-render.
|
|
41
|
+
&& (!waitForPublicData || isPublicDataReady)
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return !isAppReady && !flush;
|
|
45
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useRuntimeNavigationItems, type UseRuntimeNavigationItemsOptions } from "@squide/react-router";
|
|
2
|
+
import { useAppRouterState } from "./AppRouterContext.ts";
|
|
3
|
+
|
|
4
|
+
export type UseNavigationItemsOptions = UseRuntimeNavigationItemsOptions;
|
|
5
|
+
|
|
6
|
+
export function useNavigationItems(options?: UseNavigationItemsOptions) {
|
|
7
|
+
// This is not the most sophisticated strategy but it seems to be good enough for now.
|
|
8
|
+
// The idea is that when deferred registrations are used by the consumer applications, the deferred registrations could
|
|
9
|
+
// be updated when the global data is updated. If the deferred registrations are updated, it means that the registered
|
|
10
|
+
// navigation items might have been updated and a re-render must happens to display the new navigation items.
|
|
11
|
+
// Since the "deferredRegistrationsUpdatedAt" state value of the AppRouterReducer is updated everytime the deferred registrations
|
|
12
|
+
// are updated, subscribing to the state with useAppRouterState ensure that the navigation items will be re-rendered.
|
|
13
|
+
// A more sophisticated strategy could be implemented later on if needed, something involving a subscription to the "runtime" changes, or
|
|
14
|
+
// even introducing new module states.
|
|
15
|
+
useAppRouterState();
|
|
16
|
+
|
|
17
|
+
return useRuntimeNavigationItems(options);
|
|
18
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { useEventBus } from "@squide/core";
|
|
2
|
+
import { useQueries, type QueriesOptions, type QueriesResults, type UseQueryResult } from "@tanstack/react-query";
|
|
3
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
4
|
+
import { useAppRouterDispatcher, useAppRouterState } from "./AppRouterContext.ts";
|
|
5
|
+
import { GlobalDataQueriesError } from "./GlobalDataQueriesError.ts";
|
|
6
|
+
import { useCanFetchProtectedData } from "./useCanFetchProtectedData.ts";
|
|
7
|
+
import { useExecuteOnce } from "./useExecuteOnce.ts";
|
|
8
|
+
|
|
9
|
+
export const ProtectedDataFetchStartedEvent = "squide-protected-data-fetch-started";
|
|
10
|
+
export const ProtectedDataFetchFailedEvent = "squide-protected-data-fetch-failed";
|
|
11
|
+
|
|
12
|
+
export type IsUnauthorizedErrorCallback = (error: unknown) => boolean;
|
|
13
|
+
|
|
14
|
+
// This converts an array of UseQueryResult to an array of the data type of each query result.
|
|
15
|
+
// For more information, view: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-1.html#mapped-types-on-tuples-and-arrays.
|
|
16
|
+
type MapUseQueryResultToData<T> = { [K in keyof T]: T[K] extends UseQueryResult<infer U> ? U : never };
|
|
17
|
+
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
export function useProtectedDataQueries<T extends Array<any>>(queries: QueriesOptions<T>, isUnauthorizedError: IsUnauthorizedErrorCallback): MapUseQueryResultToData<QueriesResults<T>> {
|
|
20
|
+
const canFetchProtectedData = useCanFetchProtectedData();
|
|
21
|
+
const eventBus = useEventBus();
|
|
22
|
+
|
|
23
|
+
const dispatch = useAppRouterDispatcher();
|
|
24
|
+
|
|
25
|
+
const combineResults = useCallback((results: UseQueryResult<unknown, Error>[]) => {
|
|
26
|
+
const errors = results.filter(x => x.error).map(x => x.error) as Error[];
|
|
27
|
+
const hasErrors = errors.length > 0;
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
data: results.map(x => x.data) as MapUseQueryResultToData<QueriesResults<T>>,
|
|
31
|
+
errors,
|
|
32
|
+
hasErrors,
|
|
33
|
+
isReady: !hasErrors && !results.some(x => x.isPending)
|
|
34
|
+
};
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
const { data, errors: queriesErrors, hasErrors, isReady } = useQueries({
|
|
38
|
+
queries: queries.map(x => ({
|
|
39
|
+
enabled: canFetchProtectedData,
|
|
40
|
+
...x
|
|
41
|
+
})),
|
|
42
|
+
combine: combineResults
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const { isProtectedDataReady, isUnauthorized } = useAppRouterState();
|
|
46
|
+
|
|
47
|
+
useExecuteOnce(useCallback(() => {
|
|
48
|
+
if (canFetchProtectedData) {
|
|
49
|
+
eventBus.dispatch(ProtectedDataFetchStartedEvent);
|
|
50
|
+
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return false;
|
|
55
|
+
}, [canFetchProtectedData, eventBus]), true);
|
|
56
|
+
|
|
57
|
+
// Using a ref seems to be the only way to prevent starting two deferred registrations scope.
|
|
58
|
+
const isReadyRef = useRef(false);
|
|
59
|
+
|
|
60
|
+
const dispatchReady = useExecuteOnce(useCallback(() => {
|
|
61
|
+
if (isReady) {
|
|
62
|
+
isReadyRef.current = true;
|
|
63
|
+
|
|
64
|
+
dispatch({ type: "protected-data-ready" });
|
|
65
|
+
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return false;
|
|
70
|
+
}, [isReady, dispatch]));
|
|
71
|
+
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
// State update must be executed in useEffect.
|
|
74
|
+
dispatchReady();
|
|
75
|
+
}, [dispatchReady]);
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (isReadyRef.current && data) {
|
|
79
|
+
dispatch({ type: "protected-data-updated" });
|
|
80
|
+
}
|
|
81
|
+
}, [data, dispatch]);
|
|
82
|
+
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (hasErrors) {
|
|
85
|
+
if (!isProtectedDataReady && !isUnauthorized && queriesErrors.some(x => isUnauthorizedError(x))) {
|
|
86
|
+
// Will transition the state to allow the routes to render even if the bootstrapping is not complete, because otherwise
|
|
87
|
+
// a login page for example could not be rendered.
|
|
88
|
+
dispatch({ type: "is-unauthorized" });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Otherwise, when a user is logged off, a refetch might throws a 401.
|
|
92
|
+
if (!queriesErrors.every(x => isUnauthorizedError(x))) {
|
|
93
|
+
eventBus.dispatch(ProtectedDataFetchFailedEvent, queriesErrors);
|
|
94
|
+
|
|
95
|
+
throw new GlobalDataQueriesError("[squide] Global protected data queries failed.", queriesErrors);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}, [hasErrors, queriesErrors, isProtectedDataReady, isUnauthorized, isUnauthorizedError, dispatch, eventBus]);
|
|
99
|
+
|
|
100
|
+
return data;
|
|
101
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { useEventBus } from "@squide/core";
|
|
2
|
+
import { useQueries, type QueriesOptions, type QueriesResults, type UseQueryResult } from "@tanstack/react-query";
|
|
3
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
4
|
+
import { useAppRouterDispatcher } from "./AppRouterContext.ts";
|
|
5
|
+
import { GlobalDataQueriesError } from "./GlobalDataQueriesError.ts";
|
|
6
|
+
import { useCanFetchPublicData } from "./useCanFetchPublicData.ts";
|
|
7
|
+
import { useExecuteOnce } from "./useExecuteOnce.ts";
|
|
8
|
+
|
|
9
|
+
export const PublicDataFetchStartedEvent = "squide-public-data-fetch-started";
|
|
10
|
+
export const PublicDataFetchFailedEvent = "squide-public-data-fetch-failed";
|
|
11
|
+
|
|
12
|
+
// This converts an array of UseQueryResult to an array of the data type of each query result.
|
|
13
|
+
// For more information, view: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-1.html#mapped-types-on-tuples-and-arrays.
|
|
14
|
+
type MapUseQueryResultToData<T> = { [K in keyof T]: T[K] extends UseQueryResult<infer U> ? U : never };
|
|
15
|
+
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
export function usePublicDataQueries<T extends Array<any>>(queries: QueriesOptions<T>): MapUseQueryResultToData<QueriesResults<T>> {
|
|
18
|
+
const canFetchPublicData = useCanFetchPublicData();
|
|
19
|
+
const eventBus = useEventBus();
|
|
20
|
+
|
|
21
|
+
const dispatch = useAppRouterDispatcher();
|
|
22
|
+
|
|
23
|
+
const combineResults = useCallback((results: UseQueryResult<unknown, Error>[]) => {
|
|
24
|
+
const errors = results.filter(x => x.error).map(x => x.error) as Error[];
|
|
25
|
+
const hasErrors = errors.length > 0;
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
data: results.map(x => x.data) as MapUseQueryResultToData<QueriesResults<T>>,
|
|
29
|
+
errors,
|
|
30
|
+
hasErrors,
|
|
31
|
+
isReady: !hasErrors && !results.some(x => x.isPending)
|
|
32
|
+
};
|
|
33
|
+
}, []);
|
|
34
|
+
|
|
35
|
+
const { data, errors: queriesErrors, hasErrors, isReady } = useQueries({
|
|
36
|
+
queries: queries.map(x => ({
|
|
37
|
+
enabled: canFetchPublicData,
|
|
38
|
+
...x
|
|
39
|
+
})),
|
|
40
|
+
combine: combineResults
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
useExecuteOnce(useCallback(() => {
|
|
44
|
+
if (canFetchPublicData) {
|
|
45
|
+
eventBus.dispatch(PublicDataFetchStartedEvent);
|
|
46
|
+
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return false;
|
|
51
|
+
}, [canFetchPublicData, eventBus]), true);
|
|
52
|
+
|
|
53
|
+
// Using a ref seems to be the only way to prevent starting two deferred registrations scope.
|
|
54
|
+
const isReadyRef = useRef(false);
|
|
55
|
+
|
|
56
|
+
const dispatchReady = useExecuteOnce(useCallback(() => {
|
|
57
|
+
if (isReady) {
|
|
58
|
+
isReadyRef.current = true;
|
|
59
|
+
|
|
60
|
+
dispatch({ type: "public-data-ready" });
|
|
61
|
+
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return false;
|
|
66
|
+
}, [isReady, dispatch]));
|
|
67
|
+
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
// State update must be executed in useEffect.
|
|
70
|
+
dispatchReady();
|
|
71
|
+
}, [dispatchReady]);
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (isReadyRef.current && data) {
|
|
75
|
+
dispatch({ type: "public-data-updated" });
|
|
76
|
+
}
|
|
77
|
+
}, [data, dispatch]);
|
|
78
|
+
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (hasErrors) {
|
|
81
|
+
eventBus.dispatch(PublicDataFetchFailedEvent, queriesErrors);
|
|
82
|
+
|
|
83
|
+
throw new GlobalDataQueriesError("[squide] Global public data queries failed.", queriesErrors);
|
|
84
|
+
}
|
|
85
|
+
}, [hasErrors, queriesErrors, eventBus]);
|
|
86
|
+
|
|
87
|
+
return data;
|
|
88
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Runtime } from "@squide/core";
|
|
2
|
+
import { registerDeferredRegistrations } from "@squide/module-federation";
|
|
3
|
+
import { useCallback } from "react";
|
|
4
|
+
|
|
5
|
+
export function useRegisterDeferredRegistrations() {
|
|
6
|
+
return useCallback(<TData = unknown, TRuntime extends Runtime = Runtime>(data: TData, runtime: TRuntime) => {
|
|
7
|
+
return registerDeferredRegistrations(data, runtime);
|
|
8
|
+
}, []);
|
|
9
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { addLocalModuleRegistrationStatusChangedListener, getLocalModuleRegistrationStatus, removeLocalModuleRegistrationStatusChangedListener, useRuntime } from "@squide/core";
|
|
2
|
+
import { addRemoteModuleRegistrationStatusChangedListener, areModulesReady, getRemoteModuleRegistrationStatus, removeRemoteModuleRegistrationStatusChangedListener } from "@squide/module-federation";
|
|
3
|
+
import { useEffect, useSyncExternalStore } from "react";
|
|
4
|
+
|
|
5
|
+
function subscribeToLocalModuleRegistrationStatusChanged(callback: () => void) {
|
|
6
|
+
addLocalModuleRegistrationStatusChangedListener(callback);
|
|
7
|
+
|
|
8
|
+
return () => removeLocalModuleRegistrationStatusChangedListener(callback);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function subscribeToRemoteModuleRegistrationStatusChanged(callback: () => void) {
|
|
12
|
+
addRemoteModuleRegistrationStatusChangedListener(callback);
|
|
13
|
+
|
|
14
|
+
return () => removeRemoteModuleRegistrationStatusChangedListener(callback);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function useStrictRegistrationMode() {
|
|
18
|
+
const runtime = useRuntime();
|
|
19
|
+
|
|
20
|
+
const localModuleStatus = useSyncExternalStore(subscribeToLocalModuleRegistrationStatusChanged, getLocalModuleRegistrationStatus);
|
|
21
|
+
const remoteModuleStatus = useSyncExternalStore(subscribeToRemoteModuleRegistrationStatusChanged, getRemoteModuleRegistrationStatus);
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (areModulesReady(localModuleStatus, remoteModuleStatus)) {
|
|
25
|
+
runtime._validateRegistrations();
|
|
26
|
+
}
|
|
27
|
+
}, [runtime, localModuleStatus, remoteModuleStatus]);
|
|
28
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Runtime } from "@squide/core";
|
|
2
|
+
import { updateDeferredRegistrations } from "@squide/module-federation";
|
|
3
|
+
import { useCallback } from "react";
|
|
4
|
+
import { useAppRouterDispatcher } from "./AppRouterContext.ts";
|
|
5
|
+
|
|
6
|
+
export function useUpdateDeferredRegistrations() {
|
|
7
|
+
const dispatch = useAppRouterDispatcher();
|
|
8
|
+
|
|
9
|
+
return useCallback(async <TData = unknown, TRuntime extends Runtime = Runtime>(data: TData, runtime: TRuntime) => {
|
|
10
|
+
const errors = await updateDeferredRegistrations(data, runtime);
|
|
11
|
+
|
|
12
|
+
dispatch({ type: "deferred-registrations-updated" });
|
|
13
|
+
|
|
14
|
+
return errors;
|
|
15
|
+
}, [dispatch]);
|
|
16
|
+
}
|
package/dist/chunk-4RUCDAUT.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { useAppRouterState } from './chunk-G32YX2KR.js';
|
|
2
|
-
|
|
3
|
-
// src/useCanRegisterDeferredRegistrations.ts
|
|
4
|
-
function useCanRegisterDeferredRegistrations() {
|
|
5
|
-
const {
|
|
6
|
-
waitForPublicData,
|
|
7
|
-
waitForProtectedData,
|
|
8
|
-
areModulesReady,
|
|
9
|
-
areModulesRegistered,
|
|
10
|
-
isPublicDataReady,
|
|
11
|
-
isProtectedDataReady,
|
|
12
|
-
activeRouteVisibility,
|
|
13
|
-
isUnauthorized
|
|
14
|
-
} = useAppRouterState();
|
|
15
|
-
return !isUnauthorized && areModulesRegistered && !areModulesReady && (!waitForPublicData || isPublicDataReady) && (!waitForProtectedData || activeRouteVisibility === "public" || isProtectedDataReady);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export { useCanRegisterDeferredRegistrations };
|
package/dist/chunk-BFHHJOJT.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { useRegisterDeferredRegistrations } from './chunk-YRWFYWK4.js';
|
|
2
|
-
import { useUpdateDeferredRegistrations } from './chunk-N2GOIQ5E.js';
|
|
3
|
-
import { useCanRegisterDeferredRegistrations } from './chunk-4RUCDAUT.js';
|
|
4
|
-
import { useCanUpdateDeferredRegistrations } from './chunk-WVRMJZV6.js';
|
|
5
|
-
import { useRuntime } from '@squide/core';
|
|
6
|
-
import { useEffect } from 'react';
|
|
7
|
-
|
|
8
|
-
function hasError({ localModuleErrors, remoteModuleErrors }) {
|
|
9
|
-
return localModuleErrors.length > 0 || remoteModuleErrors.length > 0;
|
|
10
|
-
}
|
|
11
|
-
function useDeferredRegistrations(data, { onError } = {}) {
|
|
12
|
-
const runtime = useRuntime();
|
|
13
|
-
const canRegisterDeferredRegistrations = useCanRegisterDeferredRegistrations();
|
|
14
|
-
const canUpdateDeferredRegistrations = useCanUpdateDeferredRegistrations();
|
|
15
|
-
const registerDeferredRegistrations = useRegisterDeferredRegistrations();
|
|
16
|
-
const updateDeferredRegistrations = useUpdateDeferredRegistrations();
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
if (canRegisterDeferredRegistrations) {
|
|
19
|
-
const register = async () => {
|
|
20
|
-
const errors = await registerDeferredRegistrations(data, runtime);
|
|
21
|
-
if (hasError(errors) && onError) {
|
|
22
|
-
onError(errors);
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
register();
|
|
26
|
-
}
|
|
27
|
-
}, [canRegisterDeferredRegistrations, registerDeferredRegistrations, data, onError, runtime]);
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
if (canUpdateDeferredRegistrations) {
|
|
30
|
-
const update = async () => {
|
|
31
|
-
const errors = await updateDeferredRegistrations(data, runtime);
|
|
32
|
-
if (hasError(errors) && onError) {
|
|
33
|
-
onError(errors);
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
update();
|
|
37
|
-
}
|
|
38
|
-
}, [canUpdateDeferredRegistrations, updateDeferredRegistrations, data, onError, runtime]);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export { useDeferredRegistrations };
|
package/dist/chunk-CTLPFYLM.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { useCanFetchPublicData } from './chunk-IOMSOUAL.js';
|
|
2
|
-
import { useExecuteOnce } from './chunk-TURKCH7J.js';
|
|
3
|
-
import { GlobalDataQueriesError } from './chunk-HE5HKFL3.js';
|
|
4
|
-
import { useAppRouterDispatcher } from './chunk-G32YX2KR.js';
|
|
5
|
-
import { useEventBus } from '@squide/core';
|
|
6
|
-
import { useQueries } from '@tanstack/react-query';
|
|
7
|
-
import { useCallback, useRef, useEffect } from 'react';
|
|
8
|
-
|
|
9
|
-
var PublicDataFetchStartedEvent = "squide-public-data-fetch-started";
|
|
10
|
-
var PublicDataFetchFailedEvent = "squide-public-data-fetch-failed";
|
|
11
|
-
function usePublicDataQueries(queries) {
|
|
12
|
-
const canFetchPublicData = useCanFetchPublicData();
|
|
13
|
-
const eventBus = useEventBus();
|
|
14
|
-
const dispatch = useAppRouterDispatcher();
|
|
15
|
-
const combineResults = useCallback((results) => {
|
|
16
|
-
const errors = results.filter((x) => x.error).map((x) => x.error);
|
|
17
|
-
const hasErrors2 = errors.length > 0;
|
|
18
|
-
return {
|
|
19
|
-
data: results.map((x) => x.data),
|
|
20
|
-
errors,
|
|
21
|
-
hasErrors: hasErrors2,
|
|
22
|
-
isReady: !hasErrors2 && !results.some((x) => x.isPending)
|
|
23
|
-
};
|
|
24
|
-
}, []);
|
|
25
|
-
const { data, errors: queriesErrors, hasErrors, isReady } = useQueries({
|
|
26
|
-
queries: queries.map((x) => ({
|
|
27
|
-
enabled: canFetchPublicData,
|
|
28
|
-
...x
|
|
29
|
-
})),
|
|
30
|
-
combine: combineResults
|
|
31
|
-
});
|
|
32
|
-
useExecuteOnce(useCallback(() => {
|
|
33
|
-
if (canFetchPublicData) {
|
|
34
|
-
eventBus.dispatch(PublicDataFetchStartedEvent);
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
return false;
|
|
38
|
-
}, [canFetchPublicData, eventBus]), true);
|
|
39
|
-
const isReadyRef = useRef(false);
|
|
40
|
-
const dispatchReady = useExecuteOnce(useCallback(() => {
|
|
41
|
-
if (isReady) {
|
|
42
|
-
isReadyRef.current = true;
|
|
43
|
-
dispatch({ type: "public-data-ready" });
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
return false;
|
|
47
|
-
}, [isReady, dispatch]));
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
dispatchReady();
|
|
50
|
-
}, [dispatchReady]);
|
|
51
|
-
useEffect(() => {
|
|
52
|
-
if (isReadyRef.current && data) {
|
|
53
|
-
dispatch({ type: "public-data-updated" });
|
|
54
|
-
}
|
|
55
|
-
}, [data, dispatch]);
|
|
56
|
-
useEffect(() => {
|
|
57
|
-
if (hasErrors) {
|
|
58
|
-
eventBus.dispatch(PublicDataFetchFailedEvent, queriesErrors);
|
|
59
|
-
throw new GlobalDataQueriesError("[squide] Global public data queries failed.", queriesErrors);
|
|
60
|
-
}
|
|
61
|
-
}, [hasErrors, queriesErrors, eventBus]);
|
|
62
|
-
return data;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export { PublicDataFetchFailedEvent, PublicDataFetchStartedEvent, usePublicDataQueries };
|
package/dist/chunk-G32YX2KR.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { isNil } from '@squide/core';
|
|
2
|
-
import { createContext, useContext } from 'react';
|
|
3
|
-
|
|
4
|
-
// src/AppRouterContext.ts
|
|
5
|
-
var AppRouterStateContext = createContext(void 0);
|
|
6
|
-
function useAppRouterState() {
|
|
7
|
-
const state = useContext(AppRouterStateContext);
|
|
8
|
-
if (isNil(state)) {
|
|
9
|
-
throw new Error("[squide] The useAppRouterState hook must be called by a children of the AppRouter component.");
|
|
10
|
-
}
|
|
11
|
-
return state;
|
|
12
|
-
}
|
|
13
|
-
var AppRouterDispatcherContext = createContext(void 0);
|
|
14
|
-
function useAppRouterDispatcher() {
|
|
15
|
-
const dispatch = useContext(AppRouterDispatcherContext);
|
|
16
|
-
if (isNil(dispatch)) {
|
|
17
|
-
throw new Error("[squide] The useAppRouterDispatcher hook must be called by a children of the AppRouter component.");
|
|
18
|
-
}
|
|
19
|
-
return dispatch;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export { AppRouterDispatcherContext, AppRouterStateContext, useAppRouterDispatcher, useAppRouterState };
|
package/dist/chunk-GXSW4CZS.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { useAppRouterState } from './chunk-G32YX2KR.js';
|
|
2
|
-
import { useRuntimeNavigationItems } from '@squide/react-router';
|
|
3
|
-
|
|
4
|
-
function useNavigationItems(options) {
|
|
5
|
-
useAppRouterState();
|
|
6
|
-
return useRuntimeNavigationItems(options);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export { useNavigationItems };
|
package/dist/chunk-H2ILEMPE.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { useAppRouterState } from './chunk-G32YX2KR.js';
|
|
2
|
-
|
|
3
|
-
// src/useIsBootstrapping.ts
|
|
4
|
-
function useIsBootstrapping() {
|
|
5
|
-
const state = useAppRouterState();
|
|
6
|
-
return isApplicationBootstrapping(state);
|
|
7
|
-
}
|
|
8
|
-
function isApplicationBootstrapping(state) {
|
|
9
|
-
const {
|
|
10
|
-
waitForMsw,
|
|
11
|
-
waitForPublicData,
|
|
12
|
-
waitForProtectedData,
|
|
13
|
-
areModulesReady,
|
|
14
|
-
isMswReady,
|
|
15
|
-
isPublicDataReady,
|
|
16
|
-
isProtectedDataReady,
|
|
17
|
-
activeRouteVisibility,
|
|
18
|
-
isUnauthorized
|
|
19
|
-
} = state;
|
|
20
|
-
const isAppReady = !isUnauthorized && areModulesReady && (!waitForMsw || isMswReady) && (!waitForPublicData || isPublicDataReady) && (!waitForProtectedData || activeRouteVisibility === "public" || isProtectedDataReady);
|
|
21
|
-
const flush = (
|
|
22
|
-
// Only applicable when there's a unauthorized request while fetching the initial data.
|
|
23
|
-
isUnauthorized && (!waitForMsw || isMswReady) && (!waitForPublicData || isPublicDataReady)
|
|
24
|
-
);
|
|
25
|
-
return !isAppReady && !flush;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export { isApplicationBootstrapping, useIsBootstrapping };
|
package/dist/chunk-HE5HKFL3.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
// src/GlobalDataQueriesError.ts
|
|
2
|
-
var GlobalDataQueriesError = class extends Error {
|
|
3
|
-
#errors;
|
|
4
|
-
constructor(message, errors) {
|
|
5
|
-
super(message);
|
|
6
|
-
this.#errors = errors;
|
|
7
|
-
}
|
|
8
|
-
get errors() {
|
|
9
|
-
return this.#errors;
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
function isGlobalDataQueriesError(error) {
|
|
13
|
-
return error !== void 0 && error !== null && error instanceof GlobalDataQueriesError;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export { GlobalDataQueriesError, isGlobalDataQueriesError };
|
package/dist/chunk-IOMSOUAL.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { useAppRouterState } from './chunk-G32YX2KR.js';
|
|
2
|
-
|
|
3
|
-
// src/useCanFetchPublicData.ts
|
|
4
|
-
function useCanFetchPublicData() {
|
|
5
|
-
const {
|
|
6
|
-
waitForMsw,
|
|
7
|
-
areModulesRegistered,
|
|
8
|
-
areModulesReady,
|
|
9
|
-
isMswReady,
|
|
10
|
-
isPublicDataReady
|
|
11
|
-
} = useAppRouterState();
|
|
12
|
-
return (
|
|
13
|
-
// Always return true when the public data has already been fetched sucessfully so TanStack Query can update the data in the background.
|
|
14
|
-
isPublicDataReady || // Wait until the modules has been registered, but do not wait for the deferred registrations to be registered has they will probably
|
|
15
|
-
// depends on the protected data.
|
|
16
|
-
(areModulesRegistered || areModulesReady) && (!waitForMsw || isMswReady)
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export { useCanFetchPublicData };
|