@squide/firefly 16.1.11 → 16.2.0

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 CHANGED
@@ -1,5 +1,23 @@
1
1
  # @squide/firefly
2
2
 
3
+ ## 16.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#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.
8
+
9
+ ## 16.1.12
10
+
11
+ ### Patch Changes
12
+
13
+ - [#551](https://github.com/workleap/wl-squide/pull/551) [`0a1bbdd`](https://github.com/workleap/wl-squide/commit/0a1bbddc758dc805e980b224f2f7c73fbd69f84b) Thanks [@patricklafrance](https://github.com/patricklafrance)! - Updated workspace dependency specifiers to use ranged workspace versions in package.json files.
14
+
15
+ - Updated dependencies [[`0a1bbdd`](https://github.com/workleap/wl-squide/commit/0a1bbddc758dc805e980b224f2f7c73fbd69f84b)]:
16
+ - @squide/launch-darkly@1.0.10
17
+ - @squide/react-router@8.1.17
18
+ - @squide/env-vars@1.4.19
19
+ - @squide/msw@4.0.17
20
+
3
21
  ## 16.1.11
4
22
 
5
23
  ### Patch Changes
@@ -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
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAAyC;AACoB;AACC;AAE4B;AACT;AACrC;AAC+B;AAcpE,SAASS,mBAAmB,EAAEC,oBAAoB,EAAE,iBAAiBC,oBAAoB,EAAkB;IAC9G,OACI,qIAAqI;IACrI,iCAAiC;IAChCD,wBAAwBC;AAEjC;AAEA,SAASC,wBAAwBC,KAAqB,EAAEC,oBAAkD,EAAEC,UAAmB;IAC3H,MAAMC,SAASf,SAASA;IAExB,uEAAuE;IACvE,yEAAyE;IACzE,MAAMgB,kBAAkBR,mBAAmBI;IAE3C,OAAOV,OAAOA,CAAC;QACX,IAAIc,iBAAiB;YACjB,OAAOH,qBAAqB;gBACxB,yBAAW,IAACP,SAASA;oBAAC,YAAYQ;;gBAClC,kBAAkBC;gBAClB,qBAAqB,CAAC;YAC1B;QACJ;QAEA,OAAO;IACX,GAAG;QAACC;QAAiBD;QAAQF;QAAsBC;KAAW;AAClE;AASO,SAASG,UAAUC,KAAqB;IAC3C,MAAM,EACFC,oBAAoB,KAAK,EACzBC,uBAAuB,KAAK,EAC5BN,aAAa,IAAI,EACjB,UAAUD,oBAAoB,EACjC,GAAGK;IACJ,MAAM,CAACN,OAAOS,SAAS,GAAGhB,mBAAmBA,CAACc,mBAAmBC;IAEjE,MAAME,SAASvB,SAASA;IAExBQ,yBAAyBA,CAAC;QACtB,WAAWO;IACf;IAEAb,SAASA,CAAC;QACNqB,OACK,QAAQ,CAAC,iDACT,UAAU,CAACV,OACX,KAAK;IACd,GAAG;QAACA;QAAOU;KAAO;IAElB,MAAMC,iBAAiBZ,wBAAwBC,OAAOC,sBAAsBC;IAE5E,qBACI,IAACX,mCAAmC;QAAC,OAAOkB;kBACxC,kBAACjB,8BAA8B;YAAC,OAAOQ;sBAClCW;;;AAIjB"}
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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@squide/firefly",
3
3
  "author": "Workleap",
4
- "version": "16.1.11",
4
+ "version": "16.2.0",
5
5
  "description": "Squide bundle for the firefly technology stack.",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
@@ -49,11 +49,11 @@
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
- "@squide/env-vars": "1.4.18",
54
- "@squide/launch-darkly": "1.0.9",
55
- "@squide/msw": "4.0.16",
56
- "@squide/react-router": "8.1.16"
52
+ "@squide/core": "^6.1.14",
53
+ "@squide/launch-darkly": "^1.0.10",
54
+ "@squide/env-vars": "^1.4.19",
55
+ "@squide/msw": "^4.0.17",
56
+ "@squide/react-router": "^8.1.17"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@eslint/js": "9.39.2",
@@ -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.20260224.1",
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.7.0",
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 {