@squide/firefly 16.1.12 → 16.2.1
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 +12 -0
- package/dist/AppRouter.d.ts +4 -0
- package/dist/AppRouter.js +33 -2
- package/dist/AppRouter.js.map +1 -1
- package/dist/useCanUpdateDeferredRegistrations.d.ts +1 -1
- package/dist/useCanUpdateDeferredRegistrations.js +15 -4
- package/dist/useCanUpdateDeferredRegistrations.js.map +1 -1
- package/dist/useDeferredRegistrations.js +12 -1
- package/dist/useDeferredRegistrations.js.map +1 -1
- package/package.json +4 -4
- package/src/AppRouter.tsx +41 -2
- package/src/useCanUpdateDeferredRegistrations.ts +20 -18
- package/src/useDeferredRegistrations.ts +14 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @squide/firefly
|
|
2
2
|
|
|
3
|
+
## 16.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#567](https://github.com/workleap/wl-squide/pull/567) [`5f16979`](https://github.com/workleap/wl-squide/commit/5f16979d9e922590fdca39be76e79dde54211efa) Thanks [@patricklafrance](https://github.com/patricklafrance)! - useDeferredRegistrations will now trigger a deferred registration update whenever the data object reference changes, even if no global data has been fetch or no feature flags changed.
|
|
8
|
+
|
|
9
|
+
## 16.2.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [#559](https://github.com/workleap/wl-squide/pull/559) [`33b1cd6`](https://github.com/workleap/wl-squide/commit/33b1cd639ef97e9d9fc892918ab6cbd08e6b91e9) Thanks [@patricklafrance](https://github.com/patricklafrance)! - React Router loaders execution is now delayed until MSW is ready.
|
|
14
|
+
|
|
3
15
|
## 16.1.12
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/dist/AppRouter.d.ts
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { type Route } from "@squide/react-router";
|
|
2
2
|
import { type ReactElement } from "react";
|
|
3
|
+
import { DataStrategyFunction, DataStrategyFunctionArgs, DOMRouterOpts } from "react-router";
|
|
3
4
|
import type { RouterProviderProps } from "react-router/dom";
|
|
4
5
|
import { type AppRouterState } from "./AppRouterReducer.ts";
|
|
6
|
+
import { FireflyRuntime } from "./FireflyRuntime.tsx";
|
|
5
7
|
export interface AppRouterRenderFunctionArgs {
|
|
6
8
|
routes: Route[];
|
|
7
9
|
}
|
|
8
10
|
export interface RenderRouterProviderFunctionArgs {
|
|
9
11
|
rootRoute: ReactElement;
|
|
10
12
|
registeredRoutes: Route[];
|
|
13
|
+
routerProps: DOMRouterOpts;
|
|
11
14
|
routerProviderProps: Omit<RouterProviderProps, "router">;
|
|
12
15
|
}
|
|
13
16
|
export type RenderRouterProviderFunction = (args: RenderRouterProviderFunctionArgs) => ReactElement;
|
|
14
17
|
export declare function useCanRenderRouter({ areModulesRegistered, areModulesReady: areModulesReadyValue }: AppRouterState): boolean;
|
|
18
|
+
export declare function createWaitForMswDataStrategy(runtime: FireflyRuntime): (args: DataStrategyFunctionArgs<unknown>) => ReturnType<DataStrategyFunction<unknown>>;
|
|
15
19
|
export interface AppRouterProps {
|
|
16
20
|
waitForPublicData?: boolean;
|
|
17
21
|
waitForProtectedData?: boolean;
|
package/dist/AppRouter.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useLogger } from "@squide/core";
|
|
2
|
+
import { useLogger, useRuntime } from "@squide/core";
|
|
3
3
|
import { useRoutes } from "@squide/react-router";
|
|
4
4
|
import { useEffect, useMemo } from "react";
|
|
5
5
|
import { AppRouterDispatcherContext, AppRouterStateContext } from "./AppRouterContext.js";
|
|
@@ -37,7 +37,34 @@ function useCanRenderRouter({ areModulesRegistered, areModulesReady: areModulesR
|
|
|
37
37
|
// depends on the protected data.
|
|
38
38
|
areModulesRegistered || areModulesReadyValue);
|
|
39
39
|
}
|
|
40
|
+
// This is a custom React Router data strategy (https://reactrouter.com/api/data-routers/createMemoryRouter#optsdatastrategy)
|
|
41
|
+
// to delay the execution of React Router data browser loaders until MSW is ready.
|
|
42
|
+
// The data strategy implemention is copied from React Router default data strategy: https://github.com/remix-run/react-router/blob/main/packages/react-router/lib/router/router.ts#L5710.
|
|
43
|
+
function createWaitForMswDataStrategy(runtime) {
|
|
44
|
+
const strategy = async (args)=>{
|
|
45
|
+
await new Promise((resolve)=>{
|
|
46
|
+
if (runtime.mswState.isReady) {
|
|
47
|
+
resolve(null);
|
|
48
|
+
} else {
|
|
49
|
+
const handler = ()=>{
|
|
50
|
+
runtime.mswState.removeMswReadyListener(handler);
|
|
51
|
+
resolve(null);
|
|
52
|
+
};
|
|
53
|
+
runtime.mswState.addMswReadyListener(handler);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
const matchesToLoad = args.matches.filter((m)=>m.shouldCallHandler);
|
|
57
|
+
const keyedResults = {};
|
|
58
|
+
const results = await Promise.all(matchesToLoad.map((m)=>m.resolve()));
|
|
59
|
+
results.forEach((result, i)=>{
|
|
60
|
+
keyedResults[matchesToLoad[i].route.id] = result;
|
|
61
|
+
});
|
|
62
|
+
return keyedResults;
|
|
63
|
+
};
|
|
64
|
+
return strategy;
|
|
65
|
+
}
|
|
40
66
|
function useRenderRouterProvider(state, renderRouterProvider, strictMode) {
|
|
67
|
+
const runtime = useRuntime();
|
|
41
68
|
const routes = useRoutes();
|
|
42
69
|
// The value is computed outside of the router provider memo to prevent
|
|
43
70
|
// rendering a new router provider everytime the app router state change.
|
|
@@ -49,11 +76,15 @@ function useRenderRouterProvider(state, renderRouterProvider, strictMode) {
|
|
|
49
76
|
strictMode: strictMode
|
|
50
77
|
}),
|
|
51
78
|
registeredRoutes: routes,
|
|
79
|
+
routerProps: {
|
|
80
|
+
dataStrategy: runtime.isMswEnabled ? createWaitForMswDataStrategy(runtime) : undefined
|
|
81
|
+
},
|
|
52
82
|
routerProviderProps: {}
|
|
53
83
|
});
|
|
54
84
|
}
|
|
55
85
|
return null;
|
|
56
86
|
}, [
|
|
87
|
+
runtime,
|
|
57
88
|
canRenderRouter,
|
|
58
89
|
routes,
|
|
59
90
|
renderRouterProvider,
|
|
@@ -83,6 +114,6 @@ function AppRouter(props) {
|
|
|
83
114
|
});
|
|
84
115
|
}
|
|
85
116
|
|
|
86
|
-
export { AppRouter, useCanRenderRouter };
|
|
117
|
+
export { AppRouter, createWaitForMswDataStrategy, useCanRenderRouter };
|
|
87
118
|
|
|
88
119
|
//# sourceMappingURL=AppRouter.js.map
|
package/dist/AppRouter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppRouter.js","sources":["../src/AppRouter.tsx"],"sourcesContent":["import { useLogger } from \"@squide/core\";\nimport { useRoutes, type Route } from \"@squide/react-router\";\nimport { useEffect, useMemo, type ReactElement } from \"react\";\nimport type { RouterProviderProps } from \"react-router/dom\";\nimport { AppRouterDispatcherContext, AppRouterStateContext } from \"./AppRouterContext.ts\";\nimport { useAppRouterReducer, type AppRouterState } from \"./AppRouterReducer.ts\";\nimport { RootRoute } from \"./RootRoute.tsx\";\nimport { useStrictRegistrationMode } from \"./useStrictRegistrationMode.ts\";\n\nexport interface AppRouterRenderFunctionArgs {\n routes: Route[];\n}\n\nexport interface RenderRouterProviderFunctionArgs {\n rootRoute: ReactElement;\n registeredRoutes: Route[];\n routerProviderProps: Omit<RouterProviderProps, \"router\">;\n}\n\nexport type RenderRouterProviderFunction = (args: RenderRouterProviderFunctionArgs) => ReactElement;\n\nexport function useCanRenderRouter({ areModulesRegistered, areModulesReady: areModulesReadyValue }: AppRouterState) {\n return (\n // Wait until the modules has been registered, but do not wait for the deferred registrations to be registered has they will probably\n // depends on the protected data.\n (areModulesRegistered || areModulesReadyValue)\n );\n}\n\nfunction useRenderRouterProvider(state: AppRouterState, renderRouterProvider: RenderRouterProviderFunction, strictMode: boolean) {\n const routes = useRoutes();\n\n // The value is computed outside of the router provider memo to prevent\n // rendering a new router provider everytime the app router state change.\n const canRenderRouter = useCanRenderRouter(state);\n\n return useMemo(() => {\n if (canRenderRouter) {\n return renderRouterProvider({\n rootRoute: <RootRoute strictMode={strictMode} />,\n registeredRoutes: routes,\n routerProviderProps: {}\n });\n }\n\n return null;\n }, [canRenderRouter, routes, renderRouterProvider, strictMode]);\n}\n\nexport interface AppRouterProps {\n waitForPublicData?: boolean;\n waitForProtectedData?: boolean;\n strictMode?: boolean;\n children: RenderRouterProviderFunction;\n}\n\nexport function AppRouter(props: AppRouterProps) {\n const {\n waitForPublicData = false,\n waitForProtectedData = false,\n strictMode = true,\n children: renderRouterProvider\n } = props;\n const [state, dispatch] = useAppRouterReducer(waitForPublicData, waitForProtectedData);\n\n const logger = useLogger();\n\n useStrictRegistrationMode({\n isEnabled: strictMode\n });\n\n useEffect(() => {\n logger\n .withText(\"[squide] AppRouter state has been updated to:\")\n .withObject(state)\n .debug();\n }, [state, logger]);\n\n const routerProvider = useRenderRouterProvider(state, renderRouterProvider, strictMode);\n\n return (\n <AppRouterDispatcherContext.Provider value={dispatch}>\n <AppRouterStateContext.Provider value={state}>\n {routerProvider}\n </AppRouterStateContext.Provider>\n </AppRouterDispatcherContext.Provider>\n );\n}\n"],"names":["useLogger","useRoutes","useEffect","useMemo","AppRouterDispatcherContext","AppRouterStateContext","useAppRouterReducer","RootRoute","useStrictRegistrationMode","useCanRenderRouter","areModulesRegistered","areModulesReadyValue","useRenderRouterProvider","state","renderRouterProvider","strictMode","routes","canRenderRouter","AppRouter","props","waitForPublicData","waitForProtectedData","dispatch","logger","routerProvider"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"AppRouter.js","sources":["../src/AppRouter.tsx"],"sourcesContent":["import { useLogger, useRuntime } from \"@squide/core\";\nimport { useRoutes, type Route } from \"@squide/react-router\";\nimport { useEffect, useMemo, type ReactElement } from \"react\";\nimport { DataStrategyFunction, DataStrategyFunctionArgs, DataStrategyResult, DOMRouterOpts } from \"react-router\";\nimport type { RouterProviderProps } from \"react-router/dom\";\nimport { AppRouterDispatcherContext, AppRouterStateContext } from \"./AppRouterContext.ts\";\nimport { useAppRouterReducer, type AppRouterState } from \"./AppRouterReducer.ts\";\nimport { FireflyRuntime } from \"./FireflyRuntime.tsx\";\nimport { RootRoute } from \"./RootRoute.tsx\";\nimport { useStrictRegistrationMode } from \"./useStrictRegistrationMode.ts\";\n\nexport interface AppRouterRenderFunctionArgs {\n routes: Route[];\n}\n\nexport interface RenderRouterProviderFunctionArgs {\n rootRoute: ReactElement;\n registeredRoutes: Route[];\n routerProps: DOMRouterOpts;\n routerProviderProps: Omit<RouterProviderProps, \"router\">;\n}\n\nexport type RenderRouterProviderFunction = (args: RenderRouterProviderFunctionArgs) => ReactElement;\n\nexport function useCanRenderRouter({ areModulesRegistered, areModulesReady: areModulesReadyValue }: AppRouterState) {\n return (\n // Wait until the modules has been registered, but do not wait for the deferred registrations to be registered has they will probably\n // depends on the protected data.\n (areModulesRegistered || areModulesReadyValue)\n );\n}\n\n// This is a custom React Router data strategy (https://reactrouter.com/api/data-routers/createMemoryRouter#optsdatastrategy)\n// to delay the execution of React Router data browser loaders until MSW is ready.\n// The data strategy implemention is copied from React Router default data strategy: https://github.com/remix-run/react-router/blob/main/packages/react-router/lib/router/router.ts#L5710.\nexport function createWaitForMswDataStrategy(runtime: FireflyRuntime) {\n const strategy: (args: DataStrategyFunctionArgs<unknown>) => ReturnType<DataStrategyFunction<unknown>> = async args => {\n await new Promise(resolve => {\n if (runtime.mswState.isReady) {\n resolve(null);\n } else {\n const handler = () => {\n runtime.mswState.removeMswReadyListener(handler);\n resolve(null);\n };\n\n runtime.mswState.addMswReadyListener(handler);\n }\n });\n\n const matchesToLoad = args.matches.filter(m => m.shouldCallHandler);\n const keyedResults: Record<string, DataStrategyResult> = {};\n const results = await Promise.all(matchesToLoad.map(m => m.resolve()));\n\n results.forEach((result, i) => {\n keyedResults[matchesToLoad[i].route.id] = result;\n });\n\n return keyedResults;\n };\n\n return strategy;\n}\n\nfunction useRenderRouterProvider(state: AppRouterState, renderRouterProvider: RenderRouterProviderFunction, strictMode: boolean) {\n const runtime = useRuntime() as FireflyRuntime;\n const routes = useRoutes();\n\n // The value is computed outside of the router provider memo to prevent\n // rendering a new router provider everytime the app router state change.\n const canRenderRouter = useCanRenderRouter(state);\n\n return useMemo(() => {\n if (canRenderRouter) {\n return renderRouterProvider({\n rootRoute: <RootRoute strictMode={strictMode} />,\n registeredRoutes: routes,\n routerProps: {\n dataStrategy: runtime.isMswEnabled ? createWaitForMswDataStrategy(runtime) : undefined\n },\n routerProviderProps: {}\n });\n }\n\n return null;\n }, [runtime, canRenderRouter, routes, renderRouterProvider, strictMode]);\n}\n\nexport interface AppRouterProps {\n waitForPublicData?: boolean;\n waitForProtectedData?: boolean;\n strictMode?: boolean;\n children: RenderRouterProviderFunction;\n}\n\nexport function AppRouter(props: AppRouterProps) {\n const {\n waitForPublicData = false,\n waitForProtectedData = false,\n strictMode = true,\n children: renderRouterProvider\n } = props;\n const [state, dispatch] = useAppRouterReducer(waitForPublicData, waitForProtectedData);\n\n const logger = useLogger();\n\n useStrictRegistrationMode({\n isEnabled: strictMode\n });\n\n useEffect(() => {\n logger\n .withText(\"[squide] AppRouter state has been updated to:\")\n .withObject(state)\n .debug();\n }, [state, logger]);\n\n const routerProvider = useRenderRouterProvider(state, renderRouterProvider, strictMode);\n\n return (\n <AppRouterDispatcherContext.Provider value={dispatch}>\n <AppRouterStateContext.Provider value={state}>\n {routerProvider}\n </AppRouterStateContext.Provider>\n </AppRouterDispatcherContext.Provider>\n );\n}\n"],"names":["useLogger","useRuntime","useRoutes","useEffect","useMemo","AppRouterDispatcherContext","AppRouterStateContext","useAppRouterReducer","RootRoute","useStrictRegistrationMode","useCanRenderRouter","areModulesRegistered","areModulesReadyValue","createWaitForMswDataStrategy","runtime","strategy","args","Promise","resolve","handler","matchesToLoad","m","keyedResults","results","result","i","useRenderRouterProvider","state","renderRouterProvider","strictMode","routes","canRenderRouter","undefined","AppRouter","props","waitForPublicData","waitForProtectedData","dispatch","logger","routerProvider"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAAqD;AACQ;AACC;AAG4B;AACT;AAErC;AAC+B;AAepE,SAASU,mBAAmB,EAAEC,oBAAoB,EAAE,iBAAiBC,oBAAoB,EAAkB;IAC9G,OACI,qIAAqI;IACrI,iCAAiC;IAChCD,wBAAwBC;AAEjC;AAEA,6HAA6H;AAC7H,kFAAkF;AAClF,0LAA0L;AACnL,SAASC,6BAA6BC,OAAuB;IAChE,MAAMC,WAAmG,OAAMC;QAC3G,MAAM,IAAIC,QAAQC,CAAAA;YACd,IAAIJ,QAAQ,QAAQ,CAAC,OAAO,EAAE;gBAC1BI,QAAQ;YACZ,OAAO;gBACH,MAAMC,UAAU;oBACZL,QAAQ,QAAQ,CAAC,sBAAsB,CAACK;oBACxCD,QAAQ;gBACZ;gBAEAJ,QAAQ,QAAQ,CAAC,mBAAmB,CAACK;YACzC;QACJ;QAEA,MAAMC,gBAAgBJ,KAAK,OAAO,CAAC,MAAM,CAACK,CAAAA,IAAKA,EAAE,iBAAiB;QAClE,MAAMC,eAAmD,CAAC;QAC1D,MAAMC,UAAU,MAAMN,QAAQ,GAAG,CAACG,cAAc,GAAG,CAACC,CAAAA,IAAKA,EAAE,OAAO;QAElEE,QAAQ,OAAO,CAAC,CAACC,QAAQC;YACrBH,YAAY,CAACF,aAAa,CAACK,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,GAAGD;QAC9C;QAEA,OAAOF;IACX;IAEA,OAAOP;AACX;AAEA,SAASW,wBAAwBC,KAAqB,EAAEC,oBAAkD,EAAEC,UAAmB;IAC3H,MAAMf,UAAUb,UAAUA;IAC1B,MAAM6B,SAAS5B,SAASA;IAExB,uEAAuE;IACvE,yEAAyE;IACzE,MAAM6B,kBAAkBrB,mBAAmBiB;IAE3C,OAAOvB,OAAOA,CAAC;QACX,IAAI2B,iBAAiB;YACjB,OAAOH,qBAAqB;gBACxB,yBAAW,IAACpB,SAASA;oBAAC,YAAYqB;;gBAClC,kBAAkBC;gBAClB,aAAa;oBACT,cAAchB,QAAQ,YAAY,GAAGD,6BAA6BC,WAAWkB;gBACjF;gBACA,qBAAqB,CAAC;YAC1B;QACJ;QAEA,OAAO;IACX,GAAG;QAAClB;QAASiB;QAAiBD;QAAQF;QAAsBC;KAAW;AAC3E;AASO,SAASI,UAAUC,KAAqB;IAC3C,MAAM,EACFC,oBAAoB,KAAK,EACzBC,uBAAuB,KAAK,EAC5BP,aAAa,IAAI,EACjB,UAAUD,oBAAoB,EACjC,GAAGM;IACJ,MAAM,CAACP,OAAOU,SAAS,GAAG9B,mBAAmBA,CAAC4B,mBAAmBC;IAEjE,MAAME,SAAStC,SAASA;IAExBS,yBAAyBA,CAAC;QACtB,WAAWoB;IACf;IAEA1B,SAASA,CAAC;QACNmC,OACK,QAAQ,CAAC,iDACT,UAAU,CAACX,OACX,KAAK;IACd,GAAG;QAACA;QAAOW;KAAO;IAElB,MAAMC,iBAAiBb,wBAAwBC,OAAOC,sBAAsBC;IAE5E,qBACI,IAACxB,mCAAmC;QAAC,OAAOgC;kBACxC,kBAAC/B,8BAA8B;YAAC,OAAOqB;sBAClCY;;;AAIjB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function useCanUpdateDeferredRegistrations(): boolean
|
|
1
|
+
export declare function useCanUpdateDeferredRegistrations(): boolean;
|
|
@@ -5,10 +5,21 @@ import { useAppRouterState } from "./AppRouterContext.js";
|
|
|
5
5
|
;// CONCATENATED MODULE: ./src/useCanUpdateDeferredRegistrations.ts
|
|
6
6
|
|
|
7
7
|
function useCanUpdateDeferredRegistrations() {
|
|
8
|
-
const { areModulesReady
|
|
9
|
-
return
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
const { areModulesReady } = useAppRouterState();
|
|
9
|
+
return areModulesReady;
|
|
10
|
+
// return !!(
|
|
11
|
+
// // Do not trigger an update if the deferred registrations has not been registered yet (if there are deferred registrations, once they are
|
|
12
|
+
// // registered, the modules will be marked as ready).
|
|
13
|
+
// areModulesReady
|
|
14
|
+
// // Make sure the apps is actually having deferred registrations.
|
|
15
|
+
// // && deferredRegistrationsUpdatedAt
|
|
16
|
+
// // // If either the public data or the protected data has been updated, update the deferred registrations.
|
|
17
|
+
// // && (
|
|
18
|
+
// // (publicDataUpdatedAt && publicDataUpdatedAt > deferredRegistrationsUpdatedAt) ||
|
|
19
|
+
// // (protectedDataUpdatedAt && protectedDataUpdatedAt > deferredRegistrationsUpdatedAt) ||
|
|
20
|
+
// // (featureFlagsUpdatedAt && featureFlagsUpdatedAt > deferredRegistrationsUpdatedAt)
|
|
21
|
+
// // )
|
|
22
|
+
// );
|
|
12
23
|
}
|
|
13
24
|
|
|
14
25
|
export { useCanUpdateDeferredRegistrations };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCanUpdateDeferredRegistrations.js","sources":["../src/useCanUpdateDeferredRegistrations.ts"],"sourcesContent":["import { useAppRouterState } from \"./AppRouterContext.ts\";\n\nexport function useCanUpdateDeferredRegistrations() {\n const {\n areModulesReady
|
|
1
|
+
{"version":3,"file":"useCanUpdateDeferredRegistrations.js","sources":["../src/useCanUpdateDeferredRegistrations.ts"],"sourcesContent":["import { useAppRouterState } from \"./AppRouterContext.ts\";\n\nexport function useCanUpdateDeferredRegistrations() {\n const {\n areModulesReady\n // publicDataUpdatedAt,\n // protectedDataUpdatedAt,\n // featureFlagsUpdatedAt,\n // deferredRegistrationsUpdatedAt\n } = useAppRouterState();\n\n return areModulesReady;\n\n // return !!(\n // // Do not trigger an update if the deferred registrations has not been registered yet (if there are deferred registrations, once they are\n // // registered, the modules will be marked as ready).\n // areModulesReady\n // // Make sure the apps is actually having deferred registrations.\n // // && deferredRegistrationsUpdatedAt\n // // // If either the public data or the protected data has been updated, update the deferred registrations.\n // // && (\n // // (publicDataUpdatedAt && publicDataUpdatedAt > deferredRegistrationsUpdatedAt) ||\n // // (protectedDataUpdatedAt && protectedDataUpdatedAt > deferredRegistrationsUpdatedAt) ||\n // // (featureFlagsUpdatedAt && featureFlagsUpdatedAt > deferredRegistrationsUpdatedAt)\n // // )\n // );\n}\n"],"names":["useAppRouterState","useCanUpdateDeferredRegistrations","areModulesReady"],"mappings":";;;;;AAA0D;AAEnD,SAASC;IACZ,MAAM,EACFC,eAAe,EAKlB,GAAGF,iBAAiBA;IAErB,OAAOE;AAEP,aAAa;AACb,gJAAgJ;AAChJ,2DAA2D;AAC3D,sBAAsB;AACtB,uEAAuE;AACvE,2CAA2C;AAC3C,iHAAiH;AACjH,cAAc;AACd,8FAA8F;AAC9F,oGAAoG;AACpG,+FAA+F;AAC/F,WAAW;AACX,KAAK;AACT"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useRuntime } from "@squide/core";
|
|
2
|
-
import { useEffect } from "react";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
3
|
import { useCanRegisterDeferredRegistrations } from "./useCanRegisterDeferredRegistrations.js";
|
|
4
4
|
import { useCanUpdateDeferredRegistrations } from "./useCanUpdateDeferredRegistrations.js";
|
|
5
5
|
import { useRegisterDeferredRegistrations } from "./useRegisterDeferredRegistrations.js";
|
|
@@ -26,6 +26,7 @@ import { useUpdateDeferredRegistrations } from "./useUpdateDeferredRegistrations
|
|
|
26
26
|
|
|
27
27
|
function useDeferredRegistrations(data, { onError } = {}) {
|
|
28
28
|
const runtime = useRuntime();
|
|
29
|
+
// const isExecutedBecauseModulesAreNowReadyRef = useRef(true);
|
|
29
30
|
const canRegisterDeferredRegistrations = useCanRegisterDeferredRegistrations();
|
|
30
31
|
const canUpdateDeferredRegistrations = useCanUpdateDeferredRegistrations();
|
|
31
32
|
const registerDeferredRegistrations = useRegisterDeferredRegistrations();
|
|
@@ -46,8 +47,18 @@ function useDeferredRegistrations(data, { onError } = {}) {
|
|
|
46
47
|
data,
|
|
47
48
|
onError
|
|
48
49
|
]);
|
|
50
|
+
const isInitialUpdateDeferredRegistrationsExecution = useRef(true);
|
|
49
51
|
useEffect(()=>{
|
|
50
52
|
if (canUpdateDeferredRegistrations) {
|
|
53
|
+
// HACK: Skipping the first execution successfully passing the gate because this is due to
|
|
54
|
+
// the modules being ready, and it's most certainly the same data that has been forwarded earlier to the deferred registration.
|
|
55
|
+
// Ideally, instead of this hacky ref, it would be a ref tracking the previous data object, and the deferred registration would
|
|
56
|
+
// only be updated if the current data object != than the previous data object. Sadly, it is not possible because
|
|
57
|
+
// of the feature flags.
|
|
58
|
+
if (isInitialUpdateDeferredRegistrationsExecution.current) {
|
|
59
|
+
isInitialUpdateDeferredRegistrationsExecution.current = false;
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
51
62
|
const update = async ()=>{
|
|
52
63
|
const errors = await updateDeferredRegistrations(data);
|
|
53
64
|
if (errors.length > 0 && onError) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDeferredRegistrations.js","sources":["../src/useDeferredRegistrations.ts"],"sourcesContent":["import { useRuntime, type ModuleRegistrationError } from \"@squide/core\";\nimport { useEffect } from \"react\";\nimport { FireflyRuntime } from \"./FireflyRuntime.tsx\";\nimport { useCanRegisterDeferredRegistrations } from \"./useCanRegisterDeferredRegistrations.ts\";\nimport { useCanUpdateDeferredRegistrations } from \"./useCanUpdateDeferredRegistrations.ts\";\nimport { useRegisterDeferredRegistrations } from \"./useRegisterDeferredRegistrations.ts\";\nimport { useUpdateDeferredRegistrations } from \"./useUpdateDeferredRegistrations.ts\";\n\nexport type DeferredRegistrationsErrorCallback = (errors: ModuleRegistrationError[]) => void;\n\nexport interface UseDeferredRegistrationsOptions {\n onError?: DeferredRegistrationsErrorCallback;\n}\n\nexport function useDeferredRegistrations(data?: unknown, { onError }: UseDeferredRegistrationsOptions = {}) {\n const runtime = useRuntime() as FireflyRuntime;\n\n const canRegisterDeferredRegistrations = useCanRegisterDeferredRegistrations();\n const canUpdateDeferredRegistrations = useCanUpdateDeferredRegistrations();\n\n const registerDeferredRegistrations = useRegisterDeferredRegistrations();\n const updateDeferredRegistrations = useUpdateDeferredRegistrations();\n\n useEffect(() => {\n if (canRegisterDeferredRegistrations) {\n const register = async () => {\n const errors = await registerDeferredRegistrations(data);\n\n if (errors.length > 0 && onError) {\n onError(errors);\n }\n };\n\n register();\n }\n }, [canRegisterDeferredRegistrations, registerDeferredRegistrations, data, onError]);\n\n useEffect(() => {\n if (canUpdateDeferredRegistrations) {\n const update = async () => {\n const errors = await updateDeferredRegistrations(data);\n\n if (errors.length > 0 && onError) {\n onError(errors);\n }\n };\n\n update();\n }\n }, [\n canUpdateDeferredRegistrations,\n updateDeferredRegistrations,\n data,\n onError,\n // Trigger this closure when the feature flags changed. Using the timestamp because the\n // actual feature flags are not forwarded to the deferred registrations.\n runtime.appRouterStore.state.featureFlagsUpdatedAt\n ]);\n}\n"],"names":["useRuntime","useEffect","useCanRegisterDeferredRegistrations","useCanUpdateDeferredRegistrations","useRegisterDeferredRegistrations","useUpdateDeferredRegistrations","useDeferredRegistrations","data","onError","runtime","canRegisterDeferredRegistrations","canUpdateDeferredRegistrations","registerDeferredRegistrations","updateDeferredRegistrations","register","errors","update"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAwE;
|
|
1
|
+
{"version":3,"file":"useDeferredRegistrations.js","sources":["../src/useDeferredRegistrations.ts"],"sourcesContent":["import { useRuntime, type ModuleRegistrationError } from \"@squide/core\";\nimport { useEffect, useRef } from \"react\";\nimport { FireflyRuntime } from \"./FireflyRuntime.tsx\";\nimport { useCanRegisterDeferredRegistrations } from \"./useCanRegisterDeferredRegistrations.ts\";\nimport { useCanUpdateDeferredRegistrations } from \"./useCanUpdateDeferredRegistrations.ts\";\nimport { useRegisterDeferredRegistrations } from \"./useRegisterDeferredRegistrations.ts\";\nimport { useUpdateDeferredRegistrations } from \"./useUpdateDeferredRegistrations.ts\";\n\nexport type DeferredRegistrationsErrorCallback = (errors: ModuleRegistrationError[]) => void;\n\nexport interface UseDeferredRegistrationsOptions {\n onError?: DeferredRegistrationsErrorCallback;\n}\n\nexport function useDeferredRegistrations(data?: unknown, { onError }: UseDeferredRegistrationsOptions = {}) {\n const runtime = useRuntime() as FireflyRuntime;\n // const isExecutedBecauseModulesAreNowReadyRef = useRef(true);\n\n const canRegisterDeferredRegistrations = useCanRegisterDeferredRegistrations();\n const canUpdateDeferredRegistrations = useCanUpdateDeferredRegistrations();\n\n const registerDeferredRegistrations = useRegisterDeferredRegistrations();\n const updateDeferredRegistrations = useUpdateDeferredRegistrations();\n\n useEffect(() => {\n if (canRegisterDeferredRegistrations) {\n const register = async () => {\n const errors = await registerDeferredRegistrations(data);\n\n if (errors.length > 0 && onError) {\n onError(errors);\n }\n };\n\n register();\n }\n }, [canRegisterDeferredRegistrations, registerDeferredRegistrations, data, onError]);\n\n const isInitialUpdateDeferredRegistrationsExecution = useRef(true);\n\n useEffect(() => {\n if (canUpdateDeferredRegistrations) {\n // HACK: Skipping the first execution successfully passing the gate because this is due to\n // the modules being ready, and it's most certainly the same data that has been forwarded earlier to the deferred registration.\n // Ideally, instead of this hacky ref, it would be a ref tracking the previous data object, and the deferred registration would\n // only be updated if the current data object != than the previous data object. Sadly, it is not possible because\n // of the feature flags.\n if (isInitialUpdateDeferredRegistrationsExecution.current) {\n isInitialUpdateDeferredRegistrationsExecution.current = false;\n return;\n }\n\n const update = async () => {\n const errors = await updateDeferredRegistrations(data);\n\n if (errors.length > 0 && onError) {\n onError(errors);\n }\n };\n\n update();\n }\n }, [\n canUpdateDeferredRegistrations,\n updateDeferredRegistrations,\n data,\n onError,\n // Trigger this closure when the feature flags changed. Using the timestamp because the\n // actual feature flags are not forwarded to the deferred registrations.\n runtime.appRouterStore.state.featureFlagsUpdatedAt\n ]);\n}\n"],"names":["useRuntime","useEffect","useRef","useCanRegisterDeferredRegistrations","useCanUpdateDeferredRegistrations","useRegisterDeferredRegistrations","useUpdateDeferredRegistrations","useDeferredRegistrations","data","onError","runtime","canRegisterDeferredRegistrations","canUpdateDeferredRegistrations","registerDeferredRegistrations","updateDeferredRegistrations","register","errors","isInitialUpdateDeferredRegistrationsExecution","update"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAwE;AAC9B;AAEqD;AACJ;AACF;AACJ;AAQ9E,SAASO,yBAAyBC,IAAc,EAAE,EAAEC,OAAO,EAAmC,GAAG,CAAC,CAAC;IACtG,MAAMC,UAAUV,UAAUA;IAC1B,+DAA+D;IAE/D,MAAMW,mCAAmCR,mCAAmCA;IAC5E,MAAMS,iCAAiCR,iCAAiCA;IAExE,MAAMS,gCAAgCR,gCAAgCA;IACtE,MAAMS,8BAA8BR,8BAA8BA;IAElEL,SAASA,CAAC;QACN,IAAIU,kCAAkC;YAClC,MAAMI,WAAW;gBACb,MAAMC,SAAS,MAAMH,8BAA8BL;gBAEnD,IAAIQ,OAAO,MAAM,GAAG,KAAKP,SAAS;oBAC9BA,QAAQO;gBACZ;YACJ;YAEAD;QACJ;IACJ,GAAG;QAACJ;QAAkCE;QAA+BL;QAAMC;KAAQ;IAEnF,MAAMQ,gDAAgDf,MAAMA,CAAC;IAE7DD,SAASA,CAAC;QACN,IAAIW,gCAAgC;YAChC,0FAA0F;YAC1F,+HAA+H;YAC/H,+HAA+H;YAC/H,iHAAiH;YACjH,wBAAwB;YACxB,IAAIK,8CAA8C,OAAO,EAAE;gBACvDA,8CAA8C,OAAO,GAAG;gBACxD;YACJ;YAEA,MAAMC,SAAS;gBACX,MAAMF,SAAS,MAAMF,4BAA4BN;gBAEjD,IAAIQ,OAAO,MAAM,GAAG,KAAKP,SAAS;oBAC9BA,QAAQO;gBACZ;YACJ;YAEAE;QACJ;IACJ,GAAG;QACCN;QACAE;QACAN;QACAC;QACA,uFAAuF;QACvF,wEAAwE;QACxEC,QAAQ,cAAc,CAAC,KAAK,CAAC,qBAAqB;KACrD;AACL"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@squide/firefly",
|
|
3
3
|
"author": "Workleap",
|
|
4
|
-
"version": "16.1
|
|
4
|
+
"version": "16.2.1",
|
|
5
5
|
"description": "Squide bundle for the firefly technology stack.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
@@ -49,10 +49,10 @@
|
|
|
49
49
|
"@workleap-telemetry/core": "^2.0.0",
|
|
50
50
|
"@workleap/logging": "^1.3.6",
|
|
51
51
|
"uuid": "^13.0.0",
|
|
52
|
-
"@squide/core": "^6.1.14",
|
|
53
52
|
"@squide/env-vars": "^1.4.19",
|
|
54
53
|
"@squide/launch-darkly": "^1.0.10",
|
|
55
54
|
"@squide/msw": "^4.0.17",
|
|
55
|
+
"@squide/core": "^6.1.14",
|
|
56
56
|
"@squide/react-router": "^8.1.17"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
@@ -63,13 +63,13 @@
|
|
|
63
63
|
"@types/react": "19.2.14",
|
|
64
64
|
"@types/react-dom": "19.2.3",
|
|
65
65
|
"@typescript-eslint/parser": "8.56.1",
|
|
66
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
66
|
+
"@typescript/native-preview": "7.0.0-dev.20260303.1",
|
|
67
67
|
"@vitejs/plugin-react": "5.1.4",
|
|
68
68
|
"@workleap/eslint-configs": "1.1.13",
|
|
69
69
|
"@workleap/rslib-configs": "1.1.7",
|
|
70
70
|
"@workleap/typescript-configs": "3.0.7",
|
|
71
71
|
"eslint": "9.39.2",
|
|
72
|
-
"happy-dom": "20.
|
|
72
|
+
"happy-dom": "20.8.3",
|
|
73
73
|
"typescript": "5.9.3",
|
|
74
74
|
"typescript-eslint": "8.54.0",
|
|
75
75
|
"vitest": "4.0.18"
|
package/src/AppRouter.tsx
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { useLogger } from "@squide/core";
|
|
1
|
+
import { useLogger, useRuntime } from "@squide/core";
|
|
2
2
|
import { useRoutes, type Route } from "@squide/react-router";
|
|
3
3
|
import { useEffect, useMemo, type ReactElement } from "react";
|
|
4
|
+
import { DataStrategyFunction, DataStrategyFunctionArgs, DataStrategyResult, DOMRouterOpts } from "react-router";
|
|
4
5
|
import type { RouterProviderProps } from "react-router/dom";
|
|
5
6
|
import { AppRouterDispatcherContext, AppRouterStateContext } from "./AppRouterContext.ts";
|
|
6
7
|
import { useAppRouterReducer, type AppRouterState } from "./AppRouterReducer.ts";
|
|
8
|
+
import { FireflyRuntime } from "./FireflyRuntime.tsx";
|
|
7
9
|
import { RootRoute } from "./RootRoute.tsx";
|
|
8
10
|
import { useStrictRegistrationMode } from "./useStrictRegistrationMode.ts";
|
|
9
11
|
|
|
@@ -14,6 +16,7 @@ export interface AppRouterRenderFunctionArgs {
|
|
|
14
16
|
export interface RenderRouterProviderFunctionArgs {
|
|
15
17
|
rootRoute: ReactElement;
|
|
16
18
|
registeredRoutes: Route[];
|
|
19
|
+
routerProps: DOMRouterOpts;
|
|
17
20
|
routerProviderProps: Omit<RouterProviderProps, "router">;
|
|
18
21
|
}
|
|
19
22
|
|
|
@@ -27,7 +30,40 @@ export function useCanRenderRouter({ areModulesRegistered, areModulesReady: areM
|
|
|
27
30
|
);
|
|
28
31
|
}
|
|
29
32
|
|
|
33
|
+
// This is a custom React Router data strategy (https://reactrouter.com/api/data-routers/createMemoryRouter#optsdatastrategy)
|
|
34
|
+
// to delay the execution of React Router data browser loaders until MSW is ready.
|
|
35
|
+
// The data strategy implemention is copied from React Router default data strategy: https://github.com/remix-run/react-router/blob/main/packages/react-router/lib/router/router.ts#L5710.
|
|
36
|
+
export function createWaitForMswDataStrategy(runtime: FireflyRuntime) {
|
|
37
|
+
const strategy: (args: DataStrategyFunctionArgs<unknown>) => ReturnType<DataStrategyFunction<unknown>> = async args => {
|
|
38
|
+
await new Promise(resolve => {
|
|
39
|
+
if (runtime.mswState.isReady) {
|
|
40
|
+
resolve(null);
|
|
41
|
+
} else {
|
|
42
|
+
const handler = () => {
|
|
43
|
+
runtime.mswState.removeMswReadyListener(handler);
|
|
44
|
+
resolve(null);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
runtime.mswState.addMswReadyListener(handler);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const matchesToLoad = args.matches.filter(m => m.shouldCallHandler);
|
|
52
|
+
const keyedResults: Record<string, DataStrategyResult> = {};
|
|
53
|
+
const results = await Promise.all(matchesToLoad.map(m => m.resolve()));
|
|
54
|
+
|
|
55
|
+
results.forEach((result, i) => {
|
|
56
|
+
keyedResults[matchesToLoad[i].route.id] = result;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return keyedResults;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return strategy;
|
|
63
|
+
}
|
|
64
|
+
|
|
30
65
|
function useRenderRouterProvider(state: AppRouterState, renderRouterProvider: RenderRouterProviderFunction, strictMode: boolean) {
|
|
66
|
+
const runtime = useRuntime() as FireflyRuntime;
|
|
31
67
|
const routes = useRoutes();
|
|
32
68
|
|
|
33
69
|
// The value is computed outside of the router provider memo to prevent
|
|
@@ -39,12 +75,15 @@ function useRenderRouterProvider(state: AppRouterState, renderRouterProvider: Re
|
|
|
39
75
|
return renderRouterProvider({
|
|
40
76
|
rootRoute: <RootRoute strictMode={strictMode} />,
|
|
41
77
|
registeredRoutes: routes,
|
|
78
|
+
routerProps: {
|
|
79
|
+
dataStrategy: runtime.isMswEnabled ? createWaitForMswDataStrategy(runtime) : undefined
|
|
80
|
+
},
|
|
42
81
|
routerProviderProps: {}
|
|
43
82
|
});
|
|
44
83
|
}
|
|
45
84
|
|
|
46
85
|
return null;
|
|
47
|
-
}, [canRenderRouter, routes, renderRouterProvider, strictMode]);
|
|
86
|
+
}, [runtime, canRenderRouter, routes, renderRouterProvider, strictMode]);
|
|
48
87
|
}
|
|
49
88
|
|
|
50
89
|
export interface AppRouterProps {
|
|
@@ -2,24 +2,26 @@ import { useAppRouterState } from "./AppRouterContext.ts";
|
|
|
2
2
|
|
|
3
3
|
export function useCanUpdateDeferredRegistrations() {
|
|
4
4
|
const {
|
|
5
|
-
areModulesReady
|
|
6
|
-
publicDataUpdatedAt,
|
|
7
|
-
protectedDataUpdatedAt,
|
|
8
|
-
featureFlagsUpdatedAt,
|
|
9
|
-
deferredRegistrationsUpdatedAt
|
|
5
|
+
areModulesReady
|
|
6
|
+
// publicDataUpdatedAt,
|
|
7
|
+
// protectedDataUpdatedAt,
|
|
8
|
+
// featureFlagsUpdatedAt,
|
|
9
|
+
// deferredRegistrationsUpdatedAt
|
|
10
10
|
} = useAppRouterState();
|
|
11
11
|
|
|
12
|
-
return
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
)
|
|
12
|
+
return areModulesReady;
|
|
13
|
+
|
|
14
|
+
// return !!(
|
|
15
|
+
// // Do not trigger an update if the deferred registrations has not been registered yet (if there are deferred registrations, once they are
|
|
16
|
+
// // registered, the modules will be marked as ready).
|
|
17
|
+
// areModulesReady
|
|
18
|
+
// // Make sure the apps is actually having deferred registrations.
|
|
19
|
+
// // && deferredRegistrationsUpdatedAt
|
|
20
|
+
// // // If either the public data or the protected data has been updated, update the deferred registrations.
|
|
21
|
+
// // && (
|
|
22
|
+
// // (publicDataUpdatedAt && publicDataUpdatedAt > deferredRegistrationsUpdatedAt) ||
|
|
23
|
+
// // (protectedDataUpdatedAt && protectedDataUpdatedAt > deferredRegistrationsUpdatedAt) ||
|
|
24
|
+
// // (featureFlagsUpdatedAt && featureFlagsUpdatedAt > deferredRegistrationsUpdatedAt)
|
|
25
|
+
// // )
|
|
26
|
+
// );
|
|
25
27
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useRuntime, type ModuleRegistrationError } from "@squide/core";
|
|
2
|
-
import { useEffect } from "react";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
3
|
import { FireflyRuntime } from "./FireflyRuntime.tsx";
|
|
4
4
|
import { useCanRegisterDeferredRegistrations } from "./useCanRegisterDeferredRegistrations.ts";
|
|
5
5
|
import { useCanUpdateDeferredRegistrations } from "./useCanUpdateDeferredRegistrations.ts";
|
|
@@ -14,6 +14,7 @@ export interface UseDeferredRegistrationsOptions {
|
|
|
14
14
|
|
|
15
15
|
export function useDeferredRegistrations(data?: unknown, { onError }: UseDeferredRegistrationsOptions = {}) {
|
|
16
16
|
const runtime = useRuntime() as FireflyRuntime;
|
|
17
|
+
// const isExecutedBecauseModulesAreNowReadyRef = useRef(true);
|
|
17
18
|
|
|
18
19
|
const canRegisterDeferredRegistrations = useCanRegisterDeferredRegistrations();
|
|
19
20
|
const canUpdateDeferredRegistrations = useCanUpdateDeferredRegistrations();
|
|
@@ -35,8 +36,20 @@ export function useDeferredRegistrations(data?: unknown, { onError }: UseDeferre
|
|
|
35
36
|
}
|
|
36
37
|
}, [canRegisterDeferredRegistrations, registerDeferredRegistrations, data, onError]);
|
|
37
38
|
|
|
39
|
+
const isInitialUpdateDeferredRegistrationsExecution = useRef(true);
|
|
40
|
+
|
|
38
41
|
useEffect(() => {
|
|
39
42
|
if (canUpdateDeferredRegistrations) {
|
|
43
|
+
// HACK: Skipping the first execution successfully passing the gate because this is due to
|
|
44
|
+
// the modules being ready, and it's most certainly the same data that has been forwarded earlier to the deferred registration.
|
|
45
|
+
// Ideally, instead of this hacky ref, it would be a ref tracking the previous data object, and the deferred registration would
|
|
46
|
+
// only be updated if the current data object != than the previous data object. Sadly, it is not possible because
|
|
47
|
+
// of the feature flags.
|
|
48
|
+
if (isInitialUpdateDeferredRegistrationsExecution.current) {
|
|
49
|
+
isInitialUpdateDeferredRegistrationsExecution.current = false;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
40
53
|
const update = async () => {
|
|
41
54
|
const errors = await updateDeferredRegistrations(data);
|
|
42
55
|
|