From 26bcecb9c744479f795374bea1dddd418450636e Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Mon, 2 Oct 2023 22:21:05 -0400 Subject: [PATCH 01/51] Added a plugin for MSW --- docs/getting-started/create-host.md | 2 +- docs/getting-started/create-local-module.md | 2 +- docs/getting-started/create-remote-module.md | 4 +- docs/guides/develop-a-module-in-isolation.md | 4 + package.json | 3 +- packages/core/package.json | 1 + .../src/federation/registerLocalModules.ts | 2 +- packages/core/src/index.ts | 2 + packages/core/src/plugins/plugin.ts | 11 + packages/core/src/runtime/abstractRuntime.ts | 10 +- packages/msw/.eslintrc.json | 5 + packages/msw/README.md | 13 + packages/msw/package.json | 53 + packages/msw/src/index.ts | 3 + packages/msw/src/mswPlugin.ts | 31 + packages/msw/src/requestHandlerRegistry.ts | 14 + packages/msw/tsconfig.json | 9 + packages/msw/tsup.build.ts | 3 + packages/msw/tsup.dev.ts | 3 + .../src/registerRemoteModules.ts | 2 +- pnpm-lock.yaml | 1960 ++++++++++------- sample/another-remote-module/package.json | 1 + .../another-remote-module/public/favicon.png | Bin 0 -> 31428 bytes .../another-remote-module/public/index.html | 1 + sample/host/mocks/browser.ts | 8 + sample/host/mocks/handlers.ts | 13 + sample/host/package.json | 5 + sample/host/public/index.html | 12 +- sample/host/public/mockServiceWorker.js | 303 +++ sample/host/src/bootstrap.tsx | 12 +- sample/host/webpack.dev.js | 3 +- sample/local-module/package.json | 1 + sample/local-module/public/favicon.png | Bin 0 -> 31428 bytes sample/local-module/public/index.html | 11 +- sample/remote-module/mocks/browser.ts | 7 + sample/remote-module/mocks/handlers.ts | 340 +++ sample/remote-module/package.json | 7 +- sample/remote-module/public/favicon.png | Bin 0 -> 31428 bytes sample/remote-module/public/index.html | 1 + .../remote-module/public/mockServiceWorker.js | 303 +++ sample/remote-module/src/dev/index.tsx | 12 + sample/remote-module/src/register.tsx | 10 +- sample/remote-module/webpack.dev.js | 8 +- 43 files changed, 2401 insertions(+), 794 deletions(-) create mode 100644 packages/core/src/plugins/plugin.ts create mode 100644 packages/msw/.eslintrc.json create mode 100644 packages/msw/README.md create mode 100644 packages/msw/package.json create mode 100644 packages/msw/src/index.ts create mode 100644 packages/msw/src/mswPlugin.ts create mode 100644 packages/msw/src/requestHandlerRegistry.ts create mode 100644 packages/msw/tsconfig.json create mode 100644 packages/msw/tsup.build.ts create mode 100644 packages/msw/tsup.dev.ts create mode 100644 sample/another-remote-module/public/favicon.png create mode 100644 sample/host/mocks/browser.ts create mode 100644 sample/host/mocks/handlers.ts create mode 100644 sample/host/public/mockServiceWorker.js create mode 100644 sample/local-module/public/favicon.png create mode 100644 sample/remote-module/mocks/browser.ts create mode 100644 sample/remote-module/mocks/handlers.ts create mode 100644 sample/remote-module/public/favicon.png create mode 100644 sample/remote-module/public/mockServiceWorker.js diff --git a/docs/getting-started/create-host.md b/docs/getting-started/create-host.md index 8c534f46c..578c1e43a 100644 --- a/docs/getting-started/create-host.md +++ b/docs/getting-started/create-host.md @@ -29,7 +29,7 @@ npm install @squide/core @squide/react-router @squide/webpack-module-federation +++ !!!warning -While you can use any package manager to develop an application with Squide, it is highly recommended that you use [PNPM](https://pnpm.io/) as the following guide has been developed and tested with PNPM. +While you can use any package manager to develop an application with Squide, it is highly recommended that you use [PNPM](https://pnpm.io/) as the guides has been developed and tested with PNPM. !!! ## Setup the application diff --git a/docs/getting-started/create-local-module.md b/docs/getting-started/create-local-module.md index a19a75b7e..70031a7c5 100644 --- a/docs/getting-started/create-local-module.md +++ b/docs/getting-started/create-local-module.md @@ -40,7 +40,7 @@ npm install @squide/core @squide/react-router react react-dom react-router-dom +++ !!!warning -While you can use any package manager to develop an application with Squide, it is highly recommend that you use [PNPM](https://pnpm.io/) as the following guide has been developed and tested with PNPM. +While you can use any package manager to develop an application with Squide, it is highly recommend that you use [PNPM](https://pnpm.io/) as the guides has been developed and tested with PNPM. !!! ## Setup the application diff --git a/docs/getting-started/create-remote-module.md b/docs/getting-started/create-remote-module.md index ec05e3240..583f04648 100644 --- a/docs/getting-started/create-remote-module.md +++ b/docs/getting-started/create-remote-module.md @@ -30,7 +30,7 @@ npm install @squide/core @squide/react-router @squide/webpack-module-federation +++ !!!warning -While you can use any package manager to develop an application with Squide, it is highly recommended that you use [PNPM](https://pnpm.io/) as the following guide has been developed and tested with PNPM. +While you can use any package manager to develop an application with Squide, it is highly recommended that you use [PNPM](https://pnpm.io/) as the guides has been developed and tested with PNPM. !!! ## Setup the application @@ -67,7 +67,7 @@ import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import type { AppContext } from "@sample/shared"; import { Page } from "./Page.tsx"; -export const register: ModuleRegisterFunction = (runtime: Runtime, context: AppContext) => { +export const register: ModuleRegisterFunction = (runtime, context) => { runtime.registerRoutes([ { path: "/remote/page", diff --git a/docs/guides/develop-a-module-in-isolation.md b/docs/guides/develop-a-module-in-isolation.md index 171e52fbc..37e34c8bf 100644 --- a/docs/guides/develop-a-module-in-isolation.md +++ b/docs/guides/develop-a-module-in-isolation.md @@ -324,6 +324,10 @@ npm install -D @workleap/webpack-configs @workleap/swc-configs @workleap/browser ``` +++ +!!!warning +While you can use any package manager to develop an application with Squide, it is highly recommended that you use [PNPM](https://pnpm.io/) as the guides has been developed and tested with PNPM. +!!! + Then, create the following files in the local module application: ``` !#2-3,7-12 diff --git a/package.json b/package.json index 8d7c1edd6..25a5b13f2 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ }, "scripts": { "postinstall": "pnpm -r --parallel --include-workspace-root exec pnpm dlx rimraf node_modules/.cache && pnpm temporary-script-until-build-order-topology-is-fixed", - "temporary-script-until-build-order-topology-is-fixed": "pnpm build", + "temporary-script-until-build-order-topology-is-fixed": "pnpm build && pnpm build-sample", "dev": "pnpm --filter \"./packages/*\" -r --parallel dev", "build": "pnpm --filter \"./packages/*\" -r --parallel build", "test": "jest", @@ -23,6 +23,7 @@ "reset": "pnpm clean && pnpm reset:modules", "reset:modules": "pnpm -r --parallel --include-workspace-root exec pnpm dlx rimraf node_modules pnpm-lock.yaml", "dev-sample": "pnpm --filter \"./sample/*\" -r --parallel dev", + "dev-sample-msw": "pnpm --filter \"./sample/*\" -r --parallel dev-msw", "build-sample": "pnpm --filter \"./sample/*\" -r --parallel build", "serve-sample": "pnpm --filter \"./sample/*\" -r --parallel serve-build", "deploy-sample": "cross-env NETLIFY=true pnpm build-sample && pnpm run deploy-sample:host && pnpm run deploy-sample:remote-module && pnpm run deploy-sample:another-remote-module", diff --git a/packages/core/package.json b/packages/core/package.json index 296691e34..39f00f7c8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -48,6 +48,7 @@ "dependencies": { "eventemitter3": "5.0.1" }, + "sideEffects": false, "engines": { "node": ">=18.0.0" diff --git a/packages/core/src/federation/registerLocalModules.ts b/packages/core/src/federation/registerLocalModules.ts index 9fc022e08..3a76afb37 100644 --- a/packages/core/src/federation/registerLocalModules.ts +++ b/packages/core/src/federation/registerLocalModules.ts @@ -13,7 +13,7 @@ export interface RegisterLocalModulesOptions { export function registerLocalModules(registerFunctions: ModuleRegisterFunction[], runtime: TRuntime, { context }: RegisterLocalModulesOptions = {}) { if (registrationStatus !== "none") { - throw new Error("[squide] The \"registerLocalModules\" function can only be called once."); + throw new Error("[squide] registerLocalModules() can only be called once."); } registrationStatus = "in-progress"; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 114f15a8e..61cae374d 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -22,3 +22,5 @@ export * from "./federation/moduleRegistrationStatus.ts"; export * from "./federation/registerLocalModules.ts"; export * from "./federation/registerModule.ts"; +export * from "./plugins/plugin.ts"; + diff --git a/packages/core/src/plugins/plugin.ts b/packages/core/src/plugins/plugin.ts new file mode 100644 index 000000000..c95523d7d --- /dev/null +++ b/packages/core/src/plugins/plugin.ts @@ -0,0 +1,11 @@ +export abstract class Plugin { + protected readonly _name: string; + + constructor(name: string) { + this._name = name; + } + + get name() { + return this._name; + } +} diff --git a/packages/core/src/runtime/abstractRuntime.ts b/packages/core/src/runtime/abstractRuntime.ts index 3bba238d4..85f62876e 100644 --- a/packages/core/src/runtime/abstractRuntime.ts +++ b/packages/core/src/runtime/abstractRuntime.ts @@ -1,5 +1,6 @@ import type { Logger } from "../logging/logger.ts"; import { EventBus } from "../messaging/eventBus.ts"; +import type { Plugin } from "../plugins/plugin.ts"; import { RuntimeLogger } from "./RuntimeLogger.ts"; export type SessionAccessorFunction = () => unknown; @@ -8,6 +9,7 @@ export type RuntimeMode = "development" | "production"; export interface RuntimeOptions { mode?: RuntimeMode; + plugins?: Plugin[]; loggers?: Logger[]; services?: Record; sessionAccessor?: SessionAccessorFunction; @@ -25,13 +27,15 @@ export const RootMenuId = "root"; export abstract class AbstractRuntime { protected _mode: RuntimeMode; + protected readonly _plugins: Plugin[]; protected readonly _logger: RuntimeLogger; protected readonly _eventBus: EventBus; protected _services: Record; protected _sessionAccessor?: SessionAccessorFunction; - constructor({ mode = "development", loggers, services = {}, sessionAccessor }: RuntimeOptions = {}) { + constructor({ mode = "development", plugins = [], loggers, services = {}, sessionAccessor }: RuntimeOptions = {}) { this._mode = mode; + this._plugins = plugins; this._logger = new RuntimeLogger(loggers); this._eventBus = new EventBus({ logger: this._logger }); this._services = services; @@ -50,6 +54,10 @@ export abstract class AbstractRuntime=18.0.0" + } +} diff --git a/packages/msw/src/index.ts b/packages/msw/src/index.ts new file mode 100644 index 000000000..465ac19e8 --- /dev/null +++ b/packages/msw/src/index.ts @@ -0,0 +1,3 @@ +export * from "./mswPlugin.ts"; +export * from "./requestHandlerRegistry.ts"; + diff --git a/packages/msw/src/mswPlugin.ts b/packages/msw/src/mswPlugin.ts new file mode 100644 index 000000000..67d866bf4 --- /dev/null +++ b/packages/msw/src/mswPlugin.ts @@ -0,0 +1,31 @@ +import { Plugin } from "@squide/core"; +import type { RestHandler } from "msw"; +import { RequestHandlerRegistry } from "./requestHandlerRegistry.ts"; + +const Name = "MswPlugin"; + +export class MswPlugin extends Plugin { + readonly #requestHandlerRegistry = new RequestHandlerRegistry(); + + constructor() { + super(Name); + } + + registerRequestHandlers(handlers: RestHandler[]) { + this.#requestHandlerRegistry.add(handlers); + } + + get requestHandlers(): RestHandler[] { + return this.#requestHandlerRegistry.handlers; + } +} + +export function getMswPlugin(plugins: Plugin[]) { + const plugin = plugins.find(x => x.name === Name); + + if (!plugin) { + throw new Error("[squide] Cannot find MSW plugin. Did you added an instance of the MswPlugin class to your application Runtime?"); + } + + return plugin as MswPlugin; +} diff --git a/packages/msw/src/requestHandlerRegistry.ts b/packages/msw/src/requestHandlerRegistry.ts new file mode 100644 index 000000000..7c7f5c7fb --- /dev/null +++ b/packages/msw/src/requestHandlerRegistry.ts @@ -0,0 +1,14 @@ +import { type RestHandler } from "msw"; + +export class RequestHandlerRegistry { + readonly #handlers: RestHandler[] = []; + + add(handlers: RestHandler[]) { + this.#handlers.push(...handlers); + } + + get handlers(): RestHandler[] { + return this.#handlers; + } +} + diff --git a/packages/msw/tsconfig.json b/packages/msw/tsconfig.json new file mode 100644 index 000000000..5161504c1 --- /dev/null +++ b/packages/msw/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@workleap/typescript-configs/library.json", + "compilerOptions": { + "paths": { + "@squide/core": ["../core/src/index.ts"] + } + }, + "exclude": ["dist", "node_modules"] +} diff --git a/packages/msw/tsup.build.ts b/packages/msw/tsup.build.ts new file mode 100644 index 000000000..1b2c65a89 --- /dev/null +++ b/packages/msw/tsup.build.ts @@ -0,0 +1,3 @@ +import { defineBuildConfig } from "@workleap/tsup-configs"; + +export default defineBuildConfig(); diff --git a/packages/msw/tsup.dev.ts b/packages/msw/tsup.dev.ts new file mode 100644 index 000000000..8aa2904c8 --- /dev/null +++ b/packages/msw/tsup.dev.ts @@ -0,0 +1,3 @@ +import { defineDevConfig } from "@workleap/tsup-configs"; + +export default defineDevConfig(); diff --git a/packages/webpack-module-federation/src/registerRemoteModules.ts b/packages/webpack-module-federation/src/registerRemoteModules.ts index b374b8863..9d62b6ee3 100644 --- a/packages/webpack-module-federation/src/registerRemoteModules.ts +++ b/packages/webpack-module-federation/src/registerRemoteModules.ts @@ -24,7 +24,7 @@ export interface RegisterRemoteModulesOptions { export async function registerRemoteModules(remotes: RemoteDefinition[], runtime: AbstractRuntime, { context }: RegisterRemoteModulesOptions = {}) { if (registrationStatus !== "none") { - throw new Error("[squide] The \"registerRemoteModules\" function can only be called once."); + throw new Error("[squide] registerRemoteModules() can only be called once."); } const errors: RegistrationError[] = []; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d838817f5..ce807ae20 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,7 +74,7 @@ importers: version: 18.2.0(react@18.2.0) tsup: specifier: 7.2.0 - version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.30)(ts-node@10.9.1)(typescript@5.2.2) + version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 @@ -102,7 +102,38 @@ importers: version: 29.7.0(@types/node@20.6.3)(ts-node@10.9.1) tsup: specifier: 7.2.0 - version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.30)(ts-node@10.9.1)(typescript@5.2.2) + version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) + typescript: + specifier: 5.2.2 + version: 5.2.2 + + packages/msw: + dependencies: + '@squide/core': + specifier: workspace:* + version: link:../core + devDependencies: + '@types/jest': + specifier: 29.5.5 + version: 29.5.5 + '@workleap/eslint-plugin': + specifier: 2.1.1 + version: 2.1.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) + '@workleap/tsup-configs': + specifier: 3.0.1 + version: 3.0.1(tsup@7.2.0)(typescript@5.2.2) + '@workleap/typescript-configs': + specifier: 3.0.2 + version: 3.0.2(typescript@5.2.2) + jest: + specifier: 29.7.0 + version: 29.7.0(@types/node@20.6.3)(ts-node@10.9.1) + msw: + specifier: 1.3.2 + version: 1.3.2(typescript@5.2.2) + tsup: + specifier: 7.2.0 + version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 @@ -169,10 +200,10 @@ importers: version: 18.2.0(react@18.2.0) ts-jest: specifier: 29.1.1 - version: 29.1.1(@babel/core@7.22.20)(esbuild@0.18.20)(jest@29.7.0)(typescript@5.2.2) + version: 29.1.1(@babel/core@7.23.0)(esbuild@0.18.20)(jest@29.7.0)(typescript@5.2.2) tsup: specifier: 7.2.0 - version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.30)(ts-node@10.9.1)(typescript@5.2.2) + version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 @@ -224,7 +255,7 @@ importers: version: 3.0.2(typescript@5.2.2) '@workleap/webpack-configs': specifier: 1.0.8 - version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(browserslist@4.21.10)(esbuild@0.18.20)(postcss@8.4.30)(react-refresh@0.14.0)(typescript@5.2.2)(webpack@5.88.2) + version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(browserslist@4.21.10)(esbuild@0.18.20)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack@5.88.2) jest: specifier: 29.7.0 version: 29.7.0(@types/node@20.6.3)(ts-node@10.9.1) @@ -236,10 +267,10 @@ importers: version: 18.2.0(react@18.2.0) ts-jest: specifier: 29.1.1 - version: 29.1.1(@babel/core@7.22.20)(esbuild@0.18.20)(jest@29.7.0)(typescript@5.2.2) + version: 29.1.1(@babel/core@7.23.0)(esbuild@0.18.20)(jest@29.7.0)(typescript@5.2.2) tsup: specifier: 7.2.0 - version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.30)(ts-node@10.9.1)(typescript@5.2.2) + version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 @@ -303,7 +334,7 @@ importers: version: 3.0.2(typescript@5.2.2) '@workleap/webpack-configs': specifier: 1.0.8 - version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(postcss@8.4.30)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) + version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) browserslist: specifier: 4.21.10 version: 4.21.10 @@ -340,12 +371,18 @@ importers: '@squide/fakes': specifier: workspace:* version: link:../../packages/fakes + '@squide/msw': + specifier: workspace:* + version: link:../../packages/msw '@squide/react-router': specifier: workspace:* version: link:../../packages/react-router '@squide/webpack-module-federation': specifier: workspace:* version: link:../../packages/webpack-module-federation + '@tanstack/react-query': + specifier: rc + version: 5.0.0-rc.4(react-dom@18.2.0)(react@18.2.0) react: specifier: 18.2.0 version: 18.2.0 @@ -385,16 +422,22 @@ importers: version: 3.0.2(typescript@5.2.2) '@workleap/webpack-configs': specifier: 1.0.8 - version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(postcss@8.4.30)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) + version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) browserslist: specifier: 4.21.10 version: 4.21.10 copyfiles: specifier: 2.4.1 version: 2.4.1 + cross-env: + specifier: 7.0.3 + version: 7.0.3 http-server: specifier: 14.1.1 version: 14.1.1 + msw: + specifier: 1.3.2 + version: 1.3.2(typescript@5.2.2) nodemon: specifier: 3.0.1 version: 3.0.1 @@ -467,7 +510,7 @@ importers: version: 3.0.2(typescript@5.2.2) '@workleap/webpack-configs': specifier: 1.0.8 - version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(esbuild@0.18.20)(postcss@8.4.30)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) + version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(esbuild@0.18.20)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) browserslist: specifier: 4.21.10 version: 4.21.10 @@ -479,7 +522,7 @@ importers: version: 3.0.1 tsup: specifier: 7.2.0 - version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.30)(ts-node@10.9.1)(typescript@5.2.2) + version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 @@ -504,6 +547,9 @@ importers: '@squide/fakes': specifier: workspace:* version: link:../../packages/fakes + '@squide/msw': + specifier: workspace:* + version: link:../../packages/msw '@squide/react-router': specifier: workspace:* version: link:../../packages/react-router @@ -526,6 +572,9 @@ importers: '@swc/helpers': specifier: 0.5.2 version: 0.5.2 + '@tanstack/react-query': + specifier: rc + version: 5.0.0-rc.4(react-dom@18.2.0)(react@18.2.0) '@types/react': specifier: 18.2.22 version: 18.2.22 @@ -549,7 +598,7 @@ importers: version: 3.0.2(typescript@5.2.2) '@workleap/webpack-configs': specifier: 1.0.8 - version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(postcss@8.4.30)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) + version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) browserslist: specifier: 4.21.10 version: 4.21.10 @@ -559,6 +608,9 @@ importers: http-server: specifier: 14.1.1 version: 14.1.1 + msw: + specifier: 1.3.2 + version: 1.3.2(typescript@5.2.2) nodemon: specifier: 3.0.1 version: 3.0.1 @@ -612,7 +664,7 @@ importers: version: 6.16.0(react-dom@18.2.0)(react@18.2.0) tsup: specifier: 7.2.0 - version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.30)(ts-node@10.9.1)(typescript@5.2.2) + version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 @@ -657,7 +709,7 @@ importers: version: 6.16.0(react-dom@18.2.0)(react@18.2.0) tsup: specifier: 7.2.0 - version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.30)(ts-node@10.9.1)(typescript@5.2.2) + version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 @@ -690,21 +742,21 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/core@7.22.20: - resolution: {integrity: sha512-Y6jd1ahLubuYweD/zJH+vvOY141v4f9igNQAQ+MBgq9JlHS2iTsZKn1aMsb3vGccZsXI16VzTBw52Xx0DWmtnA==} + /@babel/core@7.23.0: + resolution: {integrity: sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==} engines: {node: '>=6.9.0'} dependencies: '@ampproject/remapping': 2.2.1 '@babel/code-frame': 7.22.13 - '@babel/generator': 7.22.15 + '@babel/generator': 7.23.0 '@babel/helper-compilation-targets': 7.22.15 - '@babel/helper-module-transforms': 7.22.20(@babel/core@7.22.20) - '@babel/helpers': 7.22.15 - '@babel/parser': 7.22.16 + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) + '@babel/helpers': 7.23.1 + '@babel/parser': 7.23.0 '@babel/template': 7.22.15 - '@babel/traverse': 7.22.20 - '@babel/types': 7.22.19 - convert-source-map: 1.9.0 + '@babel/traverse': 7.23.0 + '@babel/types': 7.23.0 + convert-source-map: 2.0.0 debug: 4.3.4(supports-color@9.4.0) gensync: 1.0.0-beta.2 json5: 2.2.3 @@ -713,11 +765,11 @@ packages: - supports-color dev: true - /@babel/generator@7.22.15: - resolution: {integrity: sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==} + /@babel/generator@7.23.0: + resolution: {integrity: sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 '@jridgewell/gen-mapping': 0.3.3 '@jridgewell/trace-mapping': 0.3.19 jsesc: 2.5.2 @@ -727,14 +779,14 @@ packages: resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 dev: true /@babel/helper-builder-binary-assignment-operator-visitor@7.22.15: resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 dev: true /@babel/helper-compilation-targets@7.22.15: @@ -748,42 +800,42 @@ packages: semver: 6.3.1 dev: true - /@babel/helper-create-class-features-plugin@7.22.15(@babel/core@7.22.20): + /@babel/helper-create-class-features-plugin@7.22.15(@babel/core@7.23.0): resolution: {integrity: sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.22.5 - '@babel/helper-member-expression-to-functions': 7.22.15 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.22.20) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.0) '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 semver: 6.3.1 dev: true - /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.22.20): + /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.23.0): resolution: {integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-annotate-as-pure': 7.22.5 regexpu-core: 5.3.2 semver: 6.3.1 dev: true - /@babel/helper-define-polyfill-provider@0.4.2(@babel/core@7.22.20): + /@babel/helper-define-polyfill-provider@0.4.2(@babel/core@7.23.0): resolution: {integrity: sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 debug: 4.3.4(supports-color@9.4.0) @@ -798,42 +850,42 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/helper-function-name@7.22.5: - resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==} + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.22.15 - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 dev: true /@babel/helper-hoist-variables@7.22.5: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 dev: true - /@babel/helper-member-expression-to-functions@7.22.15: - resolution: {integrity: sha512-qLNsZbgrNh0fDQBCPocSL8guki1hcPvltGDv/NxvUoABwFq7GkKSu1nRXeJkVZc+wJvne2E0RKQz+2SQrz6eAA==} + /@babel/helper-member-expression-to-functions@7.23.0: + resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 dev: true /@babel/helper-module-imports@7.22.15: resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 dev: true - /@babel/helper-module-transforms@7.22.20(@babel/core@7.22.20): - resolution: {integrity: sha512-dLT7JVWIUUxKOs1UnJUBR3S70YK+pKX6AbJgB2vMIvEkZkrfJDbYDJesnPshtKV4LhDOR3Oc5YULeDizRek+5A==} + /@babel/helper-module-transforms@7.23.0(@babel/core@7.23.0): + resolution: {integrity: sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-module-imports': 7.22.15 '@babel/helper-simple-access': 7.22.5 @@ -845,7 +897,7 @@ packages: resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 dev: true /@babel/helper-plugin-utils@7.22.5: @@ -853,27 +905,27 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.22.20): + /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.23.0): resolution: {integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-wrap-function': 7.22.20 dev: true - /@babel/helper-replace-supers@7.22.20(@babel/core@7.22.20): + /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.0): resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-member-expression-to-functions': 7.22.15 + '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 dev: true @@ -881,21 +933,21 @@ packages: resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 dev: true /@babel/helper-skip-transparent-expression-wrappers@7.22.5: resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 dev: true /@babel/helper-split-export-declaration@7.22.6: resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 dev: true /@babel/helper-string-parser@7.22.5: @@ -917,18 +969,18 @@ packages: resolution: {integrity: sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-function-name': 7.22.5 + '@babel/helper-function-name': 7.23.0 '@babel/template': 7.22.15 - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 dev: true - /@babel/helpers@7.22.15: - resolution: {integrity: sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw==} + /@babel/helpers@7.23.1: + resolution: {integrity: sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==} engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.22.15 - '@babel/traverse': 7.22.20 - '@babel/types': 7.22.19 + '@babel/traverse': 7.23.0 + '@babel/types': 7.23.0 transitivePeerDependencies: - supports-color dev: true @@ -942,980 +994,980 @@ packages: js-tokens: 4.0.0 dev: true - /@babel/parser@7.22.16: - resolution: {integrity: sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==} + /@babel/parser@7.23.0: + resolution: {integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==} engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 dev: true - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.22.15(@babel/core@7.22.20): + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.22.15(@babel/core@7.23.0): resolution: {integrity: sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.22.15(@babel/core@7.22.20): + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.22.15(@babel/core@7.23.0): resolution: {integrity: sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-transform-optional-chaining': 7.22.15(@babel/core@7.22.20) + '@babel/plugin-transform-optional-chaining': 7.23.0(@babel/core@7.23.0) dev: true - /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.22.20): + /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.0): resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 dev: true - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.22.20): + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.0): resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.22.20): + /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.0): resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.22.20): + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.0): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.22.20): + /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.23.0): resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.22.20): + /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.23.0): resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.22.20): + /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.23.0): resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-import-assertions@7.22.5(@babel/core@7.22.20): + /@babel/plugin-syntax-import-assertions@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-import-attributes@7.22.5(@babel/core@7.22.20): + /@babel/plugin-syntax-import-attributes@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.22.20): + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.0): resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.22.20): + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.0): resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.22.20): + /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.22.20): + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.0): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.22.20): + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.0): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.22.20): + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.0): resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.22.20): + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.0): resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.22.20): + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.0): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.22.20): + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.0): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.22.20): + /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.23.0): resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.22.20): + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.0): resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.22.20): + /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.22.20): + /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.23.0): resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.20 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-arrow-functions@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-arrow-functions@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-async-generator-functions@7.22.15(@babel/core@7.22.20): + /@babel/plugin-transform-async-generator-functions@7.22.15(@babel/core@7.23.0): resolution: {integrity: sha512-jBm1Es25Y+tVoTi5rfd5t1KLmL8ogLKpXszboWOTTtGFGz2RKnQe2yn7HbZ+kb/B8N0FVSGQo874NSlOU1T4+w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.22.20) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.22.20) + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.0) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.0) dev: true - /@babel/plugin-transform-async-to-generator@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-async-to-generator@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-module-imports': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.22.20) + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.0) dev: true - /@babel/plugin-transform-block-scoped-functions@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-block-scoped-functions@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-block-scoping@7.22.15(@babel/core@7.22.20): - resolution: {integrity: sha512-G1czpdJBZCtngoK1sJgloLiOHUnkb/bLZwqVZD8kXmq0ZnVfTTWUcs9OWtp0mBtYJ+4LQY1fllqBkOIPhXmFmw==} + /@babel/plugin-transform-block-scoping@7.23.0(@babel/core@7.23.0): + resolution: {integrity: sha512-cOsrbmIOXmf+5YbL99/S49Y3j46k/T16b9ml8bm9lP6N9US5iQ2yBK7gpui1pg0V/WMcXdkfKbTb7HXq9u+v4g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-class-properties@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-class-properties@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-class-static-block@7.22.11(@babel/core@7.22.20): + /@babel/plugin-transform-class-static-block@7.22.11(@babel/core@7.23.0): resolution: {integrity: sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 dependencies: - '@babel/core': 7.22.20 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.22.20) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.0) dev: true - /@babel/plugin-transform-classes@7.22.15(@babel/core@7.22.20): + /@babel/plugin-transform-classes@7.22.15(@babel/core@7.23.0): resolution: {integrity: sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.22.5 + '@babel/helper-function-name': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.22.20) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.0) '@babel/helper-split-export-declaration': 7.22.6 globals: 11.12.0 dev: true - /@babel/plugin-transform-computed-properties@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-computed-properties@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 '@babel/template': 7.22.15 dev: true - /@babel/plugin-transform-destructuring@7.22.15(@babel/core@7.22.20): - resolution: {integrity: sha512-HzG8sFl1ZVGTme74Nw+X01XsUTqERVQ6/RLHo3XjGRzm7XD6QTtfS3NJotVgCGy8BzkDqRjRBD8dAyJn5TuvSQ==} + /@babel/plugin-transform-destructuring@7.23.0(@babel/core@7.23.0): + resolution: {integrity: sha512-vaMdgNXFkYrB+8lbgniSYWHsgqK5gjaMNcc84bMIOMRLH0L9AqYq3hwMdvnyqj1OPqea8UtjPEuS/DCenah1wg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-dotall-regex@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-dotall-regex@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-duplicate-keys@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-duplicate-keys@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-dynamic-import@7.22.11(@babel/core@7.22.20): + /@babel/plugin-transform-dynamic-import@7.22.11(@babel/core@7.23.0): resolution: {integrity: sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.22.20) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.0) dev: true - /@babel/plugin-transform-exponentiation-operator@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-exponentiation-operator@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-builder-binary-assignment-operator-visitor': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-export-namespace-from@7.22.11(@babel/core@7.22.20): + /@babel/plugin-transform-export-namespace-from@7.22.11(@babel/core@7.23.0): resolution: {integrity: sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.22.20) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.0) dev: true - /@babel/plugin-transform-for-of@7.22.15(@babel/core@7.22.20): + /@babel/plugin-transform-for-of@7.22.15(@babel/core@7.23.0): resolution: {integrity: sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-function-name@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-function-name@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-compilation-targets': 7.22.15 - '@babel/helper-function-name': 7.22.5 + '@babel/helper-function-name': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-json-strings@7.22.11(@babel/core@7.22.20): + /@babel/plugin-transform-json-strings@7.22.11(@babel/core@7.23.0): resolution: {integrity: sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.22.20) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.0) dev: true - /@babel/plugin-transform-literals@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-literals@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-logical-assignment-operators@7.22.11(@babel/core@7.22.20): + /@babel/plugin-transform-logical-assignment-operators@7.22.11(@babel/core@7.23.0): resolution: {integrity: sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.22.20) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.0) dev: true - /@babel/plugin-transform-member-expression-literals@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-member-expression-literals@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-modules-amd@7.22.5(@babel/core@7.22.20): - resolution: {integrity: sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==} + /@babel/plugin-transform-modules-amd@7.23.0(@babel/core@7.23.0): + resolution: {integrity: sha512-xWT5gefv2HGSm4QHtgc1sYPbseOyf+FFDo2JbpE25GWl5BqTGO9IMwTYJRoIdjsF85GE+VegHxSCUt5EvoYTAw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 - '@babel/helper-module-transforms': 7.22.20(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-modules-commonjs@7.22.15(@babel/core@7.22.20): - resolution: {integrity: sha512-jWL4eh90w0HQOTKP2MoXXUpVxilxsB2Vl4ji69rSjS3EcZ/v4sBmn+A3NpepuJzBhOaEBbR7udonlHHn5DWidg==} + /@babel/plugin-transform-modules-commonjs@7.23.0(@babel/core@7.23.0): + resolution: {integrity: sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 - '@babel/helper-module-transforms': 7.22.20(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-simple-access': 7.22.5 dev: true - /@babel/plugin-transform-modules-systemjs@7.22.11(@babel/core@7.22.20): - resolution: {integrity: sha512-rIqHmHoMEOhI3VkVf5jQ15l539KrwhzqcBO6wdCNWPWc/JWt9ILNYNUssbRpeq0qWns8svuw8LnMNCvWBIJ8wA==} + /@babel/plugin-transform-modules-systemjs@7.23.0(@babel/core@7.23.0): + resolution: {integrity: sha512-qBej6ctXZD2f+DhlOC9yO47yEYgUh5CZNz/aBoH4j/3NOlRfJXJbY7xDQCqQVf9KbrqGzIWER1f23doHGrIHFg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-module-transforms': 7.22.20(@babel/core@7.22.20) + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-identifier': 7.22.20 dev: true - /@babel/plugin-transform-modules-umd@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-modules-umd@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 - '@babel/helper-module-transforms': 7.22.20(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.20 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-new-target@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-new-target@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-nullish-coalescing-operator@7.22.11(@babel/core@7.22.20): + /@babel/plugin-transform-nullish-coalescing-operator@7.22.11(@babel/core@7.23.0): resolution: {integrity: sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.22.20) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.0) dev: true - /@babel/plugin-transform-numeric-separator@7.22.11(@babel/core@7.22.20): + /@babel/plugin-transform-numeric-separator@7.22.11(@babel/core@7.23.0): resolution: {integrity: sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.22.20) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.0) dev: true - /@babel/plugin-transform-object-rest-spread@7.22.15(@babel/core@7.22.20): + /@babel/plugin-transform-object-rest-spread@7.22.15(@babel/core@7.23.0): resolution: {integrity: sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/compat-data': 7.22.20 - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.22.20) - '@babel/plugin-transform-parameters': 7.22.15(@babel/core@7.22.20) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-transform-parameters': 7.22.15(@babel/core@7.23.0) dev: true - /@babel/plugin-transform-object-super@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-object-super@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.22.20) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.0) dev: true - /@babel/plugin-transform-optional-catch-binding@7.22.11(@babel/core@7.22.20): + /@babel/plugin-transform-optional-catch-binding@7.22.11(@babel/core@7.23.0): resolution: {integrity: sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.22.20) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.0) dev: true - /@babel/plugin-transform-optional-chaining@7.22.15(@babel/core@7.22.20): - resolution: {integrity: sha512-ngQ2tBhq5vvSJw2Q2Z9i7ealNkpDMU0rGWnHPKqRZO0tzZ5tlaoz4hDvhXioOoaE0X2vfNss1djwg0DXlfu30A==} + /@babel/plugin-transform-optional-chaining@7.23.0(@babel/core@7.23.0): + resolution: {integrity: sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.20) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.0) dev: true - /@babel/plugin-transform-parameters@7.22.15(@babel/core@7.22.20): + /@babel/plugin-transform-parameters@7.22.15(@babel/core@7.23.0): resolution: {integrity: sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-private-methods@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-private-methods@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-private-property-in-object@7.22.11(@babel/core@7.22.20): + /@babel/plugin-transform-private-property-in-object@7.22.11(@babel/core@7.23.0): resolution: {integrity: sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.22.20) + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.22.20) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.0) dev: true - /@babel/plugin-transform-property-literals@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-property-literals@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-react-constant-elements@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-react-constant-elements@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-BF5SXoO+nX3h5OhlN78XbbDrBOffv+AxPP2ENaJOVqjWCgBDeOY3WcaUcddutGSfoap+5NEQ/q/4I3WZIvgkXA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-react-display-name@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-react-display-name@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 - '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.23.0) dev: true - /@babel/plugin-transform-react-jsx@7.22.15(@babel/core@7.22.20): + /@babel/plugin-transform-react-jsx@7.22.15(@babel/core@7.23.0): resolution: {integrity: sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-module-imports': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.20) - '@babel/types': 7.22.19 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.0) + '@babel/types': 7.23.0 dev: true - /@babel/plugin-transform-react-pure-annotations@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-react-pure-annotations@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-gP4k85wx09q+brArVinTXhWiyzLl9UpmGva0+mWyKxk6JZequ05x3eUcIUE+FyttPKJFRRVtAvQaJ6YF9h1ZpA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-regenerator@7.22.10(@babel/core@7.22.20): + /@babel/plugin-transform-regenerator@7.22.10(@babel/core@7.23.0): resolution: {integrity: sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 regenerator-transform: 0.15.2 dev: true - /@babel/plugin-transform-reserved-words@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-reserved-words@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-shorthand-properties@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-shorthand-properties@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-spread@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-spread@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 dev: true - /@babel/plugin-transform-sticky-regex@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-sticky-regex@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-template-literals@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-template-literals@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-typeof-symbol@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-typeof-symbol@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-typescript@7.22.15(@babel/core@7.22.20): + /@babel/plugin-transform-typescript@7.22.15(@babel/core@7.23.0): resolution: {integrity: sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.22.20) + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.20) + '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.23.0) dev: true - /@babel/plugin-transform-unicode-escapes@7.22.10(@babel/core@7.22.20): + /@babel/plugin-transform-unicode-escapes@7.22.10(@babel/core@7.23.0): resolution: {integrity: sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-unicode-property-regex@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-unicode-property-regex@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-unicode-regex@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-unicode-regex@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-unicode-sets-regex@7.22.5(@babel/core@7.22.20): + /@babel/plugin-transform-unicode-sets-regex@7.22.5(@babel/core@7.23.0): resolution: {integrity: sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.20 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.0) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/preset-env@7.22.20(@babel/core@7.22.20): + /@babel/preset-env@7.22.20(@babel/core@7.23.0): resolution: {integrity: sha512-11MY04gGC4kSzlPHRfvVkNAZhUxOvm7DCJ37hPDnUENwe06npjIRAfInEMTGSb4LZK5ZgDFkv5hw0lGebHeTyg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/compat-data': 7.22.20 - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-option': 7.22.15 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.22.15(@babel/core@7.22.20) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.22.15(@babel/core@7.22.20) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.22.20) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.22.20) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.22.20) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.22.20) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.22.20) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.22.20) - '@babel/plugin-syntax-import-assertions': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-syntax-import-attributes': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.22.20) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.22.20) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.22.20) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.22.20) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.22.20) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.22.20) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.22.20) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.20) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.22.20) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.22.20) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.22.20) - '@babel/plugin-transform-arrow-functions': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-async-generator-functions': 7.22.15(@babel/core@7.22.20) - '@babel/plugin-transform-async-to-generator': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-block-scoped-functions': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-block-scoping': 7.22.15(@babel/core@7.22.20) - '@babel/plugin-transform-class-properties': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-class-static-block': 7.22.11(@babel/core@7.22.20) - '@babel/plugin-transform-classes': 7.22.15(@babel/core@7.22.20) - '@babel/plugin-transform-computed-properties': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-destructuring': 7.22.15(@babel/core@7.22.20) - '@babel/plugin-transform-dotall-regex': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-duplicate-keys': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-dynamic-import': 7.22.11(@babel/core@7.22.20) - '@babel/plugin-transform-exponentiation-operator': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-export-namespace-from': 7.22.11(@babel/core@7.22.20) - '@babel/plugin-transform-for-of': 7.22.15(@babel/core@7.22.20) - '@babel/plugin-transform-function-name': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-json-strings': 7.22.11(@babel/core@7.22.20) - '@babel/plugin-transform-literals': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-logical-assignment-operators': 7.22.11(@babel/core@7.22.20) - '@babel/plugin-transform-member-expression-literals': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-modules-amd': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-modules-commonjs': 7.22.15(@babel/core@7.22.20) - '@babel/plugin-transform-modules-systemjs': 7.22.11(@babel/core@7.22.20) - '@babel/plugin-transform-modules-umd': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-new-target': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-nullish-coalescing-operator': 7.22.11(@babel/core@7.22.20) - '@babel/plugin-transform-numeric-separator': 7.22.11(@babel/core@7.22.20) - '@babel/plugin-transform-object-rest-spread': 7.22.15(@babel/core@7.22.20) - '@babel/plugin-transform-object-super': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-optional-catch-binding': 7.22.11(@babel/core@7.22.20) - '@babel/plugin-transform-optional-chaining': 7.22.15(@babel/core@7.22.20) - '@babel/plugin-transform-parameters': 7.22.15(@babel/core@7.22.20) - '@babel/plugin-transform-private-methods': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-private-property-in-object': 7.22.11(@babel/core@7.22.20) - '@babel/plugin-transform-property-literals': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-regenerator': 7.22.10(@babel/core@7.22.20) - '@babel/plugin-transform-reserved-words': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-shorthand-properties': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-spread': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-sticky-regex': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-template-literals': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-typeof-symbol': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-unicode-escapes': 7.22.10(@babel/core@7.22.20) - '@babel/plugin-transform-unicode-property-regex': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-unicode-regex': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-unicode-sets-regex': 7.22.5(@babel/core@7.22.20) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.22.20) - '@babel/types': 7.22.19 - babel-plugin-polyfill-corejs2: 0.4.5(@babel/core@7.22.20) - babel-plugin-polyfill-corejs3: 0.8.3(@babel/core@7.22.20) - babel-plugin-polyfill-regenerator: 0.5.2(@babel/core@7.22.20) - core-js-compat: 3.32.2 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.22.15(@babel/core@7.23.0) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.22.15(@babel/core@7.23.0) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.0) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.0) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.0) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-import-assertions': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-syntax-import-attributes': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.0) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.23.0) + '@babel/plugin-transform-arrow-functions': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-async-generator-functions': 7.22.15(@babel/core@7.23.0) + '@babel/plugin-transform-async-to-generator': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-block-scoped-functions': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-block-scoping': 7.23.0(@babel/core@7.23.0) + '@babel/plugin-transform-class-properties': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-class-static-block': 7.22.11(@babel/core@7.23.0) + '@babel/plugin-transform-classes': 7.22.15(@babel/core@7.23.0) + '@babel/plugin-transform-computed-properties': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-destructuring': 7.23.0(@babel/core@7.23.0) + '@babel/plugin-transform-dotall-regex': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-duplicate-keys': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-dynamic-import': 7.22.11(@babel/core@7.23.0) + '@babel/plugin-transform-exponentiation-operator': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-export-namespace-from': 7.22.11(@babel/core@7.23.0) + '@babel/plugin-transform-for-of': 7.22.15(@babel/core@7.23.0) + '@babel/plugin-transform-function-name': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-json-strings': 7.22.11(@babel/core@7.23.0) + '@babel/plugin-transform-literals': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-logical-assignment-operators': 7.22.11(@babel/core@7.23.0) + '@babel/plugin-transform-member-expression-literals': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-modules-amd': 7.23.0(@babel/core@7.23.0) + '@babel/plugin-transform-modules-commonjs': 7.23.0(@babel/core@7.23.0) + '@babel/plugin-transform-modules-systemjs': 7.23.0(@babel/core@7.23.0) + '@babel/plugin-transform-modules-umd': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-new-target': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.22.11(@babel/core@7.23.0) + '@babel/plugin-transform-numeric-separator': 7.22.11(@babel/core@7.23.0) + '@babel/plugin-transform-object-rest-spread': 7.22.15(@babel/core@7.23.0) + '@babel/plugin-transform-object-super': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-optional-catch-binding': 7.22.11(@babel/core@7.23.0) + '@babel/plugin-transform-optional-chaining': 7.23.0(@babel/core@7.23.0) + '@babel/plugin-transform-parameters': 7.22.15(@babel/core@7.23.0) + '@babel/plugin-transform-private-methods': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-private-property-in-object': 7.22.11(@babel/core@7.23.0) + '@babel/plugin-transform-property-literals': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-regenerator': 7.22.10(@babel/core@7.23.0) + '@babel/plugin-transform-reserved-words': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-shorthand-properties': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-spread': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-sticky-regex': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-template-literals': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-typeof-symbol': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-unicode-escapes': 7.22.10(@babel/core@7.23.0) + '@babel/plugin-transform-unicode-property-regex': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-unicode-regex': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-unicode-sets-regex': 7.22.5(@babel/core@7.23.0) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.23.0) + '@babel/types': 7.23.0 + babel-plugin-polyfill-corejs2: 0.4.5(@babel/core@7.23.0) + babel-plugin-polyfill-corejs3: 0.8.4(@babel/core@7.23.0) + babel-plugin-polyfill-regenerator: 0.5.2(@babel/core@7.23.0) + core-js-compat: 3.33.0 semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.22.20): + /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.23.0): resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 esutils: 2.0.3 dev: true - /@babel/preset-react@7.22.15(@babel/core@7.22.20): + /@babel/preset-react@7.22.15(@babel/core@7.23.0): resolution: {integrity: sha512-Csy1IJ2uEh/PecCBXXoZGAZBeCATTuePzCSB7dLYWS0vOEj6CNpjxIhW4duWwZodBNueH7QO14WbGn8YyeuN9w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-option': 7.22.15 - '@babel/plugin-transform-react-display-name': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.22.20) - '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-react-pure-annotations': 7.22.5(@babel/core@7.22.20) + '@babel/plugin-transform-react-display-name': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.23.0) + '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-react-pure-annotations': 7.22.5(@babel/core@7.23.0) dev: true - /@babel/preset-typescript@7.22.15(@babel/core@7.22.20): - resolution: {integrity: sha512-HblhNmh6yM+cU4VwbBRpxFhxsTdfS1zsvH9W+gEjD0ARV9+8B4sNfpI6GuhePti84nuvhiwKS539jKPFHskA9A==} + /@babel/preset-typescript@7.23.0(@babel/core@7.23.0): + resolution: {integrity: sha512-6P6VVa/NM/VlAYj5s2Aq/gdVg8FSENCg3wlZ6Qau9AcPaoF5LbN1nyGlR9DTRIw9PpxI94e+ReydsJHcjwAweg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-option': 7.22.15 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-transform-modules-commonjs': 7.22.15(@babel/core@7.22.20) - '@babel/plugin-transform-typescript': 7.22.15(@babel/core@7.22.20) + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-modules-commonjs': 7.23.0(@babel/core@7.23.0) + '@babel/plugin-transform-typescript': 7.22.15(@babel/core@7.23.0) dev: true /@babel/regjsgen@0.8.0: resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==} dev: true - /@babel/runtime@7.22.15: - resolution: {integrity: sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==} + /@babel/runtime@7.23.1: + resolution: {integrity: sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==} engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.0 @@ -1926,30 +1978,30 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.22.13 - '@babel/parser': 7.22.16 - '@babel/types': 7.22.19 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 dev: true - /@babel/traverse@7.22.20: - resolution: {integrity: sha512-eU260mPZbU7mZ0N+X10pxXhQFMGTeLb9eFS0mxehS8HZp9o1uSnFeWQuG1UPrlxgA7QoUzFhOnilHDp0AXCyHw==} + /@babel/traverse@7.23.0: + resolution: {integrity: sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==} engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.22.13 - '@babel/generator': 7.22.15 + '@babel/generator': 7.23.0 '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.22.5 + '@babel/helper-function-name': 7.23.0 '@babel/helper-hoist-variables': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.22.16 - '@babel/types': 7.22.19 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 debug: 4.3.4(supports-color@9.4.0) globals: 11.12.0 transitivePeerDependencies: - supports-color dev: true - /@babel/types@7.22.19: - resolution: {integrity: sha512-P7LAw/LbojPzkgp5oznjE6tQEIWbp4PkkfrZDINTro9zgBRtI324/EYsiSI7lhPbpIQ+DCeR2NNmMWANGGfZsg==} + /@babel/types@7.23.0: + resolution: {integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-string-parser': 7.22.5 @@ -2006,7 +2058,7 @@ packages: /@changesets/apply-release-plan@6.1.4: resolution: {integrity: sha512-FMpKF1fRlJyCZVYHr3CbinpZZ+6MwvOtWUuO8uo+svcATEoc1zRDcj23pAurJ2TZ/uVz1wFHH6K3NlACy0PLew==} dependencies: - '@babel/runtime': 7.22.15 + '@babel/runtime': 7.23.1 '@changesets/config': 2.3.1 '@changesets/get-version-range-type': 0.3.2 '@changesets/git': 2.0.0 @@ -2024,7 +2076,7 @@ packages: /@changesets/assemble-release-plan@5.2.4: resolution: {integrity: sha512-xJkWX+1/CUaOUWTguXEbCDTyWJFECEhmdtbkjhn5GVBGxdP/JwaHBIU9sW3FR6gD07UwZ7ovpiPclQZs+j+mvg==} dependencies: - '@babel/runtime': 7.22.15 + '@babel/runtime': 7.23.1 '@changesets/errors': 0.1.4 '@changesets/get-dependents-graph': 1.3.6 '@changesets/types': 5.2.1 @@ -2052,7 +2104,7 @@ packages: resolution: {integrity: sha512-dnWrJTmRR8bCHikJHl9b9HW3gXACCehz4OasrXpMp7sx97ECuBGGNjJhjPhdZNCvMy9mn4BWdplI323IbqsRig==} hasBin: true dependencies: - '@babel/runtime': 7.22.15 + '@babel/runtime': 7.23.1 '@changesets/apply-release-plan': 6.1.4 '@changesets/assemble-release-plan': 5.2.4 '@changesets/changelog-git': 0.1.14 @@ -2067,8 +2119,8 @@ packages: '@changesets/types': 5.2.1 '@changesets/write': 0.2.3 '@manypkg/get-packages': 1.1.3 - '@types/is-ci': 3.0.0 - '@types/semver': 7.5.2 + '@types/is-ci': 3.0.1 + '@types/semver': 7.5.3 ansi-colors: 4.1.3 chalk: 2.4.2 enquirer: 2.4.1 @@ -2084,7 +2136,7 @@ packages: semver: 7.5.4 spawndamnit: 2.0.0 term-size: 2.2.1 - tty-table: 4.2.1 + tty-table: 4.2.2 dev: true /@changesets/config@2.3.1: @@ -2127,7 +2179,7 @@ packages: /@changesets/get-release-plan@3.0.17: resolution: {integrity: sha512-6IwKTubNEgoOZwDontYc2x2cWXfr6IKxP3IhKeK+WjyD6y3M4Gl/jdQvBw+m/5zWILSOCAaGLu2ZF6Q+WiPniw==} dependencies: - '@babel/runtime': 7.22.15 + '@babel/runtime': 7.23.1 '@changesets/assemble-release-plan': 5.2.4 '@changesets/config': 2.3.1 '@changesets/pre': 1.0.14 @@ -2143,7 +2195,7 @@ packages: /@changesets/git@2.0.0: resolution: {integrity: sha512-enUVEWbiqUTxqSnmesyJGWfzd51PY4H7mH9yUw0hPVpZBJ6tQZFMU3F3mT/t9OJ/GjyiM4770i+sehAn6ymx6A==} dependencies: - '@babel/runtime': 7.22.15 + '@babel/runtime': 7.23.1 '@changesets/errors': 0.1.4 '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 @@ -2168,7 +2220,7 @@ packages: /@changesets/pre@1.0.14: resolution: {integrity: sha512-dTsHmxQWEQekHYHbg+M1mDVYFvegDh9j/kySNuDKdylwfMEevTeDouR7IfHNyVodxZXu17sXoJuf2D0vi55FHQ==} dependencies: - '@babel/runtime': 7.22.15 + '@babel/runtime': 7.23.1 '@changesets/errors': 0.1.4 '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 @@ -2178,7 +2230,7 @@ packages: /@changesets/read@0.5.9: resolution: {integrity: sha512-T8BJ6JS6j1gfO1HFq50kU3qawYxa4NTbI/ASNVVCBTsKquy2HYwM9r7ZnzkiMe8IEObAJtUVGSrePCOxAK2haQ==} dependencies: - '@babel/runtime': 7.22.15 + '@babel/runtime': 7.23.1 '@changesets/git': 2.0.0 '@changesets/logger': 0.0.5 '@changesets/parse': 0.3.16 @@ -2199,7 +2251,7 @@ packages: /@changesets/write@0.2.3: resolution: {integrity: sha512-Dbamr7AIMvslKnNYsLFafaVORx4H0pvCA2MHqgtNCySMe1blImEyAEOzDmcgKAkgz4+uwoLz7demIrX+JBr/Xw==} dependencies: - '@babel/runtime': 7.22.15 + '@babel/runtime': 7.23.1 '@changesets/types': 5.2.1 fs-extra: 7.0.1 human-id: 1.0.2 @@ -2622,8 +2674,8 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@eslint-community/regexpp@4.8.1: - resolution: {integrity: sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==} + /@eslint-community/regexpp@4.9.1: + resolution: {integrity: sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true @@ -2634,7 +2686,7 @@ packages: ajv: 6.12.6 debug: 4.3.4(supports-color@9.4.0) espree: 9.6.1 - globals: 13.21.0 + globals: 13.22.0 ignore: 5.2.4 import-fresh: 3.3.0 js-yaml: 4.1.0 @@ -2698,8 +2750,8 @@ packages: readable-stream: 4.4.2 dev: true - /@grpc/grpc-js@1.9.3: - resolution: {integrity: sha512-b8iWtdrYIeT5fdZdS4Br/6h/kuk0PW5EVBUGk1amSbrpL8DlktJD43CdcCWwRdd6+jgwHhADSbL9CsNnm6EUPA==} + /@grpc/grpc-js@1.9.5: + resolution: {integrity: sha512-iouYNlPxRAwZ2XboDT+OfRKHuaKHiqjB5VFYZ0NFrHkbEF+AV3muIUY9olQsp8uxU4VvRCMiRk9ftzFDGb61aw==} engines: {node: ^8.13.0 || >=10.10.0} dependencies: '@grpc/proto-loader': 0.7.10 @@ -2721,7 +2773,7 @@ packages: resolution: {integrity: sha512-bAg//j0Lh0SFC0LhUrrgpO4FVScOBDt+my4YXeIo9lHi1aXXn6meaB/ycecjVfjyQLaGYWKPKu2C66rTgKIzMQ==} engines: {node: '>=14'} dependencies: - '@grpc/grpc-js': 1.9.3 + '@grpc/grpc-js': 1.9.5 '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/exporter-metrics-otlp-grpc': 0.41.2(@opentelemetry/api@1.6.0) @@ -2732,7 +2784,7 @@ packages: '@opentelemetry/sdk-metrics': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/sdk-node': 0.39.1(@opentelemetry/api@1.6.0)(supports-color@9.4.0) '@opentelemetry/sdk-trace-base': 1.17.0(@opentelemetry/api@1.6.0) - axios: 1.5.0(debug@4.3.4) + axios: 1.5.1(debug@4.3.4) transitivePeerDependencies: - debug - supports-color @@ -2985,7 +3037,7 @@ packages: resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.19 babel-plugin-istanbul: 6.1.1 @@ -3009,9 +3061,9 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 + '@types/istanbul-reports': 3.0.2 '@types/node': 20.6.3 - '@types/yargs': 16.0.5 + '@types/yargs': 16.0.6 chalk: 4.1.2 dev: true @@ -3021,9 +3073,9 @@ packages: dependencies: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 + '@types/istanbul-reports': 3.0.2 '@types/node': 20.6.3 - '@types/yargs': 17.0.24 + '@types/yargs': 17.0.26 chalk: 4.1.2 dev: true @@ -3076,7 +3128,7 @@ packages: /@manypkg/find-root@1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} dependencies: - '@babel/runtime': 7.22.15 + '@babel/runtime': 7.23.1 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 @@ -3085,7 +3137,7 @@ packages: /@manypkg/get-packages@1.1.3: resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} dependencies: - '@babel/runtime': 7.22.15 + '@babel/runtime': 7.23.1 '@changesets/types': 4.1.0 '@manypkg/find-root': 1.1.0 fs-extra: 8.1.0 @@ -3111,6 +3163,30 @@ packages: - supports-color dev: true + /@mswjs/cookies@0.2.2: + resolution: {integrity: sha512-mlN83YSrcFgk7Dm1Mys40DLssI1KdJji2CMKN8eOlBqsTADYzj2+jWzsANsUTFbxDMWPD5e9bfA1RGqBpS3O1g==} + engines: {node: '>=14'} + dependencies: + '@types/set-cookie-parser': 2.4.4 + set-cookie-parser: 2.6.0 + dev: true + + /@mswjs/interceptors@0.17.10: + resolution: {integrity: sha512-N8x7eSLGcmUFNWZRxT1vsHvypzIRgQYdG0rJey/rZCy6zT/30qDt8Joj7FxzGNLSwXbeZqJOMqDurp7ra4hgbw==} + engines: {node: '>=14'} + dependencies: + '@open-draft/until': 1.0.3 + '@types/debug': 4.1.9 + '@xmldom/xmldom': 0.8.10 + debug: 4.3.4(supports-color@9.4.0) + headers-polyfill: 3.2.5 + outvariant: 1.4.0 + strict-event-emitter: 0.2.8 + web-encoding: 1.1.5 + transitivePeerDependencies: + - supports-color + dev: true + /@netlify/binary-info@1.0.0: resolution: {integrity: sha512-4wMPu9iN3/HL97QblBsBay3E1etIciR84izI3U+4iALY+JHCrI+a2jO0qbAZ/nxKoegypYEaiiqWXylm+/zfrw==} dev: true @@ -3142,7 +3218,7 @@ packages: '@netlify/config': 20.9.0 '@netlify/edge-bundler': 8.20.0 '@netlify/framework-info': 9.8.10 - '@netlify/functions-utils': 5.2.29(supports-color@9.4.0) + '@netlify/functions-utils': 5.2.30(supports-color@9.4.0) '@netlify/git-utils': 5.1.1 '@netlify/plugins-list': 6.71.0 '@netlify/run-utils': 5.1.1 @@ -3316,11 +3392,11 @@ packages: semver: 7.5.4 dev: true - /@netlify/functions-utils@5.2.29(supports-color@9.4.0): - resolution: {integrity: sha512-ZISdLE1ha9xMv7smFQ2ey37Gjw3t1W450lhc+oc3vyCzWqjgZIfTRxG8YT30iiYiXonjvs9M63TE3c0ebVf1AQ==} + /@netlify/functions-utils@5.2.30(supports-color@9.4.0): + resolution: {integrity: sha512-p2TyQxdCJjj6gXyhwTAEtkGpAsquCzjQ8Cm7LmS5Gt/Z6BqsmugxXictFHTh/BJvX3UJLxtGlyiExOw9SeWlWQ==} engines: {node: ^14.16.0 || >=16.0.0} dependencies: - '@netlify/zip-it-and-ship-it': 9.18.1(supports-color@9.4.0) + '@netlify/zip-it-and-ship-it': 9.19.0(supports-color@9.4.0) cpy: 9.0.1 path-exists: 5.0.0 transitivePeerDependencies: @@ -3469,8 +3545,8 @@ packages: engines: {node: ^14.16.0 || >=16.0.0} dev: true - /@netlify/open-api@2.21.1: - resolution: {integrity: sha512-64iTnYNZszY3Txc0V6sHRq4VUrL1P+A/jRmpA+9QxS+4KQqQFZB2O64XEWuEJABX3PKqpE0MNq2w10w8IM3pEQ==} + /@netlify/open-api@2.23.0: + resolution: {integrity: sha512-v3IAP/iiCNfmEdxkP1bAwPeWqWtbIRRYMUcCc0qyhIzhDuQCOzVBq1OOMQotWKKLs2GbgrR/a9qYTjhcY9j44g==} dev: true /@netlify/plugins-list@6.71.0: @@ -3498,7 +3574,7 @@ packages: engines: {node: ^14.18.0 || >=16.0.0} hasBin: true dependencies: - '@babel/parser': 7.22.16 + '@babel/parser': 7.23.0 '@netlify/binary-info': 1.0.0 '@netlify/serverless-functions-api': 1.7.3 '@vercel/nft': 0.23.1(supports-color@9.4.0) @@ -3510,7 +3586,49 @@ packages: execa: 6.1.0 filter-obj: 5.1.0 find-up: 6.3.0 - get-tsconfig: 4.7.0 + get-tsconfig: 4.7.2 + glob: 8.1.0 + is-builtin-module: 3.2.1 + is-path-inside: 4.0.0 + junk: 4.0.1 + locate-path: 7.2.0 + merge-options: 3.0.4 + minimatch: 9.0.3 + normalize-path: 3.0.0 + p-map: 5.5.0 + path-exists: 5.0.0 + precinct: 11.0.5(supports-color@9.4.0) + require-package-name: 2.0.1 + resolve: 2.0.0-next.4 + semver: 7.5.4 + tmp-promise: 3.0.3 + toml: 3.0.0 + unixify: 1.0.0 + urlpattern-polyfill: 8.0.2 + yargs: 17.7.2 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + + /@netlify/zip-it-and-ship-it@9.19.0(supports-color@9.4.0): + resolution: {integrity: sha512-2SAFndmls1+BSsl8pMazGehscAM+pY89kHVshQl5i4UR99HGb8/NPSbnJvG0ECZmv9EONZ3fUrzGQUku1i21Tg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + dependencies: + '@babel/parser': 7.23.0 + '@netlify/binary-info': 1.0.0 + '@netlify/serverless-functions-api': 1.7.3 + '@vercel/nft': 0.23.1(supports-color@9.4.0) + archiver: 6.0.1 + common-path-prefix: 3.0.0 + cp-file: 10.0.0 + es-module-lexer: 1.3.1 + esbuild: 0.19.2 + execa: 6.1.0 + filter-obj: 5.1.0 + find-up: 6.3.0 + get-tsconfig: 4.7.2 glob: 8.1.0 is-builtin-module: 3.2.1 is-path-inside: 4.0.0 @@ -3575,7 +3693,7 @@ packages: engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: '@npmcli/name-from-folder': 2.0.0 - glob: 10.3.4 + glob: 10.3.10 minimatch: 9.0.3 read-package-json-fast: 3.0.2 dev: true @@ -3625,8 +3743,8 @@ packages: - encoding dev: true - /@octokit/openapi-types@18.0.0: - resolution: {integrity: sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw==} + /@octokit/openapi-types@18.1.1: + resolution: {integrity: sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw==} dev: true /@octokit/plugin-paginate-rest@6.1.2(@octokit/core@4.2.4): @@ -3700,13 +3818,17 @@ packages: /@octokit/types@10.0.0: resolution: {integrity: sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==} dependencies: - '@octokit/openapi-types': 18.0.0 + '@octokit/openapi-types': 18.1.1 dev: true /@octokit/types@9.3.2: resolution: {integrity: sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==} dependencies: - '@octokit/openapi-types': 18.0.0 + '@octokit/openapi-types': 18.1.1 + dev: true + + /@open-draft/until@1.0.3: + resolution: {integrity: sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==} dev: true /@opentelemetry/api-logs@0.39.1: @@ -3786,7 +3908,7 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: - '@grpc/grpc-js': 1.9.3 + '@grpc/grpc-js': 1.9.5 '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.15.2(@opentelemetry/api@1.6.0) '@opentelemetry/exporter-metrics-otlp-http': 0.41.2(@opentelemetry/api@1.6.0) @@ -3846,7 +3968,7 @@ packages: peerDependencies: '@opentelemetry/api': ^1.0.0 dependencies: - '@grpc/grpc-js': 1.9.3 + '@grpc/grpc-js': 1.9.5 '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.13.0(@opentelemetry/api@1.6.0) '@opentelemetry/otlp-grpc-exporter-base': 0.39.1(@opentelemetry/api@1.6.0) @@ -3861,7 +3983,7 @@ packages: peerDependencies: '@opentelemetry/api': ^1.0.0 dependencies: - '@grpc/grpc-js': 1.9.3 + '@grpc/grpc-js': 1.9.5 '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.15.2(@opentelemetry/api@1.6.0) '@opentelemetry/otlp-grpc-exporter-base': 0.41.2(@opentelemetry/api@1.6.0) @@ -3967,7 +4089,7 @@ packages: peerDependencies: '@opentelemetry/api': ^1.0.0 dependencies: - '@grpc/grpc-js': 1.9.3 + '@grpc/grpc-js': 1.9.5 '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.13.0(@opentelemetry/api@1.6.0) '@opentelemetry/otlp-exporter-base': 0.39.1(@opentelemetry/api@1.6.0) @@ -3980,7 +4102,7 @@ packages: peerDependencies: '@opentelemetry/api': ^1.0.0 dependencies: - '@grpc/grpc-js': 1.9.3 + '@grpc/grpc-js': 1.9.5 '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.15.2(@opentelemetry/api@1.6.0) '@opentelemetry/otlp-exporter-base': 0.41.2(@opentelemetry/api@1.6.0) @@ -4293,7 +4415,7 @@ packages: '@types/webpack': 5.28.2(@swc/core@1.3.86)(webpack-cli@5.1.4) ansi-html-community: 0.0.8 common-path-prefix: 3.0.0 - core-js-pure: 3.32.2 + core-js-pure: 3.33.0 error-stack-parser: 2.1.4 find-up: 5.0.0 html-entities: 2.4.0 @@ -4333,7 +4455,7 @@ packages: dependencies: ansi-html-community: 0.0.8 common-path-prefix: 3.0.0 - core-js-pure: 3.32.2 + core-js-pure: 3.33.0 error-stack-parser: 2.1.4 find-up: 5.0.0 html-entities: 2.4.0 @@ -4480,101 +4602,101 @@ packages: lodash: 4.17.21 dev: true - /@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.22.20): + /@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.23.0): resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 dev: true - /@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.22.20): + /@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.23.0): resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 dev: true - /@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.22.20): + /@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.23.0): resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 dev: true - /@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.22.20): + /@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.23.0): resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 dev: true - /@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.22.20): + /@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.23.0): resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 dev: true - /@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.22.20): + /@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.23.0): resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 dev: true - /@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.22.20): + /@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.23.0): resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 dev: true - /@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.22.20): + /@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.23.0): resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==} engines: {node: '>=12'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 dev: true - /@svgr/babel-preset@8.1.0(@babel/core@7.22.20): + /@svgr/babel-preset@8.1.0(@babel/core@7.23.0): resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.20 - '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.22.20) - '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.22.20) - '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.22.20) - '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.22.20) - '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.22.20) - '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.22.20) - '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.22.20) - '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.23.0) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.23.0) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.23.0) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.23.0) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.23.0) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.23.0) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.23.0) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.23.0) dev: true /@svgr/core@8.1.0(typescript@5.2.2): resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==} engines: {node: '>=14'} dependencies: - '@babel/core': 7.22.20 - '@svgr/babel-preset': 8.1.0(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@svgr/babel-preset': 8.1.0(@babel/core@7.23.0) camelcase: 6.3.0 cosmiconfig: 8.3.6(typescript@5.2.2) snake-case: 3.0.4 @@ -4587,7 +4709,7 @@ packages: resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==} engines: {node: '>=14'} dependencies: - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 entities: 4.5.0 dev: true @@ -4597,8 +4719,8 @@ packages: peerDependencies: '@svgr/core': '*' dependencies: - '@babel/core': 7.22.20 - '@svgr/babel-preset': 8.1.0(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@svgr/babel-preset': 8.1.0(@babel/core@7.23.0) '@svgr/core': 8.1.0(typescript@5.2.2) '@svgr/hast-util-to-babel-ast': 8.0.0 svg-parser: 2.0.4 @@ -4624,11 +4746,11 @@ packages: resolution: {integrity: sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==} engines: {node: '>=14'} dependencies: - '@babel/core': 7.22.20 - '@babel/plugin-transform-react-constant-elements': 7.22.5(@babel/core@7.22.20) - '@babel/preset-env': 7.22.20(@babel/core@7.22.20) - '@babel/preset-react': 7.22.15(@babel/core@7.22.20) - '@babel/preset-typescript': 7.22.15(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@babel/plugin-transform-react-constant-elements': 7.22.5(@babel/core@7.23.0) + '@babel/preset-env': 7.22.20(@babel/core@7.23.0) + '@babel/preset-react': 7.22.15(@babel/core@7.23.0) + '@babel/preset-typescript': 7.23.0(@babel/core@7.23.0) '@svgr/core': 8.1.0(typescript@5.2.2) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0) '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0)(typescript@5.2.2) @@ -4767,13 +4889,33 @@ packages: defer-to-connect: 2.0.1 dev: true + /@tanstack/query-core@5.0.0-rc.4: + resolution: {integrity: sha512-d5QHSpR+kmutkJfXmyaiNvchVQctuY3d3vWBbAS4M3JTEQuOt+Gg2v7oghnDG05Ks7M0pq7ofNFuAJZ3s7p1yg==} + + /@tanstack/react-query@5.0.0-rc.4(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-nN88B6SFvLneav7ZWegjHRhjv46SSAgGnjE/m1aeG9IcfjBGLTP32YkimoDw71F3tRFYzJyrtQ1y6FwB+UymGA==} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + '@tanstack/query-core': 5.0.0-rc.4 + client-only: 0.0.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + /@testing-library/dom@9.3.3: resolution: {integrity: sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw==} engines: {node: '>=14'} dependencies: '@babel/code-frame': 7.22.13 - '@babel/runtime': 7.22.15 - '@types/aria-query': 5.0.1 + '@babel/runtime': 7.23.1 + '@types/aria-query': 5.0.2 aria-query: 5.1.3 chalk: 4.1.2 dom-accessibility-api: 0.5.16 @@ -4788,7 +4930,7 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 dependencies: - '@babel/runtime': 7.22.15 + '@babel/runtime': 7.23.1 '@testing-library/dom': 9.3.3 '@types/react-dom': 18.2.7 react: 18.2.0 @@ -4828,18 +4970,18 @@ packages: /@types/acorn@4.0.6: resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==} dependencies: - '@types/estree': 1.0.1 + '@types/estree': 1.0.2 dev: true - /@types/aria-query@5.0.1: - resolution: {integrity: sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==} + /@types/aria-query@5.0.2: + resolution: {integrity: sha512-PHKZuMN+K5qgKIWhBodXzQslTo5P+K/6LqeKXS6O/4liIDdZqaX5RXrCK++LAw+y/nptN48YmUMFiQHRSWYwtQ==} dev: true /@types/babel__core@7.20.2: resolution: {integrity: sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==} dependencies: - '@babel/parser': 7.22.16 - '@babel/types': 7.22.19 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 '@types/babel__generator': 7.6.5 '@types/babel__template': 7.4.2 '@types/babel__traverse': 7.20.2 @@ -4848,20 +4990,20 @@ packages: /@types/babel__generator@7.6.5: resolution: {integrity: sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==} dependencies: - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 dev: true /@types/babel__template@7.4.2: resolution: {integrity: sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==} dependencies: - '@babel/parser': 7.22.16 - '@babel/types': 7.22.19 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 dev: true /@types/babel__traverse@7.20.2: resolution: {integrity: sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==} dependencies: - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 dev: true /@types/body-parser@1.19.3: @@ -4878,13 +5020,13 @@ packages: /@types/concat-stream@2.0.0: resolution: {integrity: sha512-t3YCerNM7NTVjLuICZo5gYAXYoDvpuuTceCcFQWcDQz26kxUR5uIWolxbIR5jRNIXpMqhOpW/b8imCR1LEmuJw==} dependencies: - '@types/node': 18.17.18 + '@types/node': 20.6.3 dev: true /@types/connect-history-api-fallback@1.5.1: resolution: {integrity: sha512-iaQslNbARe8fctL5Lk+DsmgWOM83lM+7FzP0eQUJs1jd3kBE8NWqBTIT2S8SqQOJjxvt2eyIjpOuYeRXq2AdMw==} dependencies: - '@types/express-serve-static-core': 4.17.36 + '@types/express-serve-static-core': 4.17.37 '@types/node': 20.6.3 /@types/connect@3.4.36: @@ -4892,51 +5034,55 @@ packages: dependencies: '@types/node': 20.6.3 - /@types/debug@4.1.8: - resolution: {integrity: sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==} + /@types/cookie@0.4.1: + resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} + dev: true + + /@types/debug@4.1.9: + resolution: {integrity: sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow==} dependencies: - '@types/ms': 0.7.31 + '@types/ms': 0.7.32 dev: true - /@types/eslint-scope@3.7.4: - resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==} + /@types/eslint-scope@3.7.5: + resolution: {integrity: sha512-JNvhIEyxVW6EoMIFIvj93ZOywYFatlpu9deeH6eSx6PE3WHYvHaQtmHmQeNw7aA81bYGBPPQqdtBm6b1SsQMmA==} dependencies: - '@types/eslint': 8.44.2 - '@types/estree': 1.0.1 + '@types/eslint': 8.44.3 + '@types/estree': 1.0.2 - /@types/eslint@8.44.2: - resolution: {integrity: sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==} + /@types/eslint@8.44.3: + resolution: {integrity: sha512-iM/WfkwAhwmPff3wZuPLYiHX18HI24jU8k1ZSH7P8FHwxTjZ2P6CoX2wnF43oprR+YXJM6UUxATkNvyv/JHd+g==} dependencies: - '@types/estree': 1.0.1 + '@types/estree': 1.0.2 '@types/json-schema': 7.0.13 - /@types/estree-jsx@1.0.0: - resolution: {integrity: sha512-3qvGd0z8F2ENTGr/GG1yViqfiKmRfrXVx5sJyHGFu3z7m5g5utCQtGp/g29JnjflhtQJBv1WDQukHiT58xPcYQ==} + /@types/estree-jsx@1.0.1: + resolution: {integrity: sha512-sHyakZlAezNFxmYRo0fopDZW+XvK6ipeZkkp5EAOLjdPfZp8VjZBJ67vSRI99RSCAoqXVmXOHS4fnWoxpuGQtQ==} dependencies: - '@types/estree': 1.0.1 + '@types/estree': 1.0.2 dev: true - /@types/estree@1.0.1: - resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} + /@types/estree@1.0.2: + resolution: {integrity: sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==} - /@types/express-serve-static-core@4.17.36: - resolution: {integrity: sha512-zbivROJ0ZqLAtMzgzIUC4oNqDG9iF0lSsAqpOD9kbs5xcIM3dTiyuHvBc7R8MtWBp3AAWGaovJa+wzWPjLYW7Q==} + /@types/express-serve-static-core@4.17.37: + resolution: {integrity: sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==} dependencies: '@types/node': 20.6.3 '@types/qs': 6.9.8 - '@types/range-parser': 1.2.4 - '@types/send': 0.17.1 + '@types/range-parser': 1.2.5 + '@types/send': 0.17.2 - /@types/express@4.17.17: - resolution: {integrity: sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==} + /@types/express@4.17.18: + resolution: {integrity: sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ==} dependencies: '@types/body-parser': 1.19.3 - '@types/express-serve-static-core': 4.17.36 + '@types/express-serve-static-core': 4.17.37 '@types/qs': 6.9.8 - '@types/serve-static': 1.15.2 + '@types/serve-static': 1.15.3 - /@types/graceful-fs@4.1.6: - resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} + /@types/graceful-fs@4.1.7: + resolution: {integrity: sha512-MhzcwU8aUygZroVwL2jeYk6JisJrPl/oov/gsgGCue9mkgl9wjGbzReYQClxiUgFDnib9FuHqTndccKeZKxTRw==} dependencies: '@types/node': 20.6.3 dev: true @@ -4962,8 +5108,8 @@ packages: dependencies: '@types/node': 20.6.3 - /@types/is-ci@3.0.0: - resolution: {integrity: sha512-Q0Op0hdWbYd1iahB+IFNQcWXFq4O0Q5MwQP7uN0souuQ4rPg1vEYcnIOfr1gY+M+6rc8FGoRaBO1mOOvL29sEQ==} + /@types/is-ci@3.0.1: + resolution: {integrity: sha512-mnb1ngaGQPm6LFZaNdh3xPOoQMkrQb/KBPhPPN2p2Wk8XgeUqWj6xPnvyQ8rvcK/VFritVmQG8tvQuy7g+9/nQ==} dependencies: ci-info: 3.8.0 dev: true @@ -4976,16 +5122,16 @@ packages: resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} dev: true - /@types/istanbul-lib-report@3.0.0: - resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} + /@types/istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-gPQuzaPR5h/djlAv2apEG1HVOyj1IUs7GpfMZixU0/0KXT3pm64ylHuMUI1/Akh+sq/iikxg6Z2j+fcMDXaaTQ==} dependencies: '@types/istanbul-lib-coverage': 2.0.4 dev: true - /@types/istanbul-reports@3.0.1: - resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} + /@types/istanbul-reports@3.0.2: + resolution: {integrity: sha512-kv43F9eb3Lhj+lr/Hn6OcLCs/sSM8bt+fIaP11rCYngfV6NVjzWXJ17owQtDQTL9tQ8WSLUrGsSJ6rJz0F1w1A==} dependencies: - '@types/istanbul-lib-report': 3.0.0 + '@types/istanbul-lib-report': 3.0.1 dev: true /@types/jest@29.5.5: @@ -4995,6 +5141,10 @@ packages: pretty-format: 29.7.0 dev: true + /@types/js-levenshtein@1.1.1: + resolution: {integrity: sha512-qC4bCqYGy1y/NP7dDVr7KJarn+PbX1nSpwA7JXdu0HxT3QYjO8MJ+cntENtHFVy2dRAyBV23OZ6MxsW1AM1L8g==} + dev: true + /@types/jsdom@20.0.1: resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} dependencies: @@ -5010,50 +5160,50 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true - /@types/mdast@3.0.12: - resolution: {integrity: sha512-DT+iNIRNX884cx0/Q1ja7NyUPpZuv0KPyL5rGNxm1WC1OtHstl7n4Jb7nk+xacNShQMbczJjt8uFzznpp6kYBg==} + /@types/mdast@3.0.13: + resolution: {integrity: sha512-HjiGiWedR0DVFkeNljpa6Lv4/IZU1+30VY5d747K7lBudFc3R0Ibr6yJ9lN3BE28VnZyDfLF/VB1Ql1ZIbKrmg==} dependencies: '@types/unist': 2.0.8 dev: true - /@types/mime@1.3.2: - resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==} + /@types/mime@1.3.3: + resolution: {integrity: sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==} - /@types/mime@3.0.1: - resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==} + /@types/mime@3.0.2: + resolution: {integrity: sha512-Wj+fqpTLtTbG7c0tH47dkahefpLKEbB+xAZuLq7b4/IDHPl/n6VoXcyUQ2bypFlbSwvCr0y+bD4euTTqTJsPxQ==} - /@types/minimist@1.2.2: - resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} + /@types/minimist@1.2.3: + resolution: {integrity: sha512-ZYFzrvyWUNhaPomn80dsMNgMeXxNWZBdkuG/hWlUvXvbdUH8ZERNBGXnU87McuGcWDsyzX2aChCv/SVN348k3A==} dev: true - /@types/ms@0.7.31: - resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} + /@types/ms@0.7.32: + resolution: {integrity: sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g==} dev: true /@types/node@12.20.55: resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} dev: true - /@types/node@18.17.18: - resolution: {integrity: sha512-/4QOuy3ZpV7Ya1GTRz5CYSz3DgkKpyUptXuQ5PPce7uuyJAOR7r9FhkmxJfvcNUXyklbC63a+YvB3jxy7s9ngw==} + /@types/node@18.18.3: + resolution: {integrity: sha512-0OVfGupTl3NBFr8+iXpfZ8NR7jfFO+P1Q+IO/q0wbo02wYkP5gy36phojeYWpLQ6WAMjl+VfmqUk2YbUfp0irA==} dev: true /@types/node@20.6.3: resolution: {integrity: sha512-HksnYH4Ljr4VQgEy2lTStbCKv/P590tmPe5HqOnv9Gprffgv5WXAY+Y5Gqniu0GGqeTCUdBnzC3QSrzPkBkAMA==} - /@types/normalize-package-data@2.4.1: - resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} + /@types/normalize-package-data@2.4.2: + resolution: {integrity: sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A==} dev: true - /@types/prop-types@15.7.6: - resolution: {integrity: sha512-RK/kBbYOQQHLYj9Z95eh7S6t7gq4Ojt/NT8HTk8bWVhA5DaF+5SMnxHKkP4gPNN3wAZkKP+VjAf0ebtYzf+fxg==} + /@types/prop-types@15.7.8: + resolution: {integrity: sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ==} dev: true /@types/qs@6.9.8: resolution: {integrity: sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==} - /@types/range-parser@1.2.4: - resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} + /@types/range-parser@1.2.5: + resolution: {integrity: sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==} /@types/react-dom@18.2.7: resolution: {integrity: sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==} @@ -5070,8 +5220,8 @@ packages: /@types/react@18.2.22: resolution: {integrity: sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==} dependencies: - '@types/prop-types': 15.7.6 - '@types/scheduler': 0.16.3 + '@types/prop-types': 15.7.8 + '@types/scheduler': 0.16.4 csstype: 3.1.2 dev: true @@ -5082,34 +5232,40 @@ packages: resolution: {integrity: sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==} dev: true - /@types/scheduler@0.16.3: - resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} + /@types/scheduler@0.16.4: + resolution: {integrity: sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ==} dev: true - /@types/semver@7.5.2: - resolution: {integrity: sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==} + /@types/semver@7.5.3: + resolution: {integrity: sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==} dev: true - /@types/send@0.17.1: - resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==} + /@types/send@0.17.2: + resolution: {integrity: sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==} dependencies: - '@types/mime': 1.3.2 + '@types/mime': 1.3.3 '@types/node': 20.6.3 - /@types/serve-index@1.9.1: - resolution: {integrity: sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==} + /@types/serve-index@1.9.2: + resolution: {integrity: sha512-asaEIoc6J+DbBKXtO7p2shWUpKacZOoMBEGBgPG91P8xhO53ohzHWGCs4ScZo5pQMf5ukQzVT9fhX1WzpHihig==} dependencies: - '@types/express': 4.17.17 + '@types/express': 4.17.18 - /@types/serve-static@1.15.2: - resolution: {integrity: sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==} + /@types/serve-static@1.15.3: + resolution: {integrity: sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==} dependencies: '@types/http-errors': 2.0.2 - '@types/mime': 3.0.1 + '@types/mime': 3.0.2 '@types/node': 20.6.3 - /@types/sockjs@0.3.33: - resolution: {integrity: sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==} + /@types/set-cookie-parser@2.4.4: + resolution: {integrity: sha512-xCfTC/eL/GmvMC24b42qJpYSTdCIBwWcfskDF80ztXtnU6pKXyvuZP2EConb2K9ps0s7gMhCa0P1foy7wiItMA==} + dependencies: + '@types/node': 20.6.3 + dev: true + + /@types/sockjs@0.3.34: + resolution: {integrity: sha512-R+n7qBFnm/6jinlteC9DBL5dGiDGjWAvjo4viUanpnc/dG1y7uDoacXPIQ/PQEg1fI912SMHIa014ZjRpvDw4g==} dependencies: '@types/node': 20.6.3 @@ -5159,37 +5315,37 @@ packages: - webpack-cli dev: true - /@types/ws@8.5.5: - resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} + /@types/ws@8.5.6: + resolution: {integrity: sha512-8B5EO9jLVCy+B58PLHvLDuOD8DRVMgQzq8d55SjLCOn9kqGyqOvy27exVaTio1q1nX5zLu8/6N0n2ThSxOM6tg==} dependencies: '@types/node': 20.6.3 - /@types/yargs-parser@21.0.0: - resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} + /@types/yargs-parser@21.0.1: + resolution: {integrity: sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==} dev: true - /@types/yargs@16.0.5: - resolution: {integrity: sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==} + /@types/yargs@16.0.6: + resolution: {integrity: sha512-oTP7/Q13GSPrgcwEwdlnkoZSQ1Hg9THe644qq8PG6hhJzjZ3qj1JjEFPIwWV/IXVs5XGIVqtkNOS9kh63WIJ+A==} dependencies: - '@types/yargs-parser': 21.0.0 + '@types/yargs-parser': 21.0.1 dev: true - /@types/yargs@17.0.24: - resolution: {integrity: sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==} + /@types/yargs@17.0.26: + resolution: {integrity: sha512-Y3vDy2X6zw/ZCumcwLpdhM5L7jmyGpmBCTYMHDLqT2IKVMYRRLdv6ZakA+wxhra6Z/3bwhNbNl9bDGXaFU+6rw==} dependencies: - '@types/yargs-parser': 21.0.0 + '@types/yargs-parser': 21.0.1 dev: true - /@types/yauzl@2.10.0: - resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} + /@types/yauzl@2.10.1: + resolution: {integrity: sha512-CHzgNU3qYBnp/O4S3yv2tXPlvMTq0YWSTVg2/JYLqWZGHwwgJGAwd00poay/11asPq8wLFwHzubyInqHIFmmiw==} requiresBuild: true dependencies: '@types/node': 20.6.3 dev: true optional: true - /@typescript-eslint/eslint-plugin@6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2): - resolution: {integrity: sha512-ooaHxlmSgZTM6CHYAFRlifqh1OAr3PAQEwi7lhYhaegbnXrnh7CDcHmc3+ihhbQC7H0i4JF0psI5ehzkF6Yl6Q==} + /@typescript-eslint/eslint-plugin@6.7.4(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2): + resolution: {integrity: sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -5199,12 +5355,12 @@ packages: typescript: optional: true dependencies: - '@eslint-community/regexpp': 4.8.1 + '@eslint-community/regexpp': 4.9.1 '@typescript-eslint/parser': 6.7.2(eslint@8.49.0)(typescript@5.2.2) - '@typescript-eslint/scope-manager': 6.7.2 - '@typescript-eslint/type-utils': 6.7.2(eslint@8.49.0)(typescript@5.2.2) - '@typescript-eslint/utils': 6.7.2(eslint@8.49.0)(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.7.2 + '@typescript-eslint/scope-manager': 6.7.4 + '@typescript-eslint/type-utils': 6.7.4(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.4(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.7.4 debug: 4.3.4(supports-color@9.4.0) eslint: 8.49.0 graphemer: 1.4.0 @@ -5254,8 +5410,16 @@ packages: '@typescript-eslint/visitor-keys': 6.7.2 dev: true - /@typescript-eslint/type-utils@6.7.2(eslint@8.49.0)(typescript@5.2.2): - resolution: {integrity: sha512-36F4fOYIROYRl0qj95dYKx6kybddLtsbmPIYNK0OBeXv2j9L5nZ17j9jmfy+bIDHKQgn2EZX+cofsqi8NPATBQ==} + /@typescript-eslint/scope-manager@6.7.4: + resolution: {integrity: sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.7.4 + '@typescript-eslint/visitor-keys': 6.7.4 + dev: true + + /@typescript-eslint/type-utils@6.7.4(eslint@8.49.0)(typescript@5.2.2): + resolution: {integrity: sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -5264,8 +5428,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.7.2(typescript@5.2.2) - '@typescript-eslint/utils': 6.7.2(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.7.4(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.4(eslint@8.49.0)(typescript@5.2.2) debug: 4.3.4(supports-color@9.4.0) eslint: 8.49.0 ts-api-utils: 1.0.3(typescript@5.2.2) @@ -5284,6 +5448,11 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dev: true + /@typescript-eslint/types@6.7.4: + resolution: {integrity: sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + /@typescript-eslint/typescript-estree@5.62.0(supports-color@9.4.0)(typescript@5.2.2): resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -5326,6 +5495,27 @@ packages: - supports-color dev: true + /@typescript-eslint/typescript-estree@6.7.4(typescript@5.2.2): + resolution: {integrity: sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 6.7.4 + '@typescript-eslint/visitor-keys': 6.7.4 + debug: 4.3.4(supports-color@9.4.0) + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/utils@5.62.0(eslint@8.49.0)(typescript@5.2.2): resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -5334,7 +5524,7 @@ packages: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) '@types/json-schema': 7.0.13 - '@types/semver': 7.5.2 + '@types/semver': 7.5.3 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(supports-color@9.4.0)(typescript@5.2.2) @@ -5346,18 +5536,18 @@ packages: - typescript dev: true - /@typescript-eslint/utils@6.7.2(eslint@8.49.0)(typescript@5.2.2): - resolution: {integrity: sha512-ZCcBJug/TS6fXRTsoTkgnsvyWSiXwMNiPzBUani7hDidBdj1779qwM1FIAmpH4lvlOZNF3EScsxxuGifjpLSWQ==} + /@typescript-eslint/utils@6.7.4(eslint@8.49.0)(typescript@5.2.2): + resolution: {integrity: sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) '@types/json-schema': 7.0.13 - '@types/semver': 7.5.2 - '@typescript-eslint/scope-manager': 6.7.2 - '@typescript-eslint/types': 6.7.2 - '@typescript-eslint/typescript-estree': 6.7.2(typescript@5.2.2) + '@types/semver': 7.5.3 + '@typescript-eslint/scope-manager': 6.7.4 + '@typescript-eslint/types': 6.7.4 + '@typescript-eslint/typescript-estree': 6.7.4(typescript@5.2.2) eslint: 8.49.0 semver: 7.5.4 transitivePeerDependencies: @@ -5381,6 +5571,14 @@ packages: eslint-visitor-keys: 3.4.3 dev: true + /@typescript-eslint/visitor-keys@6.7.4: + resolution: {integrity: sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.7.4 + eslint-visitor-keys: 3.4.3 + dev: true + /@vercel/nft@0.23.1(supports-color@9.4.0): resolution: {integrity: sha512-NE0xSmGWVhgHF1OIoir71XAd0W0C1UE3nzFyhpFiMr3rVhetww7NvM1kc41trBsPG37Bh+dE5FYCTMzM/gBu0w==} engines: {node: '>=14'} @@ -5546,17 +5744,17 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/eslint-plugin': 6.7.4(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2) '@typescript-eslint/parser': 6.7.2(eslint@8.49.0)(typescript@5.2.2) eslint: 8.49.0 eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0) - eslint-plugin-jest: 27.4.0(@typescript-eslint/eslint-plugin@6.7.2)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) + eslint-plugin-jest: 27.4.2(@typescript-eslint/eslint-plugin@6.7.4)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) eslint-plugin-jsx-a11y: 6.7.1(eslint@8.49.0) eslint-plugin-mdx: 2.2.0(eslint@8.49.0) eslint-plugin-react: 7.33.2(eslint@8.49.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.49.0) - eslint-plugin-storybook: 0.6.13(eslint@8.49.0)(typescript@5.2.2) - eslint-plugin-testing-library: 6.0.1(eslint@8.49.0)(typescript@5.2.2) + eslint-plugin-storybook: 0.6.14(eslint@8.49.0)(typescript@5.2.2) + eslint-plugin-testing-library: 6.0.2(eslint@8.49.0)(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - eslint-import-resolver-typescript @@ -5590,7 +5788,7 @@ packages: tsup: '*' typescript: '*' dependencies: - tsup: 7.2.0(@swc/core@1.3.86)(postcss@8.4.30)(ts-node@10.9.1)(typescript@5.2.2) + tsup: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: 5.2.2 dev: true @@ -5602,7 +5800,7 @@ packages: typescript: 5.2.2 dev: true - /@workleap/webpack-configs@1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(esbuild@0.18.20)(postcss@8.4.30)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2): + /@workleap/webpack-configs@1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(esbuild@0.18.20)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2): resolution: {integrity: sha512-AVTvHu5qeFVbfp9eA4KftUU8gJRRx7Rcx17jO4gGtFpLf4ADSGi4fE6eDNwGbGgHlhRrZTPYjCyHLNwPID+Pfg==} peerDependencies: '@swc/core': '*' @@ -5623,8 +5821,8 @@ packages: css-loader: 6.8.1(webpack@5.88.2) html-webpack-plugin: 5.5.3(webpack@5.88.2) mini-css-extract-plugin: 2.7.6(webpack@5.88.2) - postcss: 8.4.30 - postcss-loader: 7.3.3(postcss@8.4.30)(typescript@5.2.2)(webpack@5.88.2) + postcss: 8.4.31 + postcss-loader: 7.3.3(postcss@8.4.31)(typescript@5.2.2)(webpack@5.88.2) style-loader: 3.3.3(webpack@5.88.2) swc-loader: 0.2.3(@swc/core@1.3.86)(webpack@5.88.2) terser-webpack-plugin: 5.3.9(@swc/core@1.3.86)(esbuild@0.18.20)(webpack@5.88.2) @@ -5643,7 +5841,7 @@ packages: - webpack-plugin-serve dev: true - /@workleap/webpack-configs@1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(postcss@8.4.30)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2): + /@workleap/webpack-configs@1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2): resolution: {integrity: sha512-AVTvHu5qeFVbfp9eA4KftUU8gJRRx7Rcx17jO4gGtFpLf4ADSGi4fE6eDNwGbGgHlhRrZTPYjCyHLNwPID+Pfg==} peerDependencies: '@swc/core': '*' @@ -5664,8 +5862,8 @@ packages: css-loader: 6.8.1(webpack@5.88.2) html-webpack-plugin: 5.5.3(webpack@5.88.2) mini-css-extract-plugin: 2.7.6(webpack@5.88.2) - postcss: 8.4.30 - postcss-loader: 7.3.3(postcss@8.4.30)(typescript@5.2.2)(webpack@5.88.2) + postcss: 8.4.31 + postcss-loader: 7.3.3(postcss@8.4.31)(typescript@5.2.2)(webpack@5.88.2) style-loader: 3.3.3(webpack@5.88.2) swc-loader: 0.2.3(@swc/core@1.3.86)(webpack@5.88.2) terser-webpack-plugin: 5.3.9(@swc/core@1.3.86)(webpack@5.88.2) @@ -5684,7 +5882,7 @@ packages: - webpack-plugin-serve dev: true - /@workleap/webpack-configs@1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(browserslist@4.21.10)(esbuild@0.18.20)(postcss@8.4.30)(react-refresh@0.14.0)(typescript@5.2.2)(webpack@5.88.2): + /@workleap/webpack-configs@1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(browserslist@4.21.10)(esbuild@0.18.20)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack@5.88.2): resolution: {integrity: sha512-AVTvHu5qeFVbfp9eA4KftUU8gJRRx7Rcx17jO4gGtFpLf4ADSGi4fE6eDNwGbGgHlhRrZTPYjCyHLNwPID+Pfg==} peerDependencies: '@swc/core': '*' @@ -5705,8 +5903,8 @@ packages: css-loader: 6.8.1(webpack@5.88.2) html-webpack-plugin: 5.5.3(webpack@5.88.2) mini-css-extract-plugin: 2.7.6(webpack@5.88.2) - postcss: 8.4.30 - postcss-loader: 7.3.3(postcss@8.4.30)(typescript@5.2.2)(webpack@5.88.2) + postcss: 8.4.31 + postcss-loader: 7.3.3(postcss@8.4.31)(typescript@5.2.2)(webpack@5.88.2) style-loader: 3.3.3(webpack@5.88.2) swc-loader: 0.2.3(@swc/core@1.3.86)(webpack@5.88.2) terser-webpack-plugin: 5.3.9(@swc/core@1.3.86)(esbuild@0.18.20)(webpack@5.88.2) @@ -5798,12 +5996,23 @@ packages: p-event: 5.0.1 dev: true + /@xmldom/xmldom@0.8.10: + resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} + engines: {node: '>=10.0.0'} + dev: true + /@xtuc/ieee754@1.2.0: resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} /@xtuc/long@4.2.2: resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + /@zxing/text-encoding@0.9.0: + resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==} + requiresBuild: true + dev: true + optional: true + /abab@2.0.6: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} dev: true @@ -6336,13 +6545,13 @@ packages: - supports-color dev: true - /axe-core@4.8.1: - resolution: {integrity: sha512-9l850jDDPnKq48nbad8SiEelCv4OrUWrKab/cPj0GScVg6cb6NbCCt/Ulk26QEq5jP9NnGr04Bit1BHyV6r5CQ==} + /axe-core@4.8.2: + resolution: {integrity: sha512-/dlp0fxyM3R8YW7MFzaHWXrf4zzbr0vaYb23VBFCl83R7nWNPg/yaQw2Dc8jzCMmDVLhSdzH8MjrsuIUuvX+6g==} engines: {node: '>=4'} dev: true - /axios@1.5.0(debug@4.3.4): - resolution: {integrity: sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==} + /axios@1.5.1(debug@4.3.4): + resolution: {integrity: sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==} dependencies: follow-redirects: 1.15.3(debug@4.3.4) form-data: 4.0.0 @@ -6361,17 +6570,17 @@ packages: resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} dev: true - /babel-jest@29.7.0(@babel/core@7.22.20): + /babel-jest@29.7.0(@babel/core@7.23.0): resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.8.0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@jest/transform': 29.7.0 '@types/babel__core': 7.20.2 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.22.20) + babel-preset-jest: 29.6.3(@babel/core@7.23.0) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -6397,76 +6606,76 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/template': 7.22.15 - '@babel/types': 7.22.19 + '@babel/types': 7.23.0 '@types/babel__core': 7.20.2 '@types/babel__traverse': 7.20.2 dev: true - /babel-plugin-polyfill-corejs2@0.4.5(@babel/core@7.22.20): + /babel-plugin-polyfill-corejs2@0.4.5(@babel/core@7.23.0): resolution: {integrity: sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: '@babel/compat-data': 7.22.20 - '@babel/core': 7.22.20 - '@babel/helper-define-polyfill-provider': 0.4.2(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@babel/helper-define-polyfill-provider': 0.4.2(@babel/core@7.23.0) semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-corejs3@0.8.3(@babel/core@7.22.20): - resolution: {integrity: sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA==} + /babel-plugin-polyfill-corejs3@0.8.4(@babel/core@7.23.0): + resolution: {integrity: sha512-9l//BZZsPR+5XjyJMPtZSK4jv0BsTO1zDac2GC6ygx9WLGlcsnRd1Co0B2zT5fF5Ic6BZy+9m3HNZ3QcOeDKfg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.22.20 - '@babel/helper-define-polyfill-provider': 0.4.2(@babel/core@7.22.20) - core-js-compat: 3.32.2 + '@babel/core': 7.23.0 + '@babel/helper-define-polyfill-provider': 0.4.2(@babel/core@7.23.0) + core-js-compat: 3.33.0 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-regenerator@0.5.2(@babel/core@7.22.20): + /babel-plugin-polyfill-regenerator@0.5.2(@babel/core@7.23.0): resolution: {integrity: sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.22.20 - '@babel/helper-define-polyfill-provider': 0.4.2(@babel/core@7.22.20) + '@babel/core': 7.23.0 + '@babel/helper-define-polyfill-provider': 0.4.2(@babel/core@7.23.0) transitivePeerDependencies: - supports-color dev: true - /babel-preset-current-node-syntax@1.0.1(@babel/core@7.22.20): + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.0): resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.20 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.22.20) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.22.20) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.22.20) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.22.20) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.22.20) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.22.20) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.22.20) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.22.20) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.22.20) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.22.20) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.20) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.22.20) - dev: true - - /babel-preset-jest@29.6.3(@babel/core@7.22.20): + '@babel/core': 7.23.0 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.0) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.0) + dev: true + + /babel-preset-jest@29.6.3(@babel/core@7.23.0): resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.20) + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.0) dev: true /backoff@2.5.0: @@ -6561,6 +6770,14 @@ packages: file-uri-to-path: 1.0.0 dev: true + /bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: true + /bl@5.1.0: resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} dependencies: @@ -6671,10 +6888,21 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001538 - electron-to-chromium: 1.4.525 + caniuse-lite: 1.0.30001542 + electron-to-chromium: 1.4.539 node-releases: 2.0.13 - update-browserslist-db: 1.0.11(browserslist@4.21.10) + update-browserslist-db: 1.0.13(browserslist@4.21.10) + + /browserslist@4.22.1: + resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001542 + electron-to-chromium: 1.4.539 + node-releases: 2.0.13 + update-browserslist-db: 1.0.13(browserslist@4.22.1) + dev: true /bs-logger@0.2.6: resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} @@ -6742,8 +6970,8 @@ packages: run-applescript: 5.0.0 dev: true - /bundle-require@4.0.1(esbuild@0.18.20): - resolution: {integrity: sha512-9NQkRHlNdNpDBGmLpngF3EFDcwodhMUuLz9PaWYciVcQF9SE4LFjM2DB/xV1Li5JiuDMv7ZUWuC3rGbqR0MAXQ==} + /bundle-require@4.0.2(esbuild@0.18.20): + resolution: {integrity: sha512-jwzPOChofl67PSTW2SGubV9HBQAhhR2i6nskiOThauo9dzwDUgOWQScFVaJkjEfYX+UXiD+LEx8EblQMc2wIag==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: esbuild: '>=0.17' @@ -6853,8 +7081,8 @@ packages: engines: {node: '>=14.16'} dev: true - /caniuse-lite@1.0.30001538: - resolution: {integrity: sha512-HWJnhnID+0YMtGlzcp3T9drmBJUVDchPJ08tpUGFLs9CYlwWPH2uLgpHn8fND5pCgXVtnGS3H4QR9XLMHVNkHw==} + /caniuse-lite@1.0.30001542: + resolution: {integrity: sha512-UrtAXVcj1mvPBFQ4sKd38daP8dEcXXr5sQe6QNNinaPd0iA/cxg9/l3VrSdL73jgw5sKyuQ6jNgiKO12W3SsVA==} /ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -7006,6 +7234,13 @@ packages: restore-cursor: 2.0.0 dev: true + /cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + dependencies: + restore-cursor: 3.1.0 + dev: true + /cli-cursor@4.0.0: resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -7037,6 +7272,14 @@ packages: resolution: {integrity: sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==} dev: true + /cli-width@3.0.0: + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} + dev: true + + /client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + /cliui@6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} dependencies: @@ -7326,6 +7569,11 @@ packages: /cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + /cookie@0.4.2: + resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} + engines: {node: '>= 0.6'} + dev: true + /cookie@0.5.0: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} @@ -7364,14 +7612,14 @@ packages: yargs: 16.2.0 dev: true - /core-js-compat@3.32.2: - resolution: {integrity: sha512-+GjlguTDINOijtVRUxrQOv3kfu9rl+qPNdX2LTbJ/ZyVTuxK+ksVSAGX1nHstu4hrv1En/uPTtWgq2gI5wt4AQ==} + /core-js-compat@3.33.0: + resolution: {integrity: sha512-0w4LcLXsVEuNkIqwjjf9rjCoPhK8uqA4tMRh4Ge26vfLtUutshn+aRJU21I9LCJlh2QQHfisNToLjw1XEJLTWw==} dependencies: - browserslist: 4.21.10 + browserslist: 4.22.1 dev: true - /core-js-pure@3.32.2: - resolution: {integrity: sha512-Y2rxThOuNywTjnX/PgA5vWM6CZ9QB9sz9oGeCixV8MqXZO70z/5SHzf9EeBrEBK0PN36DnEBBu9O/aGWzKuMZQ==} + /core-js-pure@3.33.0: + resolution: {integrity: sha512-FKSIDtJnds/YFIEaZ4HszRX7hkxGpNKM7FC9aJ9WLJbSd3lD4vOltFuVIBLR8asSx9frkTSqL0dw90SKQxgKrg==} requiresBuild: true dev: true @@ -7513,12 +7761,12 @@ packages: peerDependencies: webpack: ^5.0.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.30) - postcss: 8.4.30 - postcss-modules-extract-imports: 3.0.0(postcss@8.4.30) - postcss-modules-local-by-default: 4.0.3(postcss@8.4.30) - postcss-modules-scope: 3.0.0(postcss@8.4.30) - postcss-modules-values: 4.0.0(postcss@8.4.30) + icss-utils: 5.1.0(postcss@8.4.31) + postcss: 8.4.31 + postcss-modules-extract-imports: 3.0.0(postcss@8.4.31) + postcss-modules-local-by-default: 4.0.3(postcss@8.4.31) + postcss-modules-scope: 3.0.0(postcss@8.4.31) + postcss-modules-values: 4.0.0(postcss@8.4.31) postcss-value-parser: 4.2.0 semver: 7.5.4 webpack: 5.88.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4) @@ -7937,8 +8185,8 @@ packages: engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dependencies: is-url: 1.2.4 - postcss: 8.4.30 - postcss-values-parser: 6.0.2(postcss@8.4.30) + postcss: 8.4.31 + postcss-values-parser: 6.0.2(postcss@8.4.31) dev: true /detective-sass@5.0.3: @@ -8129,8 +8377,8 @@ packages: /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - /electron-to-chromium@1.4.525: - resolution: {integrity: sha512-GIZ620hDK4YmIqAWkscG4W6RwY6gOx1y5J6f4JUQwctiJrqH2oxZYU4mXHi35oV32tr630UcepBzSBGJ/WYcZA==} + /electron-to-chromium@1.4.539: + resolution: {integrity: sha512-wRmWJ8F7rgmINuI32S6r2SLrw/h/bJQsDSvBiq9GBfvc2Lh73qTOwn73r3Cf67mjVgFGJYcYtmERzySa5jIWlg==} /elegant-spinner@1.0.1: resolution: {integrity: sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ==} @@ -8538,8 +8786,8 @@ packages: - supports-color dev: true - /eslint-plugin-jest@27.4.0(@typescript-eslint/eslint-plugin@6.7.2)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2): - resolution: {integrity: sha512-ukVeKmMPAUA5SWjHenvyyXnirKfHKMdOsTZdn5tZx5EW05HGVQwBohigjFZGGj3zuv1cV6hc82FvWv6LdIbkgg==} + /eslint-plugin-jest@27.4.2(@typescript-eslint/eslint-plugin@6.7.4)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2): + resolution: {integrity: sha512-3Nfvv3wbq2+PZlRTf2oaAWXWwbdBejFRBR2O8tAO67o+P8zno+QGbcDYaAXODlreXVg+9gvWhKKmG2rgfb8GEg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@typescript-eslint/eslint-plugin': ^5.0.0 || ^6.0.0 @@ -8551,7 +8799,7 @@ packages: jest: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/eslint-plugin': 6.7.4(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2) '@typescript-eslint/utils': 5.62.0(eslint@8.49.0)(typescript@5.2.2) eslint: 8.49.0 jest: 29.7.0(@types/node@20.6.3)(ts-node@10.9.1) @@ -8566,12 +8814,12 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 dependencies: - '@babel/runtime': 7.22.15 + '@babel/runtime': 7.23.1 aria-query: 5.3.0 array-includes: 3.1.7 array.prototype.flatmap: 1.3.2 ast-types-flow: 0.0.7 - axe-core: 4.8.1 + axe-core: 4.8.2 axobject-query: 3.2.1 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 @@ -8650,8 +8898,8 @@ packages: string.prototype.matchall: 4.0.10 dev: true - /eslint-plugin-storybook@0.6.13(eslint@8.49.0)(typescript@5.2.2): - resolution: {integrity: sha512-smd+CS0WH1jBqUEJ3znGS7DU4ayBE9z6lkQAK2yrSUv1+rq8BT/tiI5C/rKE7rmiqiAfojtNYZRhzo5HrulccQ==} + /eslint-plugin-storybook@0.6.14(eslint@8.49.0)(typescript@5.2.2): + resolution: {integrity: sha512-IeYigPur/MvESNDo43Z+Z5UvlcEVnt0dDZmnw1odi9X2Th1R3bpGyOZsHXb9bp1pFecOpRUuoMG5xdID2TwwOg==} engines: {node: 12.x || 14.x || >= 16} peerDependencies: eslint: '>=6' @@ -8666,8 +8914,8 @@ packages: - typescript dev: true - /eslint-plugin-testing-library@6.0.1(eslint@8.49.0)(typescript@5.2.2): - resolution: {integrity: sha512-CEYtjpcF3hAaQtYsTZqciR7s5z+T0LCMTwJeW+pz6kBnGtc866wAKmhaiK2Gsjc2jWNP7Gt6zhNr2DE1ZW4e+g==} + /eslint-plugin-testing-library@6.0.2(eslint@8.49.0)(typescript@5.2.2): + resolution: {integrity: sha512-3BV6FWtLbpKFb4Y1obSdt8PC9xSqz6T+7EHB/6pSCXqVjKPoS67ck903feKMKQphd5VhrX+N51yHuVaPa7elsw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6'} peerDependencies: eslint: ^7.5.0 || ^8.0.0 @@ -8705,7 +8953,7 @@ packages: hasBin: true dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) - '@eslint-community/regexpp': 4.8.1 + '@eslint-community/regexpp': 4.9.1 '@eslint/eslintrc': 2.1.2 '@eslint/js': 8.49.0 '@humanwhocodes/config-array': 0.11.11 @@ -8726,7 +8974,7 @@ packages: file-entry-cache: 6.0.1 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.21.0 + globals: 13.22.0 graphemer: 1.4.0 ignore: 5.2.4 imurmurhash: 0.1.4 @@ -8788,7 +9036,7 @@ packages: /estree-util-visit@1.2.1: resolution: {integrity: sha512-xbgqcrkIVbIG+lI/gzbvd9SGTJL4zqJKBFttUl5pP27KhAjtMKbX/mQXJ7qgyXpMgVy/zvpm0xoQQaGL8OloOw==} dependencies: - '@types/estree-jsx': 1.0.0 + '@types/estree-jsx': 1.0.1 '@types/unist': 2.0.8 dev: true @@ -9013,13 +9261,13 @@ packages: get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: - '@types/yauzl': 2.10.0 + '@types/yauzl': 2.10.1 transitivePeerDependencies: - supports-color dev: true - /fast-content-type-parse@1.0.0: - resolution: {integrity: sha512-Xbc4XcysUXcsP5aHUU7Nq3OwvHq97C+WnbkeIefpeYLX+ryzFJlU6OStFJhs6Ol0LkUGpcK+wL0JwfM+FCU5IA==} + /fast-content-type-parse@1.1.0: + resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} dev: true /fast-decode-uri-component@1.0.1: @@ -9105,17 +9353,17 @@ packages: '@fastify/fast-json-stringify-compiler': 4.3.0 abstract-logging: 2.0.1 avvio: 8.2.1 - fast-content-type-parse: 1.0.0 + fast-content-type-parse: 1.1.0 fast-json-stringify: 5.8.0 find-my-way: 7.6.2 light-my-request: 5.11.0 - pino: 8.15.1 + pino: 8.15.3 process-warning: 2.2.0 proxy-addr: 2.0.7 rfdc: 1.3.0 secure-json-parse: 2.7.0 semver: 7.5.4 - tiny-lru: 11.0.1 + tiny-lru: 11.2.0 transitivePeerDependencies: - supports-color dev: true @@ -9472,8 +9720,8 @@ packages: minipass: 3.3.6 dev: true - /fs-monkey@1.0.4: - resolution: {integrity: sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==} + /fs-monkey@1.0.5: + resolution: {integrity: sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==} /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -9582,8 +9830,8 @@ packages: get-intrinsic: 1.2.1 dev: true - /get-tsconfig@4.7.0: - resolution: {integrity: sha512-pmjiZ7xtB8URYm74PlGJozDNyhvsVLUcpBa8DZBG3bWHwaHa9bPiRpiSfovw+fjhwONSCWKRyk+JQHEGZmMrzw==} + /get-tsconfig@4.7.2: + resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} dependencies: resolve-pkg-maps: 1.0.0 dev: true @@ -9629,15 +9877,15 @@ packages: /glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - /glob@10.3.4: - resolution: {integrity: sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ==} + /glob@10.3.10: + resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true dependencies: foreground-child: 3.1.1 - jackspeak: 2.3.3 + jackspeak: 2.3.6 minimatch: 9.0.3 - minipass: 7.0.3 + minipass: 7.0.4 path-scurry: 1.10.1 dev: true @@ -9693,8 +9941,8 @@ packages: engines: {node: '>=4'} dev: true - /globals@13.21.0: - resolution: {integrity: sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==} + /globals@13.22.0: + resolution: {integrity: sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==} engines: {node: '>=8'} dependencies: type-fest: 0.20.2 @@ -9776,6 +10024,11 @@ packages: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true + /graphql@16.8.1: + resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + dev: true + /handle-thing@2.0.1: resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} @@ -9895,6 +10148,10 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + /headers-polyfill@3.2.5: + resolution: {integrity: sha512-tUCGvt191vNSQgttSyJoibR+VO+I6+iCHIUdhzEMJKE+EAL8BwCN7fUOZlY4ofOelNHsK+gEjxB/B+9N3EWtdA==} + dev: true + /hexer@1.5.0: resolution: {integrity: sha512-dyrPC8KzBzUJ19QTIo1gXNqIISRXQ0NwteW6OeQHRN4ZuZeHkdODfj0zHBdOlHbRY8GqbqK57C9oWSvQZizFsg==} engines: {node: '>= 0.10.x'} @@ -10031,7 +10288,7 @@ packages: - supports-color dev: true - /http-proxy-middleware@2.0.6(@types/express@4.17.17): + /http-proxy-middleware@2.0.6(@types/express@4.17.18): resolution: {integrity: sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==} engines: {node: '>=12.0.0'} peerDependencies: @@ -10040,7 +10297,7 @@ packages: '@types/express': optional: true dependencies: - '@types/express': 4.17.17 + '@types/express': 4.17.18 '@types/http-proxy': 1.17.12 http-proxy: 1.18.1(debug@4.3.4) is-glob: 4.0.3 @@ -10149,13 +10406,13 @@ packages: safer-buffer: 2.1.2 dev: true - /icss-utils@5.1.0(postcss@8.4.30): + /icss-utils@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.30 + postcss: 8.4.31 dev: true /ieee754@1.2.1: @@ -10275,6 +10532,27 @@ packages: through: 2.3.8 dev: true + /inquirer@8.2.6: + resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} + engines: {node: '>=12.0.0'} + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 5.4.1 + run-async: 2.4.1 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + wrap-ansi: 6.2.0 + dev: true + /inspect-with-kind@1.0.5: resolution: {integrity: sha512-MAQUJuIo7Xqk8EVNP+6d3CKq9c80hi4tjIbIAT6lmGW9W6WzlHiu9PS8uSuUYU+Do+j1baiFp3H25XEVxDIG2g==} dependencies: @@ -10570,6 +10848,11 @@ packages: is-path-inside: 3.0.3 dev: true + /is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + dev: true + /is-interactive@2.0.0: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} @@ -10584,6 +10867,10 @@ packages: engines: {node: '>= 0.4'} dev: true + /is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + dev: true + /is-npm@6.0.0: resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -10731,6 +11018,11 @@ packages: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} dev: true + /is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + dev: true + /is-unicode-supported@1.3.0: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} engines: {node: '>=12'} @@ -10816,8 +11108,8 @@ packages: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} dependencies: - '@babel/core': 7.22.20 - '@babel/parser': 7.22.16 + '@babel/core': 7.23.0 + '@babel/parser': 7.23.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 6.3.1 @@ -10829,8 +11121,8 @@ packages: resolution: {integrity: sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==} engines: {node: '>=10'} dependencies: - '@babel/core': 7.22.20 - '@babel/parser': 7.22.16 + '@babel/core': 7.23.0 + '@babel/parser': 7.23.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 7.5.4 @@ -10876,8 +11168,8 @@ packages: set-function-name: 2.0.1 dev: true - /jackspeak@2.3.3: - resolution: {integrity: sha512-R2bUw+kVZFS/h1AZqBKrSgDmdmjApzgY0AlCPumopFiAlbUxE2gf+SCuBzQ0cP5hHmUmFYF5yw55T97Th5Kstg==} + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} engines: {node: '>=14'} dependencies: '@isaacs/cliui': 8.0.2 @@ -10926,7 +11218,7 @@ packages: jest-util: 29.7.0 p-limit: 3.1.0 pretty-format: 29.7.0 - pure-rand: 6.0.3 + pure-rand: 6.0.4 slash: 3.0.0 stack-utils: 2.0.6 transitivePeerDependencies: @@ -10974,11 +11266,11 @@ packages: ts-node: optional: true dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 '@types/node': 20.6.3 - babel-jest: 29.7.0(@babel/core@7.22.20) + babel-jest: 29.7.0(@babel/core@7.23.0) chalk: 4.1.2 ci-info: 3.8.0 deepmerge: 4.3.1 @@ -11081,7 +11373,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/graceful-fs': 4.1.6 + '@types/graceful-fs': 4.1.7 '@types/node': 20.6.3 anymatch: 3.1.3 fb-watchman: 2.0.2 @@ -11242,15 +11534,15 @@ packages: resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/core': 7.22.20 - '@babel/generator': 7.22.15 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.20) - '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.20) - '@babel/types': 7.22.19 + '@babel/core': 7.23.0 + '@babel/generator': 7.23.0 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.23.0) + '@babel/types': 7.23.0 '@jest/expect-utils': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.20) + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.0) chalk: 4.1.2 expect: 29.7.0 graceful-fs: 4.2.11 @@ -11365,6 +11657,11 @@ packages: engines: {node: '>=10'} dev: true + /js-levenshtein@1.1.6: + resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} + engines: {node: '>=0.10.0'} + dev: true + /js-string-escape@1.0.1: resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==} engines: {node: '>= 0.8'} @@ -11827,6 +12124,14 @@ packages: chalk: 1.1.3 dev: true + /log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + dev: true + /log-symbols@5.1.0: resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} engines: {node: '>=12'} @@ -12009,7 +12314,7 @@ packages: /mdast-util-from-markdown@0.8.5: resolution: {integrity: sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==} dependencies: - '@types/mdast': 3.0.12 + '@types/mdast': 3.0.13 mdast-util-to-string: 2.0.0 micromark: 2.11.4 parse-entities: 2.0.0 @@ -12021,7 +12326,7 @@ packages: /mdast-util-from-markdown@1.3.1: resolution: {integrity: sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==} dependencies: - '@types/mdast': 3.0.12 + '@types/mdast': 3.0.13 '@types/unist': 2.0.8 decode-named-character-reference: 1.0.2 mdast-util-to-string: 3.2.0 @@ -12040,9 +12345,9 @@ packages: /mdast-util-mdx-expression@1.3.2: resolution: {integrity: sha512-xIPmR5ReJDu/DHH1OoIT1HkuybIfRGYRywC+gJtI7qHjCJp/M9jrmBEJW22O8lskDWm562BX2W8TiAwRTb0rKA==} dependencies: - '@types/estree-jsx': 1.0.0 + '@types/estree-jsx': 1.0.1 '@types/hast': 2.3.6 - '@types/mdast': 3.0.12 + '@types/mdast': 3.0.13 mdast-util-from-markdown: 1.3.1 mdast-util-to-markdown: 1.5.0 transitivePeerDependencies: @@ -12052,9 +12357,9 @@ packages: /mdast-util-mdx-jsx@2.1.4: resolution: {integrity: sha512-DtMn9CmVhVzZx3f+optVDF8yFgQVt7FghCRNdlIaS3X5Bnym3hZwPbg/XW86vdpKjlc1PVj26SpnLGeJBXD3JA==} dependencies: - '@types/estree-jsx': 1.0.0 + '@types/estree-jsx': 1.0.1 '@types/hast': 2.3.6 - '@types/mdast': 3.0.12 + '@types/mdast': 3.0.13 '@types/unist': 2.0.8 ccount: 2.0.1 mdast-util-from-markdown: 1.3.1 @@ -12083,9 +12388,9 @@ packages: /mdast-util-mdxjs-esm@1.3.1: resolution: {integrity: sha512-SXqglS0HrEvSdUEfoXFtcg7DRl7S2cwOXc7jkuusG472Mmjag34DUDeOJUZtl+BVnyeO1frIgVpHlNRWc2gk/w==} dependencies: - '@types/estree-jsx': 1.0.0 + '@types/estree-jsx': 1.0.1 '@types/hast': 2.3.6 - '@types/mdast': 3.0.12 + '@types/mdast': 3.0.13 mdast-util-from-markdown: 1.3.1 mdast-util-to-markdown: 1.5.0 transitivePeerDependencies: @@ -12095,14 +12400,14 @@ packages: /mdast-util-phrasing@3.0.1: resolution: {integrity: sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==} dependencies: - '@types/mdast': 3.0.12 + '@types/mdast': 3.0.13 unist-util-is: 5.2.1 dev: true /mdast-util-to-markdown@1.5.0: resolution: {integrity: sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==} dependencies: - '@types/mdast': 3.0.12 + '@types/mdast': 3.0.13 '@types/unist': 2.0.8 longest-streak: 3.1.0 mdast-util-phrasing: 3.0.1 @@ -12119,7 +12424,7 @@ packages: /mdast-util-to-string@3.2.0: resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==} dependencies: - '@types/mdast': 3.0.12 + '@types/mdast': 3.0.13 dev: true /mdn-data@2.0.28: @@ -12138,7 +12443,7 @@ packages: resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} dependencies: - fs-monkey: 1.0.4 + fs-monkey: 1.0.5 /memoize-one@6.0.0: resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} @@ -12148,7 +12453,7 @@ packages: resolution: {integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==} engines: {node: '>=8'} dependencies: - '@types/minimist': 1.2.2 + '@types/minimist': 1.2.3 camelcase-keys: 6.2.2 decamelize-keys: 1.1.1 hard-rejection: 2.1.0 @@ -12215,7 +12520,7 @@ packages: /micromark-extension-mdx-expression@1.0.8: resolution: {integrity: sha512-zZpeQtc5wfWKdzDsHRBY003H2Smg+PUi2REhqgIhdzAa5xonhP03FcXxqFSerFiNUr5AWmHpaNPQTBVOS4lrXw==} dependencies: - '@types/estree': 1.0.1 + '@types/estree': 1.0.2 micromark-factory-mdx-expression: 1.0.9 micromark-factory-space: 1.1.0 micromark-util-character: 1.2.0 @@ -12229,7 +12534,7 @@ packages: resolution: {integrity: sha512-gPH+9ZdmDflbu19Xkb8+gheqEDqkSpdCEubQyxuz/Hn8DOXiXvrXeikOoBA71+e8Pfi0/UYmU3wW3H58kr7akA==} dependencies: '@types/acorn': 4.0.6 - '@types/estree': 1.0.1 + '@types/estree': 1.0.2 estree-util-is-identifier-name: 2.1.0 micromark-factory-mdx-expression: 1.0.9 micromark-factory-space: 1.1.0 @@ -12249,7 +12554,7 @@ packages: /micromark-extension-mdxjs-esm@1.0.5: resolution: {integrity: sha512-xNRBw4aoURcyz/S69B19WnZAkWJMxHMT5hE36GtDAyhoyn/8TuAeqjFJQlwk+MKQsUD7b3l7kFX+vlfVWgcX1w==} dependencies: - '@types/estree': 1.0.1 + '@types/estree': 1.0.2 micromark-core-commonmark: 1.1.0 micromark-util-character: 1.2.0 micromark-util-events-to-acorn: 1.2.3 @@ -12293,7 +12598,7 @@ packages: /micromark-factory-mdx-expression@1.0.9: resolution: {integrity: sha512-jGIWzSmNfdnkJq05c7b0+Wv0Kfz3NJ3N4cBjnbO4zjXIlxJr+f8lk+5ZmwFvqdAbUy2q6B5rCY//g0QAAaXDWA==} dependencies: - '@types/estree': 1.0.1 + '@types/estree': 1.0.2 micromark-util-character: 1.2.0 micromark-util-events-to-acorn: 1.2.3 micromark-util-symbol: 1.1.0 @@ -12379,7 +12684,7 @@ packages: resolution: {integrity: sha512-ij4X7Wuc4fED6UoLWkmo0xJQhsktfNh1J0m8g4PbIMPlx+ek/4YdW5mvbye8z/aZvAPUoxgXHrwVlXAPKMRp1w==} dependencies: '@types/acorn': 4.0.6 - '@types/estree': 1.0.1 + '@types/estree': 1.0.2 '@types/unist': 2.0.8 estree-util-visit: 1.2.1 micromark-util-symbol: 1.1.0 @@ -12441,7 +12746,7 @@ packages: /micromark@3.2.0: resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==} dependencies: - '@types/debug': 4.1.8 + '@types/debug': 4.1.9 debug: 4.3.4(supports-color@9.4.0) decode-named-character-reference: 1.0.2 micromark-core-commonmark: 1.1.0 @@ -12597,8 +12902,8 @@ packages: engines: {node: '>=8'} dev: true - /minipass@7.0.3: - resolution: {integrity: sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==} + /minipass@7.0.4: + resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} engines: {node: '>=16 || 14 >=14.17'} dev: true @@ -12677,6 +12982,42 @@ packages: /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + /msw@1.3.2(typescript@5.2.2): + resolution: {integrity: sha512-wKLhFPR+NitYTkQl5047pia0reNGgf0P6a1eTnA5aNlripmiz0sabMvvHcicE8kQ3/gZcI0YiPFWmYfowfm3lA==} + engines: {node: '>=14'} + hasBin: true + requiresBuild: true + peerDependencies: + typescript: '>= 4.4.x <= 5.2.x' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@mswjs/cookies': 0.2.2 + '@mswjs/interceptors': 0.17.10 + '@open-draft/until': 1.0.3 + '@types/cookie': 0.4.1 + '@types/js-levenshtein': 1.1.1 + chalk: 4.1.2 + chokidar: 3.5.3 + cookie: 0.4.2 + graphql: 16.8.1 + headers-polyfill: 3.2.5 + inquirer: 8.2.6 + is-node-process: 1.2.0 + js-levenshtein: 1.1.6 + node-fetch: 2.7.0 + outvariant: 1.4.0 + path-to-regexp: 6.2.1 + strict-event-emitter: 0.4.6 + type-fest: 2.19.0 + typescript: 5.2.2 + yargs: 17.7.2 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /multicast-dns@7.2.5: resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} hasBin: true @@ -12697,6 +13038,10 @@ packages: resolution: {integrity: sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==} dev: true + /mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + dev: true + /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} dependencies: @@ -12907,7 +13252,7 @@ packages: resolution: {integrity: sha512-ByFz8S08HWVKd9r/lkTahZX7xSq4IRyPCUvuaduI4GHyQaSWEdVNK1krC05vlhL9W0SzDn8Yjowh0Ru4PKrOYw==} engines: {node: ^14.16.0 || >=16.0.0} dependencies: - '@netlify/open-api': 2.21.1 + '@netlify/open-api': 2.23.0 lodash-es: 4.17.21 micro-api-client: 3.3.0 node-fetch: 3.3.2 @@ -12980,7 +13325,7 @@ packages: resolution: {integrity: sha512-jn9vOIK/nfqoFCcpK89/VCVaLg1IHE6UVfDOzvqmANaJ/rWCTEdH8RZ1V278nv2jr36BJdyQXIAavBLXpzdlag==} engines: {node: '>=14'} dependencies: - '@babel/parser': 7.22.16 + '@babel/parser': 7.23.0 dev: true /node-stream-zip@1.15.0: @@ -13240,8 +13585,9 @@ packages: resolution: {integrity: sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg==} dev: true - /on-exit-leak-free@2.1.0: - resolution: {integrity: sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==} + /on-exit-leak-free@2.1.1: + resolution: {integrity: sha512-IPTBZ175tI0sSg0ikDcCDfa5dPgcFbJgABsTHsY+Mkdm6Y2VKGuchubXSvTuu5tSPl4mqt53o3nLI74HTs8UgQ==} + engines: {node: '>=14.0.0'} dev: true /on-finished@2.4.1: @@ -13325,6 +13671,21 @@ packages: type-check: 0.4.0 dev: true + /ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.1 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + dev: true + /ora@6.3.1: resolution: {integrity: sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -13357,6 +13718,10 @@ packages: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} dev: true + /outvariant@1.4.0: + resolution: {integrity: sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==} + dev: true + /p-cancelable@3.0.0: resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} engines: {node: '>=12.20'} @@ -13651,12 +14016,16 @@ packages: engines: {node: '>=16 || 14 >=14.17'} dependencies: lru-cache: 10.0.1 - minipass: 7.0.3 + minipass: 7.0.4 dev: true /path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + /path-to-regexp@6.2.1: + resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==} + dev: true + /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -13699,20 +14068,20 @@ packages: resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} dev: true - /pino@8.15.1: - resolution: {integrity: sha512-Cp4QzUQrvWCRJaQ8Lzv0mJzXVk4z2jlq8JNKMGaixC2Pz5L4l2p95TkuRvYbrEbe85NQsDKrAd4zalf7Ml6WiA==} + /pino@8.15.3: + resolution: {integrity: sha512-wDds1+DH8VaREe4fpLEKttGnDoLiX3KR3AP5bHsrRwEZ93y+Z/HFC03zkGSxpIGWKDHg24sloVqGcIWoLCkTLQ==} hasBin: true dependencies: atomic-sleep: 1.0.0 fast-redact: 3.3.0 - on-exit-leak-free: 2.1.0 + on-exit-leak-free: 2.1.1 pino-abstract-transport: 1.1.0 pino-std-serializers: 6.2.2 process-warning: 2.2.0 quick-format-unescaped: 4.0.4 real-require: 0.2.0 safe-stable-stringify: 2.4.3 - sonic-boom: 3.3.0 + sonic-boom: 3.4.0 thread-stream: 2.4.0 dev: true @@ -13750,7 +14119,7 @@ packages: engines: {node: '>=0.10.0'} dev: true - /postcss-load-config@4.0.1(postcss@8.4.30)(ts-node@10.9.1): + /postcss-load-config@4.0.1(postcss@8.4.31)(ts-node@10.9.1): resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} engines: {node: '>= 14'} peerDependencies: @@ -13763,12 +14132,12 @@ packages: optional: true dependencies: lilconfig: 2.1.0 - postcss: 8.4.30 + postcss: 8.4.31 ts-node: 10.9.1(@types/node@20.6.3)(typescript@5.2.2) yaml: 2.3.2 dev: true - /postcss-loader@7.3.3(postcss@8.4.30)(typescript@5.2.2)(webpack@5.88.2): + /postcss-loader@7.3.3(postcss@8.4.31)(typescript@5.2.2)(webpack@5.88.2): resolution: {integrity: sha512-YgO/yhtevGO/vJePCQmTxiaEwER94LABZN0ZMT4A0vsak9TpO+RvKRs7EmJ8peIlB9xfXCsS7M8LjqncsUZ5HA==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -13777,52 +14146,52 @@ packages: dependencies: cosmiconfig: 8.3.6(typescript@5.2.2) jiti: 1.20.0 - postcss: 8.4.30 + postcss: 8.4.31 semver: 7.5.4 webpack: 5.88.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - typescript dev: true - /postcss-modules-extract-imports@3.0.0(postcss@8.4.30): + /postcss-modules-extract-imports@3.0.0(postcss@8.4.31): resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.30 + postcss: 8.4.31 dev: true - /postcss-modules-local-by-default@4.0.3(postcss@8.4.30): + /postcss-modules-local-by-default@4.0.3(postcss@8.4.31): resolution: {integrity: sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.30) - postcss: 8.4.30 + icss-utils: 5.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-selector-parser: 6.0.13 postcss-value-parser: 4.2.0 dev: true - /postcss-modules-scope@3.0.0(postcss@8.4.30): + /postcss-modules-scope@3.0.0(postcss@8.4.31): resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.30 + postcss: 8.4.31 postcss-selector-parser: 6.0.13 dev: true - /postcss-modules-values@4.0.0(postcss@8.4.30): + /postcss-modules-values@4.0.0(postcss@8.4.31): resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.30) - postcss: 8.4.30 + icss-utils: 5.1.0(postcss@8.4.31) + postcss: 8.4.31 dev: true /postcss-selector-parser@6.0.13: @@ -13837,7 +14206,7 @@ packages: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} dev: true - /postcss-values-parser@6.0.2(postcss@8.4.30): + /postcss-values-parser@6.0.2(postcss@8.4.31): resolution: {integrity: sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==} engines: {node: '>=10'} peerDependencies: @@ -13845,12 +14214,12 @@ packages: dependencies: color-name: 1.1.4 is-url-superb: 4.0.0 - postcss: 8.4.30 + postcss: 8.4.31 quote-unquote: 1.0.0 dev: true - /postcss@8.4.30: - resolution: {integrity: sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==} + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.6 @@ -14058,8 +14427,8 @@ packages: escape-goat: 4.0.0 dev: true - /pure-rand@6.0.3: - resolution: {integrity: sha512-KddyFewCsO0j3+np81IQ+SweXLDnDQTs5s67BOnrYmYe/yNmUhttQyGsYzy8yUnoljGAQ9sl38YB4vH8ur7Y+w==} + /pure-rand@6.0.4: + resolution: {integrity: sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==} dev: true /qs@6.11.0: @@ -14252,7 +14621,7 @@ packages: resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} engines: {node: '>=8'} dependencies: - '@types/normalize-package-data': 2.4.1 + '@types/normalize-package-data': 2.4.2 normalize-package-data: 2.5.0 parse-json: 5.2.0 type-fest: 0.6.0 @@ -14262,7 +14631,7 @@ packages: resolution: {integrity: sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==} engines: {node: '>=12.20'} dependencies: - '@types/normalize-package-data': 2.4.1 + '@types/normalize-package-data': 2.4.2 normalize-package-data: 3.0.3 parse-json: 5.2.0 type-fest: 2.19.0 @@ -14396,7 +14765,7 @@ packages: /regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} dependencies: - '@babel/runtime': 7.22.15 + '@babel/runtime': 7.23.1 dev: true /regex-not@1.0.2: @@ -14470,7 +14839,7 @@ packages: /remark-parse@10.0.2: resolution: {integrity: sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==} dependencies: - '@types/mdast': 3.0.12 + '@types/mdast': 3.0.13 mdast-util-from-markdown: 1.3.1 unified: 10.1.2 transitivePeerDependencies: @@ -14480,7 +14849,7 @@ packages: /remark-stringify@10.0.3: resolution: {integrity: sha512-koyOzCMYoUHudypbj4XpnAKFbkddRMYZHwghnxd7ue5210WzGw6kOBwauJTRUMq16jsovXx8dYNvSSWP89kZ3A==} dependencies: - '@types/mdast': 3.0.12 + '@types/mdast': 3.0.13 mdast-util-to-markdown: 1.5.0 unified: 10.1.2 dev: true @@ -14609,6 +14978,14 @@ packages: signal-exit: 3.0.7 dev: true + /restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: true + /restore-cursor@4.0.0: resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -14651,8 +15028,8 @@ packages: dependencies: glob: 7.2.3 - /rollup@3.29.2: - resolution: {integrity: sha512-CJouHoZ27v6siztc21eEQGo0kIcE5D1gVPA571ez0mMYb25LGYGKnVNXpEj5MGlepmDWGXNjDB5q7uNiPHC11A==} + /rollup@3.29.4: + resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: @@ -14684,6 +15061,12 @@ packages: tslib: 1.14.1 dev: true + /rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + dependencies: + tslib: 2.6.2 + dev: true + /sade@1.8.1: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} @@ -15049,8 +15432,8 @@ packages: uuid: 8.3.2 websocket-driver: 0.7.4 - /sonic-boom@3.3.0: - resolution: {integrity: sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==} + /sonic-boom@3.4.0: + resolution: {integrity: sha512-zSe9QQW30nPzjkSJ0glFQO5T9lHsk39tz+2bAAwCj8CNgEG8ItZiX7Wb2ZgA8I04dwRGCcf1m3ABJa8AYm12Fw==} dependencies: atomic-sleep: 1.0.0 dev: true @@ -15263,6 +15646,16 @@ packages: queue-tick: 1.0.1 dev: true + /strict-event-emitter@0.2.8: + resolution: {integrity: sha512-KDf/ujU8Zud3YaLtMCcTI4xkZlZVIYxTLr+XIULexP+77EEVWixeXroLUXQXiVtH4XH2W7jr/3PT1v3zBuvc3A==} + dependencies: + events: 3.3.0 + dev: true + + /strict-event-emitter@0.4.6: + resolution: {integrity: sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==} + dev: true + /string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} @@ -15775,8 +16168,8 @@ packages: engines: {node: '>=4'} dev: true - /tiny-lru@11.0.1: - resolution: {integrity: sha512-iNgFugVuQgBKrqeO/mpiTTgmBsTP0WL6yeuLfLs/Ctf0pI/ixGqIRm8sDCwMcXGe9WWvt2sGXI5mNqZbValmJg==} + /tiny-lru@11.2.0: + resolution: {integrity: sha512-DTmmE5orth9hNGwBrblQ8D+RMFHr93LawLJCBidSOX3TEFAEYCskzhAOS4UgtnKFGylkGYGX/IgtzKTAOrxy8A==} engines: {node: '>=12'} dev: true @@ -15955,7 +16348,7 @@ packages: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} dev: true - /ts-jest@29.1.1(@babel/core@7.22.20)(esbuild@0.18.20)(jest@29.7.0)(typescript@5.2.2): + /ts-jest@29.1.1(@babel/core@7.23.0)(esbuild@0.18.20)(jest@29.7.0)(typescript@5.2.2): resolution: {integrity: sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -15976,7 +16369,7 @@ packages: esbuild: optional: true dependencies: - '@babel/core': 7.22.20 + '@babel/core': 7.23.0 bs-logger: 0.2.6 esbuild: 0.18.20 fast-json-stable-stringify: 2.1.0 @@ -16037,7 +16430,7 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - /tsup@7.2.0(@swc/core@1.3.86)(postcss@8.4.30)(ts-node@10.9.1)(typescript@5.2.2): + /tsup@7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2): resolution: {integrity: sha512-vDHlczXbgUvY3rWvqFEbSqmC1L7woozbzngMqTtL2PGBODTtWlRwGDDawhvWzr5c1QjKe4OAKqJGfE1xeXUvtQ==} engines: {node: '>=16.14'} hasBin: true @@ -16054,7 +16447,7 @@ packages: optional: true dependencies: '@swc/core': 1.3.86(@swc/helpers@0.5.2) - bundle-require: 4.0.1(esbuild@0.18.20) + bundle-require: 4.0.2(esbuild@0.18.20) cac: 6.7.14 chokidar: 3.5.3 debug: 4.3.4(supports-color@9.4.0) @@ -16062,10 +16455,10 @@ packages: execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss: 8.4.30 - postcss-load-config: 4.0.1(postcss@8.4.30)(ts-node@10.9.1) + postcss: 8.4.31 + postcss-load-config: 4.0.1(postcss@8.4.31)(ts-node@10.9.1) resolve-from: 5.0.0 - rollup: 3.29.2 + rollup: 3.29.4 source-map: 0.8.0-beta.0 sucrase: 3.34.0 tree-kill: 1.2.2 @@ -16085,8 +16478,8 @@ packages: typescript: 5.2.2 dev: true - /tty-table@4.2.1: - resolution: {integrity: sha512-xz0uKo+KakCQ+Dxj1D/tKn2FSyreSYWzdkL/BYhgN6oMW808g8QRMuh1atAV9fjTPbWBjfbkKQpI/5rEcnAc7g==} + /tty-table@4.2.2: + resolution: {integrity: sha512-2gvCArMZLxgvpZ2NvQKdnYWIFLe7I/z5JClMuhrDXunmKgSZcQKcZRjN9XjAFiToMz2pUo1dEIXyrm0AwgV5Tw==} engines: {node: '>=8.0.0'} hasBin: true dependencies: @@ -16271,9 +16664,9 @@ packages: resolution: {integrity: sha512-5+JDIs4hqKfHnJcVCxTid1yBoI/++FfF/1PFdSMpaftZZZY+qg2JFruRbf7PaIwa9KgLotXQV3gSjtY0IdcFGQ==} dependencies: '@types/concat-stream': 2.0.0 - '@types/debug': 4.1.8 + '@types/debug': 4.1.9 '@types/is-empty': 1.2.1 - '@types/node': 18.17.18 + '@types/node': 18.18.3 '@types/unist': 2.0.8 concat-stream: 2.0.0 debug: 4.3.4(supports-color@9.4.0) @@ -16437,8 +16830,8 @@ packages: engines: {node: '>=8'} dev: true - /update-browserslist-db@1.0.11(browserslist@4.21.10): - resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} + /update-browserslist-db@1.0.13(browserslist@4.21.10): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -16447,6 +16840,17 @@ packages: escalade: 3.1.1 picocolors: 1.0.0 + /update-browserslist-db@1.0.13(browserslist@4.22.1): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.22.1 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + /update-notifier@6.0.2: resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} engines: {node: '>=14.16'} @@ -16500,6 +16904,16 @@ packages: /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + /util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + dependencies: + inherits: 2.0.4 + is-arguments: 1.1.1 + is-generator-function: 1.0.10 + is-typed-array: 1.1.12 + which-typed-array: 1.1.11 + dev: true + /utila@0.4.0: resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} @@ -16648,6 +17062,14 @@ packages: defaults: 1.0.4 dev: true + /web-encoding@1.1.5: + resolution: {integrity: sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==} + dependencies: + util: 0.12.5 + optionalDependencies: + '@zxing/text-encoding': 0.9.0 + dev: true + /web-streams-polyfill@3.2.1: resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} engines: {node: '>= 8'} @@ -16727,11 +17149,11 @@ packages: dependencies: '@types/bonjour': 3.5.11 '@types/connect-history-api-fallback': 1.5.1 - '@types/express': 4.17.17 - '@types/serve-index': 1.9.1 - '@types/serve-static': 1.15.2 - '@types/sockjs': 0.3.33 - '@types/ws': 8.5.5 + '@types/express': 4.17.18 + '@types/serve-index': 1.9.2 + '@types/serve-static': 1.15.3 + '@types/sockjs': 0.3.34 + '@types/ws': 8.5.6 ansi-html-community: 0.0.8 bonjour-service: 1.1.1 chokidar: 3.5.3 @@ -16742,7 +17164,7 @@ packages: express: 4.18.2 graceful-fs: 4.2.11 html-entities: 2.4.0 - http-proxy-middleware: 2.0.6(@types/express@4.17.17) + http-proxy-middleware: 2.0.6(@types/express@4.17.18) ipaddr.js: 2.1.0 launch-editor: 2.6.0 open: 8.4.2 @@ -16784,8 +17206,8 @@ packages: webpack-cli: optional: true dependencies: - '@types/eslint-scope': 3.7.4 - '@types/estree': 1.0.1 + '@types/eslint-scope': 3.7.5 + '@types/estree': 1.0.2 '@webassemblyjs/ast': 1.11.6 '@webassemblyjs/wasm-edit': 1.11.6 '@webassemblyjs/wasm-parser': 1.11.6 @@ -16824,8 +17246,8 @@ packages: webpack-cli: optional: true dependencies: - '@types/eslint-scope': 3.7.4 - '@types/estree': 1.0.1 + '@types/eslint-scope': 3.7.5 + '@types/estree': 1.0.2 '@webassemblyjs/ast': 1.11.6 '@webassemblyjs/wasm-edit': 1.11.6 '@webassemblyjs/wasm-parser': 1.11.6 diff --git a/sample/another-remote-module/package.json b/sample/another-remote-module/package.json index 3f56c715c..a09445f49 100644 --- a/sample/another-remote-module/package.json +++ b/sample/another-remote-module/package.json @@ -8,6 +8,7 @@ "type": "module", "scripts": { "dev": "webpack serve --config webpack.dev.js", + "dev-msw": "pnpm dev", "dev-local": "cross-env LOCAL=true webpack serve --config webpack.dev.js", "build": "webpack --config webpack.build.js", "serve-build": "pnpm build && pnpm http-server dist -p 8082 -P http://localhost:8082? -c-1" diff --git a/sample/another-remote-module/public/favicon.png b/sample/another-remote-module/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..47157f6b014741fca15dd80db4939ee1e1098175 GIT binary patch literal 31428 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 mxo^y^-Z6)u1aY{6#-0B28$;=-+&0)_K +
diff --git a/sample/host/mocks/browser.ts b/sample/host/mocks/browser.ts new file mode 100644 index 000000000..f3ec9ee76 --- /dev/null +++ b/sample/host/mocks/browser.ts @@ -0,0 +1,8 @@ +import { setupWorker, type RestHandler, type StartOptions } from "msw"; +import { hostRequestHandlers } from "./handlers.ts"; + +export function startMsw(moduleRequestHandlers: RestHandler[], options?: StartOptions) { + const worker = setupWorker(...hostRequestHandlers, ...moduleRequestHandlers); + + worker.start(options); +} diff --git a/sample/host/mocks/handlers.ts b/sample/host/mocks/handlers.ts new file mode 100644 index 000000000..233ab03fb --- /dev/null +++ b/sample/host/mocks/handlers.ts @@ -0,0 +1,13 @@ +import { rest, type RestHandler } from "msw"; + +export const hostRequestHandlers: RestHandler[] = [ + rest.get("/feature-flags", (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json([ + { feature: "send-message", isActive: true }, + { feature: "ricky-and-morty-characters", isActive: true } + ]) + ); + }) +]; diff --git a/sample/host/package.json b/sample/host/package.json index 99d343f52..bb760350a 100644 --- a/sample/host/package.json +++ b/sample/host/package.json @@ -8,6 +8,7 @@ "type": "module", "scripts": { "dev": "nodemon", + "dev-msw": "cross-env USE_MSW=true pnpm dev", "build": "pnpm build:webpack && pnpm build:copy-redirects", "build:webpack": "webpack --config webpack.build.js", "build:copy-redirects": "copyfiles _redirects dist", @@ -26,7 +27,9 @@ "@workleap/webpack-configs": "1.0.8", "browserslist": "4.21.10", "copyfiles": "2.4.1", + "cross-env": "7.0.3", "http-server": "14.1.1", + "msw": "1.3.2", "nodemon": "3.0.1", "typescript": "5.2.2", "webpack": "5.88.2", @@ -39,7 +42,9 @@ "@sample/shell": "workspace:*", "@squide/fakes": "workspace:*", "@squide/react-router": "workspace:*", + "@squide/msw": "workspace:*", "@squide/webpack-module-federation": "workspace:*", + "@tanstack/react-query": "rc", "react": "18.2.0", "react-dom": "18.2.0", "react-router-dom": "6.16.0" diff --git a/sample/host/public/index.html b/sample/host/public/index.html index 74bd0a31d..98accb408 100644 --- a/sample/host/public/index.html +++ b/sample/host/public/index.html @@ -1,9 +1,9 @@ - - - - -
- + + + + +
+ diff --git a/sample/host/public/mockServiceWorker.js b/sample/host/public/mockServiceWorker.js new file mode 100644 index 000000000..8ee70b3e4 --- /dev/null +++ b/sample/host/public/mockServiceWorker.js @@ -0,0 +1,303 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker (1.2.2). + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + * - Please do NOT serve this file on production. + */ + +const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70' +const activeClientIds = new Set() + +self.addEventListener('install', function () { + self.skipWaiting() +}) + +self.addEventListener('activate', function (event) { + event.waitUntil(self.clients.claim()) +}) + +self.addEventListener('message', async function (event) { + const clientId = event.source.id + + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: INTEGRITY_CHECKSUM, + }) + break + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: true, + }) + break + } + + case 'MOCK_DEACTIVATE': { + activeClientIds.delete(clientId) + break + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break + } + } +}) + +self.addEventListener('fetch', function (event) { + const { request } = event + const accept = request.headers.get('accept') || '' + + // Bypass server-sent events. + if (accept.includes('text/event-stream')) { + return + } + + // Bypass navigation requests. + if (request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + // Generate unique request ID. + const requestId = Math.random().toString(16).slice(2) + + event.respondWith( + handleRequest(event, requestId).catch((error) => { + if (error.name === 'NetworkError') { + console.warn( + '[MSW] Successfully emulated a network error for the "%s %s" request.', + request.method, + request.url, + ) + return + } + + // At this point, any exception indicates an issue with the original request/response. + console.error( + `\ +[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`, + request.method, + request.url, + `${error.name}: ${error.message}`, + ) + }), + ) +}) + +async function handleRequest(event, requestId) { + const client = await resolveMainClient(event) + const response = await getResponse(event, client, requestId) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + ;(async function () { + const clonedResponse = response.clone() + sendToClient(client, { + type: 'RESPONSE', + payload: { + requestId, + type: clonedResponse.type, + ok: clonedResponse.ok, + status: clonedResponse.status, + statusText: clonedResponse.statusText, + body: + clonedResponse.body === null ? null : await clonedResponse.text(), + headers: Object.fromEntries(clonedResponse.headers.entries()), + redirected: clonedResponse.redirected, + }, + }) + })() + } + + return response +} + +// Resolve the main client for the given event. +// Client that issues a request doesn't necessarily equal the client +// that registered the worker. It's with the latter the worker should +// communicate with during the response resolving phase. +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (client?.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + +async function getResponse(event, client, requestId) { + const { request } = event + const clonedRequest = request.clone() + + function passthrough() { + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const headers = Object.fromEntries(clonedRequest.headers.entries()) + + // Remove MSW-specific request headers so the bypassed requests + // comply with the server's CORS preflight check. + // Operate with the headers as an object because request "Headers" + // are immutable. + delete headers['x-msw-bypass'] + + return fetch(clonedRequest, { headers }) + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough() + } + + // Bypass requests with the explicit bypass header. + // Such requests can be issued by "ctx.fetch()". + if (request.headers.get('x-msw-bypass') === 'true') { + return passthrough() + } + + // Notify the client that a request has been intercepted. + const clientMessage = await sendToClient(client, { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + mode: request.mode, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: await request.text(), + bodyUsed: request.bodyUsed, + keepalive: request.keepalive, + }, + }) + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data) + } + + case 'MOCK_NOT_FOUND': { + return passthrough() + } + + case 'NETWORK_ERROR': { + const { name, message } = clientMessage.data + const networkError = new Error(message) + networkError.name = name + + // Rejecting a "respondWith" promise emulates a network error. + throw networkError + } + } + + return passthrough() +} + +function sendToClient(client, message) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } + + resolve(event.data) + } + + client.postMessage(message, [channel.port2]) + }) +} + +function sleep(timeMs) { + return new Promise((resolve) => { + setTimeout(resolve, timeMs) + }) +} + +async function respondWithMock(response) { + await sleep(response.delay) + return new Response(response.body, response) +} diff --git a/sample/host/src/bootstrap.tsx b/sample/host/src/bootstrap.tsx index 3b041d3aa..49736c1be 100644 --- a/sample/host/src/bootstrap.tsx +++ b/sample/host/src/bootstrap.tsx @@ -1,5 +1,6 @@ import { register as registerLocalModule } from "@sample/local-module"; import { isNetlify, type AppContext } from "@sample/shared"; +import { MswPlugin } from "@squide/msw"; import { ConsoleLogger, Runtime, RuntimeContext, registerLocalModules } from "@squide/react-router"; import { registerRemoteModules, type RemoteDefinition } from "@squide/webpack-module-federation"; import { StrictMode } from "react"; @@ -18,7 +19,10 @@ const Remotes: RemoteDefinition[] = [ } ]; +const mswPlugin = new MswPlugin(); + const runtime = new Runtime({ + plugins: [mswPlugin], loggers: [new ConsoleLogger()], sessionAccessor }); @@ -29,7 +33,13 @@ const context: AppContext = { registerLocalModules([registerLocalModule], runtime, { context }); -registerRemoteModules(Remotes, runtime, { context }); +registerRemoteModules(Remotes, runtime, { context }).then(() => { + if (process.env.USE_MSW) { + import("../mocks/browser.ts").then(({ startMsw }) => { + startMsw(mswPlugin.requestHandlers); + }); + } +}); const root = createRoot(document.getElementById("root")!); diff --git a/sample/host/webpack.dev.js b/sample/host/webpack.dev.js index 092270968..5429082fa 100644 --- a/sample/host/webpack.dev.js +++ b/sample/host/webpack.dev.js @@ -11,6 +11,7 @@ export default defineDevHostConfig(swcConfig, "host", 8080, { } }, environmentVariables: { - "NETLIFY": process.env.NETLIFY === "true" + "NETLIFY": process.env.NETLIFY === "true", + "USE_MSW": process.env.USE_MSW === "true" } }); diff --git a/sample/local-module/package.json b/sample/local-module/package.json index f10d3a378..26d89d1f1 100644 --- a/sample/local-module/package.json +++ b/sample/local-module/package.json @@ -15,6 +15,7 @@ }, "scripts": { "dev": "nodemon", + "dev-msw": "pnpm dev", "dev-local": "nodemon --config nodemon.local.json", "build": "tsup --config ./tsup.build.ts", "serve-build": "pnpm build" diff --git a/sample/local-module/public/favicon.png b/sample/local-module/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..47157f6b014741fca15dd80db4939ee1e1098175 GIT binary patch literal 31428 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
?A9r6C2}zB4mk zSHJe)zm>JE#`EE!n4|;SUuqD`q67%cqEOWKR{7psS;XJHT4i;PcVxQW zRt#4$TZ1oeRA6TnLvO=Yqead+T4C10)>IOZl1=wdnb%)9+KD=XEeV)1x&(la;GWM%pIPsrYs0Bgs-MSJR4TWueG)_-X%64N z%}K=WGQ@?wxc;&IXY}EZ;QHSgE~N07*i@{0)h9YzgZQ_JuwhJtOi3H-DbFq*t4)4# zp4)bP^=#C+IOiB75(R+LkGV0{AN|k%)&F?$`hId1`~K)q&*i%$e*?yv$?*sBe=Tnk zDH`$apHLuAyge`I`P9fUKDh3QFDvgskil2(?crLTU>ai*_U7D7^s@VK%+s6$+vNGC zJ%2q^9NEdx*z4QQtgU}67%pdmDO_{EewjRqndx?feKkpy%}=Oga5EfsFXO-ajC*X_ zCYxP6=B2Q(IBPOZ`Fx@=wnbXZ4zh*<(w{S5j`!;4LG^!a(Lb0cS17=?E5vHPAtw8n zTV9~YN3(MuOlBFV)!A;K^anjQqSEfV==V0vIC!G30{!T#kzb6t7dw*hbV*p7+y7>_ zcMKp$;U>Jeiu`OaeC~z(NSjNY{VXJ(vd`ih`!o@CwyfRm=JaT&?&b`^EJ-VO? zJ@JuMc>$VQbk@7)g(-3$Ghr&2Cj;QVqt88Y5`)F9veuASakX4tXn{(?^2N#oml3$0 z%f7z{5}GB^g+7@ly1I&nQj-1_+$sHPz!X4LK0@gMn3D(k%gp&3JMw~N6ud4kdG`fAfR5TFC8!}MdSB4@e^}8-l*bzCV~lnC8O_-`Ou{PcD+Gh|au2 z(YsG3lD9nvuq~go+)F#%|C3EHva-c?9VnJO6sN-dNf@k(IE3Pisc5yvxUM;_Z9F5# zEGa#!Z+9x&s|6nWnv%a~Cyp;Um48IgLvxaIzk_1S^Rtgtj`3O%yBwG~G_-q9%-v}E zs5@T9Lz7p0yiQJeG(!qWJr|a+KnwC9aE=-s2aV~`WqzH8Y@3h!1+2DRRd}qf mxo^y^-Z6)u1aY{6#-0B28$;=-+&0)_K - - - -
- + + + + +
+ diff --git a/sample/remote-module/mocks/browser.ts b/sample/remote-module/mocks/browser.ts new file mode 100644 index 000000000..7d722244b --- /dev/null +++ b/sample/remote-module/mocks/browser.ts @@ -0,0 +1,7 @@ +import { setupWorker, type RestHandler, type StartOptions } from "msw"; + +export function startMsw(requestHandlers: RestHandler[], options?: StartOptions) { + const worker = setupWorker(...requestHandlers); + + worker.start(options); +} diff --git a/sample/remote-module/mocks/handlers.ts b/sample/remote-module/mocks/handlers.ts new file mode 100644 index 000000000..37430410c --- /dev/null +++ b/sample/remote-module/mocks/handlers.ts @@ -0,0 +1,340 @@ +import { rest, type RestHandler } from "msw"; + +export const requestHandlers: RestHandler[] = [ + rest.get("https://rickandmortyapi.com/api/character/1,2,3,4,5", (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json([{ + "id": 1, + // "name": "Rick Sanchez", + "name": "IT'S WORKING", + "status": "Alive", + "species": "Human", + "type": "", + "gender": "Male", + "origin": { + "name": "Earth (C-137)", + "url": "https://rickandmortyapi.com/api/location/1" + }, + "location": { + "name": "Citadel of Ricks", + "url": "https://rickandmortyapi.com/api/location/3" + }, + "image": "https://rickandmortyapi.com/api/character/avatar/1.jpeg", + "episode": [ + "https://rickandmortyapi.com/api/episode/1", + "https://rickandmortyapi.com/api/episode/2", + "https://rickandmortyapi.com/api/episode/3", + "https://rickandmortyapi.com/api/episode/4", + "https://rickandmortyapi.com/api/episode/5", + "https://rickandmortyapi.com/api/episode/6", + "https://rickandmortyapi.com/api/episode/7", + "https://rickandmortyapi.com/api/episode/8", + "https://rickandmortyapi.com/api/episode/9", + "https://rickandmortyapi.com/api/episode/10", + "https://rickandmortyapi.com/api/episode/11", + "https://rickandmortyapi.com/api/episode/12", + "https://rickandmortyapi.com/api/episode/13", + "https://rickandmortyapi.com/api/episode/14", + "https://rickandmortyapi.com/api/episode/15", + "https://rickandmortyapi.com/api/episode/16", + "https://rickandmortyapi.com/api/episode/17", + "https://rickandmortyapi.com/api/episode/18", + "https://rickandmortyapi.com/api/episode/19", + "https://rickandmortyapi.com/api/episode/20", + "https://rickandmortyapi.com/api/episode/21", + "https://rickandmortyapi.com/api/episode/22", + "https://rickandmortyapi.com/api/episode/23", + "https://rickandmortyapi.com/api/episode/24", + "https://rickandmortyapi.com/api/episode/25", + "https://rickandmortyapi.com/api/episode/26", + "https://rickandmortyapi.com/api/episode/27", + "https://rickandmortyapi.com/api/episode/28", + "https://rickandmortyapi.com/api/episode/29", + "https://rickandmortyapi.com/api/episode/30", + "https://rickandmortyapi.com/api/episode/31", + "https://rickandmortyapi.com/api/episode/32", + "https://rickandmortyapi.com/api/episode/33", + "https://rickandmortyapi.com/api/episode/34", + "https://rickandmortyapi.com/api/episode/35", + "https://rickandmortyapi.com/api/episode/36", + "https://rickandmortyapi.com/api/episode/37", + "https://rickandmortyapi.com/api/episode/38", + "https://rickandmortyapi.com/api/episode/39", + "https://rickandmortyapi.com/api/episode/40", + "https://rickandmortyapi.com/api/episode/41", + "https://rickandmortyapi.com/api/episode/42", + "https://rickandmortyapi.com/api/episode/43", + "https://rickandmortyapi.com/api/episode/44", + "https://rickandmortyapi.com/api/episode/45", + "https://rickandmortyapi.com/api/episode/46", + "https://rickandmortyapi.com/api/episode/47", + "https://rickandmortyapi.com/api/episode/48", + "https://rickandmortyapi.com/api/episode/49", + "https://rickandmortyapi.com/api/episode/50", + "https://rickandmortyapi.com/api/episode/51" + ], + "url": "https://rickandmortyapi.com/api/character/1", + "created": "2017-11-04T18:48:46.250Z" + }, + { + "id": 2, + "name": "Morty Smith", + "status": "Alive", + "species": "Human", + "type": "", + "gender": "Male", + "origin": { + "name": "unknown", + "url": "" + }, + "location": { + "name": "Citadel of Ricks", + "url": "https://rickandmortyapi.com/api/location/3" + }, + "image": "https://rickandmortyapi.com/api/character/avatar/2.jpeg", + "episode": [ + "https://rickandmortyapi.com/api/episode/1", + "https://rickandmortyapi.com/api/episode/2", + "https://rickandmortyapi.com/api/episode/3", + "https://rickandmortyapi.com/api/episode/4", + "https://rickandmortyapi.com/api/episode/5", + "https://rickandmortyapi.com/api/episode/6", + "https://rickandmortyapi.com/api/episode/7", + "https://rickandmortyapi.com/api/episode/8", + "https://rickandmortyapi.com/api/episode/9", + "https://rickandmortyapi.com/api/episode/10", + "https://rickandmortyapi.com/api/episode/11", + "https://rickandmortyapi.com/api/episode/12", + "https://rickandmortyapi.com/api/episode/13", + "https://rickandmortyapi.com/api/episode/14", + "https://rickandmortyapi.com/api/episode/15", + "https://rickandmortyapi.com/api/episode/16", + "https://rickandmortyapi.com/api/episode/17", + "https://rickandmortyapi.com/api/episode/18", + "https://rickandmortyapi.com/api/episode/19", + "https://rickandmortyapi.com/api/episode/20", + "https://rickandmortyapi.com/api/episode/21", + "https://rickandmortyapi.com/api/episode/22", + "https://rickandmortyapi.com/api/episode/23", + "https://rickandmortyapi.com/api/episode/24", + "https://rickandmortyapi.com/api/episode/25", + "https://rickandmortyapi.com/api/episode/26", + "https://rickandmortyapi.com/api/episode/27", + "https://rickandmortyapi.com/api/episode/28", + "https://rickandmortyapi.com/api/episode/29", + "https://rickandmortyapi.com/api/episode/30", + "https://rickandmortyapi.com/api/episode/31", + "https://rickandmortyapi.com/api/episode/32", + "https://rickandmortyapi.com/api/episode/33", + "https://rickandmortyapi.com/api/episode/34", + "https://rickandmortyapi.com/api/episode/35", + "https://rickandmortyapi.com/api/episode/36", + "https://rickandmortyapi.com/api/episode/37", + "https://rickandmortyapi.com/api/episode/38", + "https://rickandmortyapi.com/api/episode/39", + "https://rickandmortyapi.com/api/episode/40", + "https://rickandmortyapi.com/api/episode/41", + "https://rickandmortyapi.com/api/episode/42", + "https://rickandmortyapi.com/api/episode/43", + "https://rickandmortyapi.com/api/episode/44", + "https://rickandmortyapi.com/api/episode/45", + "https://rickandmortyapi.com/api/episode/46", + "https://rickandmortyapi.com/api/episode/47", + "https://rickandmortyapi.com/api/episode/48", + "https://rickandmortyapi.com/api/episode/49", + "https://rickandmortyapi.com/api/episode/50", + "https://rickandmortyapi.com/api/episode/51" + ], + "url": "https://rickandmortyapi.com/api/character/2", + "created": "2017-11-04T18:50:21.651Z" + }, + { + "id": 3, + "name": "Summer Smith", + "status": "Alive", + "species": "Human", + "type": "", + "gender": "Female", + "origin": { + "name": "Earth (Replacement Dimension)", + "url": "https://rickandmortyapi.com/api/location/20" + }, + "location": { + "name": "Earth (Replacement Dimension)", + "url": "https://rickandmortyapi.com/api/location/20" + }, + "image": "https://rickandmortyapi.com/api/character/avatar/3.jpeg", + "episode": [ + "https://rickandmortyapi.com/api/episode/6", + "https://rickandmortyapi.com/api/episode/7", + "https://rickandmortyapi.com/api/episode/8", + "https://rickandmortyapi.com/api/episode/9", + "https://rickandmortyapi.com/api/episode/10", + "https://rickandmortyapi.com/api/episode/11", + "https://rickandmortyapi.com/api/episode/12", + "https://rickandmortyapi.com/api/episode/14", + "https://rickandmortyapi.com/api/episode/15", + "https://rickandmortyapi.com/api/episode/16", + "https://rickandmortyapi.com/api/episode/17", + "https://rickandmortyapi.com/api/episode/18", + "https://rickandmortyapi.com/api/episode/19", + "https://rickandmortyapi.com/api/episode/20", + "https://rickandmortyapi.com/api/episode/21", + "https://rickandmortyapi.com/api/episode/22", + "https://rickandmortyapi.com/api/episode/23", + "https://rickandmortyapi.com/api/episode/24", + "https://rickandmortyapi.com/api/episode/25", + "https://rickandmortyapi.com/api/episode/26", + "https://rickandmortyapi.com/api/episode/27", + "https://rickandmortyapi.com/api/episode/29", + "https://rickandmortyapi.com/api/episode/30", + "https://rickandmortyapi.com/api/episode/31", + "https://rickandmortyapi.com/api/episode/32", + "https://rickandmortyapi.com/api/episode/33", + "https://rickandmortyapi.com/api/episode/34", + "https://rickandmortyapi.com/api/episode/35", + "https://rickandmortyapi.com/api/episode/36", + "https://rickandmortyapi.com/api/episode/38", + "https://rickandmortyapi.com/api/episode/39", + "https://rickandmortyapi.com/api/episode/40", + "https://rickandmortyapi.com/api/episode/41", + "https://rickandmortyapi.com/api/episode/42", + "https://rickandmortyapi.com/api/episode/43", + "https://rickandmortyapi.com/api/episode/44", + "https://rickandmortyapi.com/api/episode/45", + "https://rickandmortyapi.com/api/episode/46", + "https://rickandmortyapi.com/api/episode/47", + "https://rickandmortyapi.com/api/episode/48", + "https://rickandmortyapi.com/api/episode/49", + "https://rickandmortyapi.com/api/episode/51" + ], + "url": "https://rickandmortyapi.com/api/character/3", + "created": "2017-11-04T19:09:56.428Z" + }, + { + "id": 4, + "name": "Beth Smith", + "status": "Alive", + "species": "Human", + "type": "", + "gender": "Female", + "origin": { + "name": "Earth (Replacement Dimension)", + "url": "https://rickandmortyapi.com/api/location/20" + }, + "location": { + "name": "Earth (Replacement Dimension)", + "url": "https://rickandmortyapi.com/api/location/20" + }, + "image": "https://rickandmortyapi.com/api/character/avatar/4.jpeg", + "episode": [ + "https://rickandmortyapi.com/api/episode/6", + "https://rickandmortyapi.com/api/episode/7", + "https://rickandmortyapi.com/api/episode/8", + "https://rickandmortyapi.com/api/episode/9", + "https://rickandmortyapi.com/api/episode/10", + "https://rickandmortyapi.com/api/episode/11", + "https://rickandmortyapi.com/api/episode/12", + "https://rickandmortyapi.com/api/episode/14", + "https://rickandmortyapi.com/api/episode/15", + "https://rickandmortyapi.com/api/episode/16", + "https://rickandmortyapi.com/api/episode/18", + "https://rickandmortyapi.com/api/episode/19", + "https://rickandmortyapi.com/api/episode/20", + "https://rickandmortyapi.com/api/episode/21", + "https://rickandmortyapi.com/api/episode/22", + "https://rickandmortyapi.com/api/episode/23", + "https://rickandmortyapi.com/api/episode/24", + "https://rickandmortyapi.com/api/episode/25", + "https://rickandmortyapi.com/api/episode/26", + "https://rickandmortyapi.com/api/episode/27", + "https://rickandmortyapi.com/api/episode/28", + "https://rickandmortyapi.com/api/episode/29", + "https://rickandmortyapi.com/api/episode/30", + "https://rickandmortyapi.com/api/episode/31", + "https://rickandmortyapi.com/api/episode/32", + "https://rickandmortyapi.com/api/episode/33", + "https://rickandmortyapi.com/api/episode/34", + "https://rickandmortyapi.com/api/episode/35", + "https://rickandmortyapi.com/api/episode/36", + "https://rickandmortyapi.com/api/episode/38", + "https://rickandmortyapi.com/api/episode/39", + "https://rickandmortyapi.com/api/episode/40", + "https://rickandmortyapi.com/api/episode/41", + "https://rickandmortyapi.com/api/episode/42", + "https://rickandmortyapi.com/api/episode/43", + "https://rickandmortyapi.com/api/episode/44", + "https://rickandmortyapi.com/api/episode/45", + "https://rickandmortyapi.com/api/episode/46", + "https://rickandmortyapi.com/api/episode/47", + "https://rickandmortyapi.com/api/episode/48", + "https://rickandmortyapi.com/api/episode/49", + "https://rickandmortyapi.com/api/episode/51" + ], + "url": "https://rickandmortyapi.com/api/character/4", + "created": "2017-11-04T19:22:43.665Z" + }, + { + "id": 5, + "name": "Jerry Smith", + "status": "Alive", + "species": "Human", + "type": "", + "gender": "Male", + "origin": { + "name": "Earth (Replacement Dimension)", + "url": "https://rickandmortyapi.com/api/location/20" + }, + "location": { + "name": "Earth (Replacement Dimension)", + "url": "https://rickandmortyapi.com/api/location/20" + }, + "image": "https://rickandmortyapi.com/api/character/avatar/5.jpeg", + "episode": [ + "https://rickandmortyapi.com/api/episode/6", + "https://rickandmortyapi.com/api/episode/7", + "https://rickandmortyapi.com/api/episode/8", + "https://rickandmortyapi.com/api/episode/9", + "https://rickandmortyapi.com/api/episode/10", + "https://rickandmortyapi.com/api/episode/11", + "https://rickandmortyapi.com/api/episode/12", + "https://rickandmortyapi.com/api/episode/13", + "https://rickandmortyapi.com/api/episode/14", + "https://rickandmortyapi.com/api/episode/15", + "https://rickandmortyapi.com/api/episode/16", + "https://rickandmortyapi.com/api/episode/18", + "https://rickandmortyapi.com/api/episode/19", + "https://rickandmortyapi.com/api/episode/20", + "https://rickandmortyapi.com/api/episode/21", + "https://rickandmortyapi.com/api/episode/22", + "https://rickandmortyapi.com/api/episode/23", + "https://rickandmortyapi.com/api/episode/26", + "https://rickandmortyapi.com/api/episode/29", + "https://rickandmortyapi.com/api/episode/30", + "https://rickandmortyapi.com/api/episode/31", + "https://rickandmortyapi.com/api/episode/32", + "https://rickandmortyapi.com/api/episode/33", + "https://rickandmortyapi.com/api/episode/35", + "https://rickandmortyapi.com/api/episode/36", + "https://rickandmortyapi.com/api/episode/38", + "https://rickandmortyapi.com/api/episode/39", + "https://rickandmortyapi.com/api/episode/40", + "https://rickandmortyapi.com/api/episode/41", + "https://rickandmortyapi.com/api/episode/42", + "https://rickandmortyapi.com/api/episode/43", + "https://rickandmortyapi.com/api/episode/44", + "https://rickandmortyapi.com/api/episode/45", + "https://rickandmortyapi.com/api/episode/46", + "https://rickandmortyapi.com/api/episode/47", + "https://rickandmortyapi.com/api/episode/48", + "https://rickandmortyapi.com/api/episode/49", + "https://rickandmortyapi.com/api/episode/50", + "https://rickandmortyapi.com/api/episode/51" + ], + "url": "https://rickandmortyapi.com/api/character/5", + "created": "2017-11-04T19:26:56.301Z" + }]) + ); + }) +]; diff --git a/sample/remote-module/package.json b/sample/remote-module/package.json index 710f2b848..7f82d73c0 100644 --- a/sample/remote-module/package.json +++ b/sample/remote-module/package.json @@ -8,13 +8,16 @@ "type": "module", "scripts": { "dev": "nodemon", - "dev-local": "cross-env LOCAL=true nodemon", + "dev-msw": "cross-env USE_MSW=true pnpm dev", + "dev-local": "cross-env LOCAL=true pnpm dev", + "dev-local-msw": "cross-env USE_MSW=true pnpm dev-local", "build": "webpack --config webpack.build.js", "serve-build": "pnpm build && pnpm http-server dist -p 8081 -P http://localhost:8081? -c-1" }, "devDependencies": { "@swc/core": "1.3.86", "@swc/helpers": "0.5.2", + "@tanstack/react-query": "rc", "@types/react": "18.2.22", "@types/react-dom": "18.2.7", "@types/webpack": "5.28.2", @@ -26,6 +29,7 @@ "browserslist": "4.21.10", "cross-env": "7.0.3", "http-server": "14.1.1", + "msw": "1.3.2", "nodemon": "3.0.1", "typescript": "5.2.2", "webpack": "5.88.2", @@ -36,6 +40,7 @@ "@sample/shared": "workspace:*", "@sample/shell": "workspace:*", "@squide/fakes": "workspace:*", + "@squide/msw": "workspace:*", "@squide/react-router": "workspace:*", "@squide/webpack-module-federation": "workspace:*", "react": "18.2.0", diff --git a/sample/remote-module/public/favicon.png b/sample/remote-module/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..47157f6b014741fca15dd80db4939ee1e1098175 GIT binary patch literal 31428 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
?A9r6C2}zB4mk zSHJe)zm>JE#`EE!n4|;SUuqD`q67%cqEOWKR{7psS;XJHT4i;PcVxQW zRt#4$TZ1oeRA6TnLvO=Yqead+T4C10)>IOZl1=wdnb%)9+KD=XEeV)1x&(la;GWM%pIPsrYs0Bgs-MSJR4TWueG)_-X%64N z%}K=WGQ@?wxc;&IXY}EZ;QHSgE~N07*i@{0)h9YzgZQ_JuwhJtOi3H-DbFq*t4)4# zp4)bP^=#C+IOiB75(R+LkGV0{AN|k%)&F?$`hId1`~K)q&*i%$e*?yv$?*sBe=Tnk zDH`$apHLuAyge`I`P9fUKDh3QFDvgskil2(?crLTU>ai*_U7D7^s@VK%+s6$+vNGC zJ%2q^9NEdx*z4QQtgU}67%pdmDO_{EewjRqndx?feKkpy%}=Oga5EfsFXO-ajC*X_ zCYxP6=B2Q(IBPOZ`Fx@=wnbXZ4zh*<(w{S5j`!;4LG^!a(Lb0cS17=?E5vHPAtw8n zTV9~YN3(MuOlBFV)!A;K^anjQqSEfV==V0vIC!G30{!T#kzb6t7dw*hbV*p7+y7>_ zcMKp$;U>Jeiu`OaeC~z(NSjNY{VXJ(vd`ih`!o@CwyfRm=JaT&?&b`^EJ-VO? zJ@JuMc>$VQbk@7)g(-3$Ghr&2Cj;QVqt88Y5`)F9veuASakX4tXn{(?^2N#oml3$0 z%f7z{5}GB^g+7@ly1I&nQj-1_+$sHPz!X4LK0@gMn3D(k%gp&3JMw~N6ud4kdG`fAfR5TFC8!}MdSB4@e^}8-l*bzCV~lnC8O_-`Ou{PcD+Gh|au2 z(YsG3lD9nvuq~go+)F#%|C3EHva-c?9VnJO6sN-dNf@k(IE3Pisc5yvxUM;_Z9F5# zEGa#!Z+9x&s|6nWnv%a~Cyp;Um48IgLvxaIzk_1S^Rtgtj`3O%yBwG~G_-q9%-v}E zs5@T9Lz7p0yiQJeG(!qWJr|a+KnwC9aE=-s2aV~`WqzH8Y@3h!1+2DRRd}qf mxo^y^-Z6)u1aY{6#-0B28$;=-+&0)_K +
diff --git a/sample/remote-module/public/mockServiceWorker.js b/sample/remote-module/public/mockServiceWorker.js new file mode 100644 index 000000000..8ee70b3e4 --- /dev/null +++ b/sample/remote-module/public/mockServiceWorker.js @@ -0,0 +1,303 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker (1.2.2). + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + * - Please do NOT serve this file on production. + */ + +const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70' +const activeClientIds = new Set() + +self.addEventListener('install', function () { + self.skipWaiting() +}) + +self.addEventListener('activate', function (event) { + event.waitUntil(self.clients.claim()) +}) + +self.addEventListener('message', async function (event) { + const clientId = event.source.id + + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: INTEGRITY_CHECKSUM, + }) + break + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: true, + }) + break + } + + case 'MOCK_DEACTIVATE': { + activeClientIds.delete(clientId) + break + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break + } + } +}) + +self.addEventListener('fetch', function (event) { + const { request } = event + const accept = request.headers.get('accept') || '' + + // Bypass server-sent events. + if (accept.includes('text/event-stream')) { + return + } + + // Bypass navigation requests. + if (request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + // Generate unique request ID. + const requestId = Math.random().toString(16).slice(2) + + event.respondWith( + handleRequest(event, requestId).catch((error) => { + if (error.name === 'NetworkError') { + console.warn( + '[MSW] Successfully emulated a network error for the "%s %s" request.', + request.method, + request.url, + ) + return + } + + // At this point, any exception indicates an issue with the original request/response. + console.error( + `\ +[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`, + request.method, + request.url, + `${error.name}: ${error.message}`, + ) + }), + ) +}) + +async function handleRequest(event, requestId) { + const client = await resolveMainClient(event) + const response = await getResponse(event, client, requestId) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + ;(async function () { + const clonedResponse = response.clone() + sendToClient(client, { + type: 'RESPONSE', + payload: { + requestId, + type: clonedResponse.type, + ok: clonedResponse.ok, + status: clonedResponse.status, + statusText: clonedResponse.statusText, + body: + clonedResponse.body === null ? null : await clonedResponse.text(), + headers: Object.fromEntries(clonedResponse.headers.entries()), + redirected: clonedResponse.redirected, + }, + }) + })() + } + + return response +} + +// Resolve the main client for the given event. +// Client that issues a request doesn't necessarily equal the client +// that registered the worker. It's with the latter the worker should +// communicate with during the response resolving phase. +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (client?.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + +async function getResponse(event, client, requestId) { + const { request } = event + const clonedRequest = request.clone() + + function passthrough() { + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const headers = Object.fromEntries(clonedRequest.headers.entries()) + + // Remove MSW-specific request headers so the bypassed requests + // comply with the server's CORS preflight check. + // Operate with the headers as an object because request "Headers" + // are immutable. + delete headers['x-msw-bypass'] + + return fetch(clonedRequest, { headers }) + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough() + } + + // Bypass requests with the explicit bypass header. + // Such requests can be issued by "ctx.fetch()". + if (request.headers.get('x-msw-bypass') === 'true') { + return passthrough() + } + + // Notify the client that a request has been intercepted. + const clientMessage = await sendToClient(client, { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + mode: request.mode, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: await request.text(), + bodyUsed: request.bodyUsed, + keepalive: request.keepalive, + }, + }) + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data) + } + + case 'MOCK_NOT_FOUND': { + return passthrough() + } + + case 'NETWORK_ERROR': { + const { name, message } = clientMessage.data + const networkError = new Error(message) + networkError.name = name + + // Rejecting a "respondWith" promise emulates a network error. + throw networkError + } + } + + return passthrough() +} + +function sendToClient(client, message) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } + + resolve(event.data) + } + + client.postMessage(message, [channel.port2]) + }) +} + +function sleep(timeMs) { + return new Promise((resolve) => { + setTimeout(resolve, timeMs) + }) +} + +async function respondWithMock(response) { + await sleep(response.delay) + return new Response(response.body, response) +} diff --git a/sample/remote-module/src/dev/index.tsx b/sample/remote-module/src/dev/index.tsx index 046ff94fa..4ad5ec39a 100644 --- a/sample/remote-module/src/dev/index.tsx +++ b/sample/remote-module/src/dev/index.tsx @@ -1,3 +1,4 @@ +import { MswPlugin } from "@squide/msw"; import { ConsoleLogger, Runtime, RuntimeContext, registerLocalModules } from "@squide/react-router"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; @@ -6,9 +7,12 @@ import { App } from "./App.tsx"; import { registerTabsPage } from "./registerTabsPage.tsx"; import { sessionAccessor } from "./session.ts"; +const mswPlugin = new MswPlugin(); + // Create the shell runtime. // Services, loggers and sessionAccessor could be reuse through a shared packages or faked when in isolation. const runtime = new Runtime({ + plugins: [mswPlugin], loggers: [new ConsoleLogger()], sessionAccessor }); @@ -17,6 +21,14 @@ const runtime = new Runtime({ // is local when developing in isolation. registerLocalModules([register, registerTabsPage], runtime); +// Register MSW after the local modules has been registered since the request handlers +// will be registered by the modules. +if (process.env.USE_MSW) { + import("../../mocks/browser.ts").then(({ startMsw }) => { + startMsw(mswPlugin.requestHandlers); + }); +} + const root = createRoot(document.getElementById("root")!); root.render( diff --git a/sample/remote-module/src/register.tsx b/sample/remote-module/src/register.tsx index fc1b58ae5..d68934d90 100644 --- a/sample/remote-module/src/register.tsx +++ b/sample/remote-module/src/register.tsx @@ -1,6 +1,8 @@ import { BackgroundColorContext } from "@sample/shared"; +import { getMswPlugin } from "@squide/msw"; import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { lazy } from "react"; +import { requestHandlers } from "../mocks/handlers.ts"; const CustomLayout = lazy(() => import("./CustomLayout.tsx")); const Remote = lazy(() => import("./Remote.tsx")); @@ -89,7 +91,7 @@ export const register: ModuleRegisterFunction = runtime => { } ]); - /////// + // Register federated tabs. runtime.registerRoutes([ { @@ -113,4 +115,10 @@ export const register: ModuleRegisterFunction = runtime => { priority: 999 } ], { menuId: "/federated-tabs" }); + + // Register request handlers for MSW. + + const mswPlugin = getMswPlugin(runtime.plugins); + + mswPlugin.registerRequestHandlers(requestHandlers); }; diff --git a/sample/remote-module/webpack.dev.js b/sample/remote-module/webpack.dev.js index 4e29997c1..328ff5927 100644 --- a/sample/remote-module/webpack.dev.js +++ b/sample/remote-module/webpack.dev.js @@ -15,12 +15,16 @@ if (!process.env.LOCAL) { } }, environmentVariables: { - "NETLIFY": process.env.NETLIFY === "true" + "NETLIFY": process.env.NETLIFY === "true", + "USE_MSW": process.env.USE_MSW === "true" } }); } else { config = defineDevConfig(swcConfig, { - entry: path.resolve("./src/dev/index.tsx") + entry: path.resolve("./src/dev/index.tsx"), + environmentVariables: { + "USE_MSW": process.env.USE_MSW === "true" + } }); } From 553f70d8ac608271021118c56428b65b4e84fb4c Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Wed, 4 Oct 2023 13:06:19 -0400 Subject: [PATCH 02/51] Updated the sample to use React Router lazy --- docs/getting-started/learn-the-api.md | 81 +++++++++++++--- docs/guides/add-authentication.md | 17 +++- docs/guides/override-the-host-layout.md | 7 +- docs/reference/default.md | 8 ++ docs/reference/fakes/index.yaml | 2 +- docs/reference/logging/Logger.md | 11 +-- docs/reference/packages.md | 1 + docs/reference/plugins/index.yaml | 2 + docs/reference/plugins/plugin.md | 80 ++++++++++++++++ docs/reference/runtime/runtime-class.md | 28 ++++-- docs/reference/runtime/useService.md | 12 +-- docs/reference/runtime/useServices.md | 4 +- docs/reference/services/index.yaml | 2 + docs/reference/services/service.md | 94 +++++++++++++++++++ docs/reference/webpack/index.yaml | 2 +- packages/core/src/index.ts | 2 + packages/core/src/runtime/abstractRuntime.ts | 29 ++++-- packages/core/src/services/service.ts | 11 +++ packages/msw/src/mswPlugin.ts | 16 +--- packages/msw/src/requestHandlerRegistry.ts | 2 +- pnpm-lock.yaml | 51 ++++++++-- sample/another-remote-module/package.json | 3 + sample/another-remote-module/src/dev/App.tsx | 5 +- .../another-remote-module/src/dev/DevHome.tsx | 2 +- sample/another-remote-module/src/register.tsx | 6 +- sample/another-remote-module/webpack.build.js | 6 ++ sample/another-remote-module/webpack.dev.js | 6 ++ sample/host/mocks/handlers.ts | 12 ++- sample/host/package.json | 2 + sample/host/src/App.tsx | 4 +- sample/host/src/bootstrap.tsx | 25 ++++- sample/host/src/session.ts | 50 ++++++++++ sample/host/tsconfig.json | 1 + sample/host/webpack.build.js | 11 ++- sample/host/webpack.dev.js | 8 ++ sample/local-module/src/About.tsx | 4 +- sample/local-module/src/Message.tsx | 4 +- sample/local-module/src/WorkleapTab.tsx | 4 +- sample/local-module/src/dev/App.tsx | 5 +- sample/local-module/src/dev/DevHome.tsx | 2 +- .../local-module/src/dev/registerTabsPage.tsx | 5 +- sample/local-module/src/register.tsx | 11 +-- sample/remote-module/package.json | 4 +- sample/remote-module/src/ColoredPage.tsx | 4 +- sample/remote-module/src/CustomLayout.tsx | 4 +- sample/remote-module/src/Fetch.tsx | 4 +- sample/remote-module/src/Hoisted.tsx | 4 +- sample/remote-module/src/OfficevibeTab.tsx | 4 +- sample/remote-module/src/Remote.tsx | 4 +- sample/remote-module/src/SkillsTab.tsx | 4 +- sample/remote-module/src/dev/App.tsx | 5 +- sample/remote-module/src/dev/DevHome.tsx | 2 +- .../src/dev/registerTabsPage.tsx | 5 +- sample/remote-module/src/register.tsx | 42 ++++----- sample/remote-module/tsconfig.json | 1 + sample/remote-module/webpack.build.js | 6 ++ sample/remote-module/webpack.dev.js | 6 ++ sample/shared/package.json | 2 + sample/shared/src/FederatedTabsLayout.tsx | 4 +- sample/shared/src/appContext.ts | 3 + sample/shell/package.json | 25 +++++ sample/shell/src/AuthenticatedLayout.tsx | 4 +- sample/shell/src/Login.tsx | 6 +- sample/shell/src/Logout.tsx | 4 +- sample/shell/src/ModuleErrorBoundary.tsx | 4 +- sample/shell/src/NoMatch.tsx | 4 +- sample/shell/src/index.ts | 5 - sample/shell/src/useAppRouter.tsx | 38 +++++--- 68 files changed, 664 insertions(+), 172 deletions(-) create mode 100644 docs/reference/plugins/index.yaml create mode 100644 docs/reference/plugins/plugin.md create mode 100644 docs/reference/services/index.yaml create mode 100644 docs/reference/services/service.md create mode 100644 packages/core/src/services/service.ts diff --git a/docs/getting-started/learn-the-api.md b/docs/getting-started/learn-the-api.md index c783bd615..a40b10b29 100644 --- a/docs/getting-started/learn-the-api.md +++ b/docs/getting-started/learn-the-api.md @@ -48,7 +48,7 @@ const logger = useLogger(); logger.debug("Hello", { world: "!" }); ``` -The logger is also available from the [Runtime](/reference/runtime/runtime-class.md) instance. +The logger is also available from the [Runtime](/reference/runtime/runtime-class.md#use-the-logger) instance. ## Messaging @@ -79,13 +79,13 @@ dispatch("foo", "bar"); You can use the event bus to enable various communication scenarios, such as notifying components of state changes, broadcasting messages across modules, or triggering actions based on specific events. -The event bus is also available from the [Runtime](/reference/runtime/runtime-class.md) instance. +The event bus is also available from the [Runtime](/reference/runtime/runtime-class.md#use-the-event-bus) instance. ## Session Most of our applications (if not all) will eventually require the user to authenticate. To facilitate this process, the Squide [Runtime](/reference/runtime/runtime-class.md) class accepts a [sessionAccessor](/reference/fakes/LocalStorageSessionManager.md#integrate-with-a-runtime-instance) function. Once the shell registration flow is completed, the function will be made accessible to every module of the application. -First, let's define a `sessionAccessor` function: +First, define a `sessionAccessor` function: ```ts host/src/session.ts import type { SessionAccessorFunction } from "@squide/react-router"; @@ -135,29 +135,84 @@ The session is also available from the [Runtime](/reference/runtime/runtime-clas While Squide provides a range of built-in functionalities, by no mean these alone can support the needs of every mature application. Therefore, the shell [Runtime](/reference/runtime/runtime-class.md) allows the addition of custom services. -First, make the service available to every part of the application by passing a service instance to the `Runtime` instance: +First, define a service by implementing the [Service](../reference/services/service.md) interface: -```ts host/src/boostrap.tsx +```ts !#3 shared/src/telemetryService.ts +import { Service } from "@squide/react-router"; + +export class TelemetryService extends Service { + constructor() { + super(TelemetryService.name) + } + + getUser(userId: string) { + ... + } +} +``` + +Then, make the service available to every part of the application by passing a service instance to the `Runtime` instance: + +```ts !#5 host/src/boostrap.tsx import { Runtime } from "@squide/react-router"; -import { UserService } from "@sample/shared"; +import { TelemetryService } from "@sample/shared"; const runtime = new Runtime({ - services: { - "user-service": new UserService() - } + services: [new TelemetryService()] }); ``` -Then, access the service instance from anywhere with the [useService](/reference/runtime/useService.md) hook: +Finally, access the service instance from anywhere with the [useService](/reference/runtime/useService.md) hook: ```ts import { useService } from "@squide/react-router"; -import { type UserService } from "@sample/shared"; +import { TelemetryService } from "@sample/shared"; + +const service = useService(TelemetryService.name) as TelemetryService; +``` + +The services are also available from the [Runtime](/reference/runtime/runtime-class.md#retrieve-a-service) instance. + +## Plugins + +To keep Squide lightweight, not all functionalities should be integrated as a core functionality. However, to accommodate a broad range of technologies, a plugin system has been implemented to fill the gap. + +First, define a plugin by implementing the [Plugin](../reference/plugins/plugin.md) interface: -const service = useService("user-service") as UserService; +```ts !#3 shared/src/mswPlugin.ts +import { Plugin } from "@squide/react-router"; +import type { RestHandler } from "msw"; + +export class MswPlugin extends Plugin { + constructor() { + super(MswPlugin.name); + } + + registerRequestHandlers(handlers: RestHandler[]) { + ... + } +} +``` + +Then, make the plugin available to every part of the application by passing a service instance to the `Runtime` instance: + +```ts !#5 host/src/boostrap.tsx +import { Runtime } from "@squide/react-router"; +import { MswPlugin } from "@squide/msw"; + +const runtime = new Runtime({ + plugins: [new MswPlugin()] +}); ``` -The services are also available from the [Runtime](/reference/runtime/runtime-class.md) instance. +Then, access the plugin instance from the [Runtime](/reference/runtime/runtime-class.md) instance: + +```ts !#4 +import { MswPlugin } from "@sample/shared"; +import { requetHandlers } from "../mocks/handlers.ts"; + +const mswPlugin = runtime.getPlugin(MswPlugin.name) as MswPlugin; +``` ## Fakes diff --git a/docs/guides/add-authentication.md b/docs/guides/add-authentication.md index 4c24f1a87..a08123467 100644 --- a/docs/guides/add-authentication.md +++ b/docs/guides/add-authentication.md @@ -10,20 +10,31 @@ When combined with a [React Router](https://reactrouter.com/en/main) authenticat ## Create a session accessor function -Define a `sessionAccessor` function wrapping a `LocalStorageSessionManager` instance: +First, create a shared type for the session: + +```ts shared/src/session.ts +export interface Session { + user: { + name: string; + }; +} +``` + +Then, define a `sessionAccessor` function wrapping a `LocalStorageSessionManager` instance: ```ts host/src/session.ts import type { SessionAccessorFunction } from "@squide/react-router"; import { LocalStorageSessionManager } from "@squide/fakes"; +import type { Session } from "@sample/shared"; export const sessionManager = new LocalStorageSessionManager(); -const sessionAccessor: SessionAccessorFunction = () => { +export const sessionAccessor: SessionAccessorFunction = () => { return sessionManager.getSession(); }; ``` -Then, create the [Runtime](/reference/runtime/runtime-class.md) instance with the new `sessionAccessor` function: +Finally, create the [Runtime](/reference/runtime/runtime-class.md) instance with the new `sessionAccessor` function: ```ts #5 host/src/boostrap.tsx import { Runtime } from "@squide/react-router"; diff --git a/docs/guides/override-the-host-layout.md b/docs/guides/override-the-host-layout.md index aa47e091f..8281b6ae7 100644 --- a/docs/guides/override-the-host-layout.md +++ b/docs/guides/override-the-host-layout.md @@ -171,10 +171,9 @@ Then, mark the pages as hoisted and optionally use a new layout: ```tsx #12-13,16,19-20 local-module/src/register.tsx import { lazy } from "react"; import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; - -const LocalLayout = lazy(() => import("./LocalLayout.tsx")); -const LocalErrorBoundary = lazy(() => import("./LocalErrorBoundary.tsx")); -const Login = lazy(() => import("./Login.tsx")); +import { LocalLayout } from "./LocalLayout.tsx"; +import { LocalErrorBoundary } from "./LocalErrorBoundary.tsx"; +import { Login } from "./Login.tsx"; export function register: ModuleRegisterFunction(runtime) { runtime.registerRoutes([ diff --git a/docs/reference/default.md b/docs/reference/default.md index 25db4f803..259028001 100644 --- a/docs/reference/default.md +++ b/docs/reference/default.md @@ -51,6 +51,14 @@ expanded: true - [useIsAuthenticated](session/useIsAuthenticated.md) +### Services + +- [Service](services/service.md) + +### Plugins + +- [Plugin](plugins/plugin.md) + ### Webpack - [defineDevHostConfig](webpack/defineDevHostConfig.md) diff --git a/docs/reference/fakes/index.yaml b/docs/reference/fakes/index.yaml index abb084c44..7f412159a 100644 --- a/docs/reference/fakes/index.yaml +++ b/docs/reference/fakes/index.yaml @@ -1 +1 @@ -order: 30 +order: 10 diff --git a/docs/reference/logging/Logger.md b/docs/reference/logging/Logger.md index 5a503521d..c1473975c 100644 --- a/docs/reference/logging/Logger.md +++ b/docs/reference/logging/Logger.md @@ -1,18 +1,15 @@ ---- -toc: - depth: 2-3 ---- - # Logger -A basic logger interface. +An abstract base class to define a logger. ## Usage +### Define a custom logger + ```ts import { Logger } from "@squide/react-router"; -class CustomLogger: Logger { +export class CustomLogger: Logger { debug(log) { ... } information(log) { ... } warning(log) { ... } diff --git a/docs/reference/packages.md b/docs/reference/packages.md index dcc53c44e..c8bddba83 100644 --- a/docs/reference/packages.md +++ b/docs/reference/packages.md @@ -19,4 +19,5 @@ order: 200 | :icon-mark-github: [@squide/core](https://github.com/gsoft-inc/wl-squide/tree/main/packages/core) | Core functionalities of Squide. | [![npm version](https://img.shields.io/npm/v/@squide/core)](https://www.npmjs.com/package/@squide/core) | | :icon-mark-github: [@squide/react-router](https://github.com/gsoft-inc/wl-squide/tree/main/packages/react-router) | Specific implementation of the core functionalities to support [React Router](https://reactrouter.com/en/main). | [![npm version](https://img.shields.io/npm/v/@squide/react-router)](https://www.npmjs.com/package/@squide/react-router) | | :icon-mark-github: [@squide/webpack-module-federation](https://github.com/gsoft-inc/wl-squide/tree/main/packages/webpack-module-federation) | Add support for [Module Federation](https://webpack.js.org/concepts/module-federation/). | [![npm version](https://img.shields.io/npm/v/@squide/webpack-module-federation)](https://www.npmjs.com/package/@squide/webpack-module-federation) | +| :icon-mark-github: [@squide/msw](https://github.com/gsoft-inc/wl-squide/tree/main/packages/msw) | Add support for [MSW](https://mswjs.io/). | [![npm version](https://img.shields.io/npm/v/@squide/msw)](https://www.npmjs.com/package/@squide/msw) | | :icon-mark-github: [@squide/fakes](https://github.com/gsoft-inc/wl-squide/tree/main/packages/fakes) | A collection of fake implementations to facilitate the development of federated modules. | [![npm version](https://img.shields.io/npm/v/@squide/fakes)](https://www.npmjs.com/package/@squide/fakes) | diff --git a/docs/reference/plugins/index.yaml b/docs/reference/plugins/index.yaml new file mode 100644 index 000000000..8149a5c67 --- /dev/null +++ b/docs/reference/plugins/index.yaml @@ -0,0 +1,2 @@ +order: 20 +label: "Plugins" diff --git a/docs/reference/plugins/plugin.md b/docs/reference/plugins/plugin.md new file mode 100644 index 000000000..e8369e4a7 --- /dev/null +++ b/docs/reference/plugins/plugin.md @@ -0,0 +1,80 @@ +--- +toc: + depth: 2-3 +--- + +# Plugin + +An abstract base class to define a plugin. + +## Usage + +### Define a plugin + +```ts !#3 shared/src/mswPlugin.ts +import { Plugin } from "@squide/react-router"; +import type { RestHandler } from "msw"; + +export class MswPlugin extends Plugin { + constructor() { + super(MswPlugin.name); + } + + registerRequestHandlers(handlers: RestHandler[]) { + ... + } +} +``` + +### Register a plugin + +```ts !#5 +import { Runtime } from "@squide/react-router"; +import { MswPlugin } from "@squide/msw"; + +const runtime = new Runtime({ + plugins: [new MswPlugin()] +}); +``` + +### Retrieve a plugin from a runtime instance + +```ts !#4 +import { MswPlugin } from "@sample/shared"; +import { requetHandlers } from "../mocks/handlers.ts"; + +const mswPlugin = runtime.getPlugin(MswPlugin.name) as MswPlugin; + +mswPlugin.registerRequestHandlers(requetHandlers); +``` + +### Retrieve a plugin with a custom function + +We recommend pairing a plugin definition with a custom function to retrieve the plugin from a runtime instance. + +```ts !#3 shared/src/mswPlugin.ts +import { Plugin, type Runtime } from "@squide/react-router"; +import type { RestHandler } from "msw"; + +export class MswPlugin extends Plugin { + constructor() { + super(MswPlugin.name); + } + + registerRequestHandlers(handlers: RestHandler[]) { + ... + } +} + +export function getMswPlugin(runtime: Runtime) { + return runtime.getPlugin(MswPlugin.name); +} +``` + +```ts +import { getMswPlugin } from "@sample/shared"; + +const mswPlugin = getMswPlugin(runtime); +``` + +Retrieving a plugin with a custom function doesn't require the consumer to remember the plugin name, and has the upside of inferring the typings. diff --git a/docs/reference/runtime/runtime-class.md b/docs/reference/runtime/runtime-class.md index 4d8b68d8a..38da37716 100644 --- a/docs/reference/runtime/runtime-class.md +++ b/docs/reference/runtime/runtime-class.md @@ -11,7 +11,7 @@ A runtime instance give modules access to functionalities such as routing, navig ## Reference ```ts -const runtime = new Runtime(options?: { loggers?: [], services?: {}, sessionAccessor?: () => {} }) +const runtime = new Runtime(options?: { loggers?: [], services?: [], plugins?: [], sessionAccessor?: () => {} }) ``` ### Parameters @@ -19,7 +19,8 @@ const runtime = new Runtime(options?: { loggers?: [], services?: {}, sessionAcce - `options`: An optional object literal of options: - `mode`: An optional mode to optimize Squide for `production`. Values are `"development"` (default) and `"production"`. - `loggers`: An optional array of `Logger` instances. - - `services`: An optional string-keyed object literal of custom service instances. + - `services`: An optional array of custom service instances. + - `plugins`: An optional array of custom plugin instances. - `sessionAccessor`: An optional function returning the current session. ## Usage @@ -29,15 +30,15 @@ const runtime = new Runtime(options?: { loggers?: [], services?: {}, sessionAcce ```ts import { ConsoleLogger, Runtime } from "@squide/react-router"; import { LocalStorageSessionManager } from "@squide/fakes"; -import { UserService, type UserService, type AppSession } from "@sample/shared"; +import { MswPlugin } from "@squide/msw"; +import { TelemetryService, type AppSession } from "@sample/shared"; const sessionManager = new LocalStorageSessionManager(); const runtime = new Runtime({ loggers: [new ConsoleLogger()], - services: { - "user-service": new UserService() - }, + services: [new TelemetryService()], + plugins: [new MswPlugin()], sessionAccessor: () => { return sessionManager.getSession(); }; @@ -356,10 +357,21 @@ runtime.eventBus.dispatch("write-to-host", "Hello host!"); ### Retrieve a service ```ts -// If the service isn't registered, undefined will be returned. -const service = runtime.getService("user-service") as UserService; +// If the service isn't registered, an exception will be thrown. +const service = runtime.getService(TelemetryService.name) as TelemetryService; ``` +[!ref Learn more about services](../services/service.md) + +### Retrieve a plugin + +```ts +// If the plugin isn't registered, an exception will be thrown. +const plugin = runtime.getPlugin(MswPlugin.name) as MswPlugin; +``` + +[!ref Learn more about plugins](../plugins/plugin.md) + ### Retrieve the current session ```ts diff --git a/docs/reference/runtime/useService.md b/docs/reference/runtime/useService.md index 11dc9976f..4dcb08861 100644 --- a/docs/reference/runtime/useService.md +++ b/docs/reference/runtime/useService.md @@ -5,7 +5,7 @@ toc: # useService -Retrieve a custom service from the `Runtime` instance provided by `RuntimeContext`. +Retrieve a custom service instance from the `Runtime` instance provided by `RuntimeContext`. ## Reference @@ -15,19 +15,17 @@ const service = useService(name) ### Parameters -- `name`: A custom service instance name. +- `name`: A custom service name. ### Returns -A service instance or undefined if the specified service name doesn't match any registered instance. +A service instance or throw an error if the specified service name doesn't match any registered instance. ## Usage ```ts import { useService } from "@squide/react-router"; -import type { UserService } from "@sample/shared"; +import { TelemetryService } from "@sample/shared"; -const userService = useService("use-service") as UserService; - -const users = userService.fetchAll(); +const telemetryService = useService(TelemetryService.name) as TelemetryService; ``` diff --git a/docs/reference/runtime/useServices.md b/docs/reference/runtime/useServices.md index c0d1fee38..debf308b1 100644 --- a/docs/reference/runtime/useServices.md +++ b/docs/reference/runtime/useServices.md @@ -5,7 +5,7 @@ toc: # useServices -Retrieve a string-keyed object literal of custom service instances from the `Runtime` instance provided by `RuntimeContext`. +Retrieve an array of custom service instances from the `Runtime` instance provided by `RuntimeContext`. ## Reference @@ -19,7 +19,7 @@ None ### Returns -A string-keyed object literal of custom service instances. +An array of custom service instances. ## Usage diff --git a/docs/reference/services/index.yaml b/docs/reference/services/index.yaml new file mode 100644 index 000000000..9f632f243 --- /dev/null +++ b/docs/reference/services/index.yaml @@ -0,0 +1,2 @@ +order: 30 +label: "Services" diff --git a/docs/reference/services/service.md b/docs/reference/services/service.md new file mode 100644 index 000000000..04e525c98 --- /dev/null +++ b/docs/reference/services/service.md @@ -0,0 +1,94 @@ +--- +toc: + depth: 2-3 +--- + +# Service + +An abstract base class to define a service. + +## Usage + +### Define a custom service + +```ts !#3 shared/src/telemetryService.ts +import { Service } from "@squide/core"; + +export class TelemetryService extends Service { + constructor() { + super(TelemetryService.name); + } +} +``` + +### Register a service + +```ts !#4 host/src/boostrap.tsx +import { TelemetryService } from "@sample/shared"; + +const runtime = new Runtime({ + services: [new TelemetryService()] +}); +``` + +### Retrieve a service from a runtime instance + +```ts +import { TelemetryService } from "@sample/shared"; + +const telemetryService = runtime.getService(TelemetryService.name); +``` + +### Retrieve a service with the `useService` hook + +```ts +import { useService } from "@squide/react-router"; +import { TelemetryService } from "@sample/shared"; + +const telemetryService = runtime.getService(TelemetryService.name); +``` + +### Retrieve a service with a custom function or a hook + +We recommend pairing a custom service definition with a custom function or hook to retrieve the service from a runtime instance. + +```ts !#9-11,13-15 shared/src/telemetryService.ts +import { Service, useRuntime, type Runtime } from "@squide/react-router"; + +export class TelemetryService extends Service { + constructor() { + super(TelemetryService.name); + } +} + +export function getTelemetryService(runtime: Runtime) { + return runtime.getService(TelemetryService.name) as TelemetryService; +} + +export function useTelemetryService() { + const runtime = useRuntime(); + + return runtime.getService(TelemetryService.name) as TelemetryService; +} +``` + +```ts +import { getTelemetryService } from "@sample/shared"; + +const telemetryService = getTelemetryService(runtime); +``` + +```ts +import { useTelemetryService } from "@sample/shared"; + +const telemetryService = useTelemetryService(); +``` + +Retrieving a service with a custom function or hook doesn't require the consumer to remember the service name, and has the upside of inferring the typings. + + + + + + + diff --git a/docs/reference/webpack/index.yaml b/docs/reference/webpack/index.yaml index b96ddaf94..4557f02a7 100644 --- a/docs/reference/webpack/index.yaml +++ b/docs/reference/webpack/index.yaml @@ -1,2 +1,2 @@ -order: 30 +order: 0 label: "webpack" diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 61cae374d..9fbd49df7 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -24,3 +24,5 @@ export * from "./federation/registerModule.ts"; export * from "./plugins/plugin.ts"; +export * from "./services/service.ts"; + diff --git a/packages/core/src/runtime/abstractRuntime.ts b/packages/core/src/runtime/abstractRuntime.ts index 85f62876e..000e0fe1a 100644 --- a/packages/core/src/runtime/abstractRuntime.ts +++ b/packages/core/src/runtime/abstractRuntime.ts @@ -1,6 +1,7 @@ import type { Logger } from "../logging/logger.ts"; import { EventBus } from "../messaging/eventBus.ts"; import type { Plugin } from "../plugins/plugin.ts"; +import type { Service } from "../services/service.ts"; import { RuntimeLogger } from "./RuntimeLogger.ts"; export type SessionAccessorFunction = () => unknown; @@ -9,9 +10,9 @@ export type RuntimeMode = "development" | "production"; export interface RuntimeOptions { mode?: RuntimeMode; - plugins?: Plugin[]; loggers?: Logger[]; - services?: Record; + services?: Service[]; + plugins?: Plugin[]; sessionAccessor?: SessionAccessorFunction; } @@ -27,13 +28,13 @@ export const RootMenuId = "root"; export abstract class AbstractRuntime { protected _mode: RuntimeMode; - protected readonly _plugins: Plugin[]; protected readonly _logger: RuntimeLogger; protected readonly _eventBus: EventBus; - protected _services: Record; + protected readonly _services: Service[]; + protected readonly _plugins: Plugin[]; protected _sessionAccessor?: SessionAccessorFunction; - constructor({ mode = "development", plugins = [], loggers, services = {}, sessionAccessor }: RuntimeOptions = {}) { + constructor({ mode = "development", loggers, services = [], plugins = [], sessionAccessor }: RuntimeOptions = {}) { this._mode = mode; this._plugins = plugins; this._logger = new RuntimeLogger(loggers); @@ -58,6 +59,16 @@ export abstract class AbstractRuntime x.name === pluginName); + + if (!plugin) { + throw new Error(`[squide] Cannot find a plugin named "${pluginName}". Did you add an instance of the plugin to the application Runtime instance?`); + } + + return plugin; + } + get logger() { return this._logger; } @@ -71,7 +82,13 @@ export abstract class AbstractRuntime x.name === serviceName); + + if (!service) { + throw new Error(`[squide] Cannot find a service named "${serviceName}". Did you add the service to the application Runtime instance?`); + } + + return service; } getSession() { diff --git a/packages/core/src/services/service.ts b/packages/core/src/services/service.ts new file mode 100644 index 000000000..b41c2abd9 --- /dev/null +++ b/packages/core/src/services/service.ts @@ -0,0 +1,11 @@ +export abstract class Service { + protected readonly _name: string; + + constructor(name: string) { + this._name = name; + } + + get name() { + return this._name; + } +} diff --git a/packages/msw/src/mswPlugin.ts b/packages/msw/src/mswPlugin.ts index 67d866bf4..3a5108bb1 100644 --- a/packages/msw/src/mswPlugin.ts +++ b/packages/msw/src/mswPlugin.ts @@ -1,14 +1,12 @@ -import { Plugin } from "@squide/core"; +import { Plugin, type AbstractRuntime } from "@squide/core"; import type { RestHandler } from "msw"; import { RequestHandlerRegistry } from "./requestHandlerRegistry.ts"; -const Name = "MswPlugin"; - export class MswPlugin extends Plugin { readonly #requestHandlerRegistry = new RequestHandlerRegistry(); constructor() { - super(Name); + super(MswPlugin.name); } registerRequestHandlers(handlers: RestHandler[]) { @@ -20,12 +18,6 @@ export class MswPlugin extends Plugin { } } -export function getMswPlugin(plugins: Plugin[]) { - const plugin = plugins.find(x => x.name === Name); - - if (!plugin) { - throw new Error("[squide] Cannot find MSW plugin. Did you added an instance of the MswPlugin class to your application Runtime?"); - } - - return plugin as MswPlugin; +export function getMswPlugin(runtime: AbstractRuntime) { + return runtime.getPlugin(MswPlugin.name) as MswPlugin; } diff --git a/packages/msw/src/requestHandlerRegistry.ts b/packages/msw/src/requestHandlerRegistry.ts index 7c7f5c7fb..1b70f21d0 100644 --- a/packages/msw/src/requestHandlerRegistry.ts +++ b/packages/msw/src/requestHandlerRegistry.ts @@ -1,4 +1,4 @@ -import { type RestHandler } from "msw"; +import type { RestHandler } from "msw"; export class RequestHandlerRegistry { readonly #handlers: RestHandler[] = []; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ce807ae20..d418a80a1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -295,6 +295,12 @@ importers: '@squide/webpack-module-federation': specifier: workspace:* version: link:../../packages/webpack-module-federation + '@tanstack/react-query': + specifier: rc + version: 5.0.0-rc.4(react-dom@18.2.0)(react@18.2.0) + axios: + specifier: 1.5.1 + version: 1.5.1(debug@4.3.4) react: specifier: 18.2.0 version: 18.2.0 @@ -311,6 +317,9 @@ importers: '@swc/helpers': specifier: 0.5.2 version: 0.5.2 + '@tanstack/react-query-devtools': + specifier: rc + version: 5.0.0-rc.4(@tanstack/react-query@5.0.0-rc.4)(react-dom@18.2.0)(react@18.2.0) '@types/react': specifier: 18.2.22 version: 18.2.22 @@ -383,6 +392,9 @@ importers: '@tanstack/react-query': specifier: rc version: 5.0.0-rc.4(react-dom@18.2.0)(react@18.2.0) + axios: + specifier: 1.5.1 + version: 1.5.1(debug@4.3.4) react: specifier: 18.2.0 version: 18.2.0 @@ -399,6 +411,9 @@ importers: '@swc/helpers': specifier: 0.5.2 version: 0.5.2 + '@tanstack/react-query-devtools': + specifier: rc + version: 5.0.0-rc.4(@tanstack/react-query@5.0.0-rc.4)(react-dom@18.2.0)(react@18.2.0) '@types/react': specifier: 18.2.22 version: 18.2.22 @@ -556,6 +571,12 @@ importers: '@squide/webpack-module-federation': specifier: workspace:* version: link:../../packages/webpack-module-federation + '@tanstack/react-query': + specifier: rc + version: 5.0.0-rc.4(react-dom@18.2.0)(react@18.2.0) + axios: + specifier: 1.5.1 + version: 1.5.1(debug@4.3.4) react: specifier: 18.2.0 version: 18.2.0 @@ -572,9 +593,9 @@ importers: '@swc/helpers': specifier: 0.5.2 version: 0.5.2 - '@tanstack/react-query': + '@tanstack/react-query-devtools': specifier: rc - version: 5.0.0-rc.4(react-dom@18.2.0)(react@18.2.0) + version: 5.0.0-rc.4(@tanstack/react-query@5.0.0-rc.4)(react-dom@18.2.0)(react@18.2.0) '@types/react': specifier: 18.2.22 version: 18.2.22 @@ -635,6 +656,9 @@ importers: '@squide/react-router': specifier: workspace:* version: link:../../packages/react-router + '@tanstack/react-query': + specifier: rc + version: 5.0.0-rc.4(react-dom@18.2.0)(react@18.2.0) '@types/react': specifier: 18.2.22 version: 18.2.22 @@ -4892,6 +4916,23 @@ packages: /@tanstack/query-core@5.0.0-rc.4: resolution: {integrity: sha512-d5QHSpR+kmutkJfXmyaiNvchVQctuY3d3vWBbAS4M3JTEQuOt+Gg2v7oghnDG05Ks7M0pq7ofNFuAJZ3s7p1yg==} + /@tanstack/query-devtools@5.0.0-rc.1: + resolution: {integrity: sha512-33ECTglWAllVT6IfUhqvgKbJulOtOP8+z9W/i0FlmWKNaEwzaR8lpa4pOCyLCZA2p5XW97nSJ/JfPOzeYQN/jQ==} + dev: true + + /@tanstack/react-query-devtools@5.0.0-rc.4(@tanstack/react-query@5.0.0-rc.4)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-ILdxaONmNW7hWC6S9oL99NhrBbK/Iv8juA7O3JhtdwF/DxDnnt/KihhzalRNtVLC8P/E2k5fDfwV4r/ofJ2QGQ==} + peerDependencies: + '@tanstack/react-query': ^5.0.0-rc.4 + react: ^18.0.0 + react-dom: ^18.0.0 + dependencies: + '@tanstack/query-devtools': 5.0.0-rc.1 + '@tanstack/react-query': 5.0.0-rc.4(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + /@tanstack/react-query@5.0.0-rc.4(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-nN88B6SFvLneav7ZWegjHRhjv46SSAgGnjE/m1aeG9IcfjBGLTP32YkimoDw71F3tRFYzJyrtQ1y6FwB+UymGA==} peerDependencies: @@ -6517,7 +6558,6 @@ packages: /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: true /atob@2.1.2: resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} @@ -6558,7 +6598,6 @@ packages: proxy-from-env: 1.1.0 transitivePeerDependencies: - debug - dev: true /axobject-query@3.2.1: resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} @@ -7418,7 +7457,6 @@ packages: engines: {node: '>= 0.8'} dependencies: delayed-stream: 1.0.0 - dev: true /commander@10.0.1: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} @@ -8109,7 +8147,6 @@ packages: /delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - dev: true /delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} @@ -9653,7 +9690,6 @@ packages: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - dev: true /format@0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} @@ -14383,7 +14419,6 @@ packages: /proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - dev: true /ps-list@8.1.1: resolution: {integrity: sha512-OPS9kEJYVmiO48u/B9qneqhkMvgCxT+Tm28VCEJpheTpl8cJ0ffZRRNgS5mrQRTrX5yRTpaJ+hRDeefXYmmorQ==} diff --git a/sample/another-remote-module/package.json b/sample/another-remote-module/package.json index a09445f49..050bd4d48 100644 --- a/sample/another-remote-module/package.json +++ b/sample/another-remote-module/package.json @@ -16,6 +16,7 @@ "devDependencies": { "@swc/core": "1.3.86", "@swc/helpers": "0.5.2", + "@tanstack/react-query-devtools": "rc", "@types/react": "18.2.22", "@types/react-dom": "18.2.7", "@types/webpack": "5.28.2", @@ -38,6 +39,8 @@ "@squide/fakes": "workspace:*", "@squide/react-router": "workspace:*", "@squide/webpack-module-federation": "workspace:*", + "@tanstack/react-query": "rc", + "axios": "1.5.1", "react": "18.2.0", "react-dom": "18.2.0", "react-router-dom": "6.16.0" diff --git a/sample/another-remote-module/src/dev/App.tsx b/sample/another-remote-module/src/dev/App.tsx index 6a2681bad..707e2c0e6 100644 --- a/sample/another-remote-module/src/dev/App.tsx +++ b/sample/another-remote-module/src/dev/App.tsx @@ -1,10 +1,9 @@ import { useAppRouter } from "@sample/shell"; -import { Suspense, lazy } from "react"; +import { Suspense } from "react"; import { RouterProvider } from "react-router-dom"; +import { DevHome } from "./DevHome.tsx"; import { sessionManager } from "./session.ts"; -const DevHome = lazy(() => import("./DevHome.tsx")); - export function App() { const router = useAppRouter(sessionManager, { managedRoutes: [ diff --git a/sample/another-remote-module/src/dev/DevHome.tsx b/sample/another-remote-module/src/dev/DevHome.tsx index 8b9043ec5..113d1f3d9 100644 --- a/sample/another-remote-module/src/dev/DevHome.tsx +++ b/sample/another-remote-module/src/dev/DevHome.tsx @@ -1,4 +1,4 @@ -export default function DevHome() { +export function DevHome() { return (

Remote module development home page

diff --git a/sample/another-remote-module/src/register.tsx b/sample/another-remote-module/src/register.tsx index 3ebef14a6..a173a0fa4 100644 --- a/sample/another-remote-module/src/register.tsx +++ b/sample/another-remote-module/src/register.tsx @@ -1,14 +1,10 @@ import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; -import { lazy } from "react"; - -const FederatedTabsLayout = lazy(() => import("@sample/shared/FederatedTabsLayout.tsx")); - export const register: ModuleRegisterFunction = runtime => { runtime.registerRoutes([ { path: "/federated-tabs", - element: + lazy: () => import("@sample/shared/FederatedTabsLayout.tsx") } ]); diff --git a/sample/another-remote-module/webpack.build.js b/sample/another-remote-module/webpack.build.js index 901112abf..a70ef7594 100644 --- a/sample/another-remote-module/webpack.build.js +++ b/sample/another-remote-module/webpack.build.js @@ -8,8 +8,14 @@ const publicPath = process.env.NETLIFY === "true" ? "https://squide-another-remo export default defineBuildRemoteModuleConfig(swcConfig, "remote2", publicPath, { sharedDependencies: { + "@tanstack/react-query": { + singleton: true + }, "@sample/shared": { singleton: true + }, + "axios": { + singleton: true } }, environmentVariables: { diff --git a/sample/another-remote-module/webpack.dev.js b/sample/another-remote-module/webpack.dev.js index 1ab2db4f8..0652870c1 100644 --- a/sample/another-remote-module/webpack.dev.js +++ b/sample/another-remote-module/webpack.dev.js @@ -10,8 +10,14 @@ let config; if (!process.env.LOCAL) { config = defineDevRemoteModuleConfig(swcConfig, "remote2", 8082, { sharedDependencies: { + "@tanstack/react-query": { + singleton: true + }, "@sample/shared": { singleton: true + }, + "axios": { + singleton: true } }, environmentVariables: { diff --git a/sample/host/mocks/handlers.ts b/sample/host/mocks/handlers.ts index 233ab03fb..7da31bba4 100644 --- a/sample/host/mocks/handlers.ts +++ b/sample/host/mocks/handlers.ts @@ -1,12 +1,22 @@ import { rest, type RestHandler } from "msw"; export const hostRequestHandlers: RestHandler[] = [ + // TODO: If not logged in, return a 401 unauthorized + rest.get("/session", (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json({ + name: "John Doe" + }) + ); + }), + rest.get("/feature-flags", (req, res, ctx) => { return res( ctx.status(200), ctx.json([ { feature: "send-message", isActive: true }, - { feature: "ricky-and-morty-characters", isActive: true } + { feature: "list-ricky-and-morty-characters", isActive: true } ]) ); }) diff --git a/sample/host/package.json b/sample/host/package.json index bb760350a..785f60b36 100644 --- a/sample/host/package.json +++ b/sample/host/package.json @@ -17,6 +17,7 @@ "devDependencies": { "@swc/core": "1.3.86", "@swc/helpers": "0.5.2", + "@tanstack/react-query-devtools": "rc", "@types/react": "18.2.22", "@types/react-dom": "18.2.7", "@types/webpack": "5.28.2", @@ -45,6 +46,7 @@ "@squide/msw": "workspace:*", "@squide/webpack-module-federation": "workspace:*", "@tanstack/react-query": "rc", + "axios": "1.5.1", "react": "18.2.0", "react-dom": "18.2.0", "react-router-dom": "6.16.0" diff --git a/sample/host/src/App.tsx b/sample/host/src/App.tsx index e842e6f3a..c84af2a5d 100644 --- a/sample/host/src/App.tsx +++ b/sample/host/src/App.tsx @@ -10,7 +10,7 @@ const Home = lazy(() => import("./Home.tsx")); export function App() { // Re-render the app once all the remotes are registered. // Otherwise, the remotes routes won't be added to the router. - const isReady = useAreModulesReady(); + const areModulesReady = useAreModulesReady(); const router = useAppRouter(sessionManager, { managedRoutes: [ @@ -21,7 +21,7 @@ export function App() { ] }); - if (!isReady) { + if (!areModulesReady) { return
Loading...
; } diff --git a/sample/host/src/bootstrap.tsx b/sample/host/src/bootstrap.tsx index 49736c1be..999a063ff 100644 --- a/sample/host/src/bootstrap.tsx +++ b/sample/host/src/bootstrap.tsx @@ -3,6 +3,8 @@ import { isNetlify, type AppContext } from "@sample/shared"; import { MswPlugin } from "@squide/msw"; import { ConsoleLogger, Runtime, RuntimeContext, registerLocalModules } from "@squide/react-router"; import { registerRemoteModules, type RemoteDefinition } from "@squide/webpack-module-federation"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import { App } from "./App.tsx"; @@ -19,16 +21,29 @@ const Remotes: RemoteDefinition[] = [ } ]; +// Add a plugin to support Mock Service Worker for a federated application. +// The plugin will help collect the request handlers from every module. const mswPlugin = new MswPlugin(); const runtime = new Runtime({ - plugins: [mswPlugin], loggers: [new ConsoleLogger()], + plugins: [mswPlugin], sessionAccessor }); +// Creates a React Query client. +// This client will be shared between the host application and the remote modules. +// It's primary purpose is to keep fresh data in cache but also to serve as a global +// state management solution for the federated application. It works for most global state +// values because with React Query the global state values are synched with the server +// data, which is our single source of truth. +const queryClient = new QueryClient(); + const context: AppContext = { - name: "Test app" + name: "Test app", + // TODO: Should eventually be a plugin instead of passing it through the context object. + // @squide/react-query. + queryClient }; registerLocalModules([registerLocalModule], runtime, { context }); @@ -36,6 +51,7 @@ registerLocalModules([registerLocalModule], runtime, { context }); registerRemoteModules(Remotes, runtime, { context }).then(() => { if (process.env.USE_MSW) { import("../mocks/browser.ts").then(({ startMsw }) => { + // Will start MSW with the request handlers provided by every module. startMsw(mswPlugin.requestHandlers); }); } @@ -46,7 +62,10 @@ const root = createRoot(document.getElementById("root")!); root.render( - + + + + ); diff --git a/sample/host/src/session.ts b/sample/host/src/session.ts index de7dd2e90..fbc2fd0a0 100644 --- a/sample/host/src/session.ts +++ b/sample/host/src/session.ts @@ -1,9 +1,59 @@ import type { Session, SessionManager } from "@sample/shared"; +// import type { Session } from "@sample/shared"; import { LocalStorageSessionManager } from "@squide/fakes"; import type { SessionAccessorFunction } from "@squide/react-router"; +// import type { QueryClient } from "@tanstack/react-query"; +// import axios from "axios"; export const sessionManager = new LocalStorageSessionManager() as SessionManager; +// export const SessionQueryKey = ["/session"]; + +// export const sessionQuery = { +// queryKey: SessionQueryKey, +// queryFn: async () => { +// const { data } = await axios.get("/session"); + +// return data; +// } +// }; + +// export class SessionManager { +// readonly #queryClient: QueryClient; + +// constructor(queryClient: QueryClient) { +// this.#queryClient = queryClient; +// } + +// getSession() { +// this.#queryClient.getQueryData(SessionQueryKey); +// } +// } + +// export async function fetchSession() { +// const { data } = await axios.get("/session"); + +// return data; +// } + +// export class SessionManager { +// #session?: Session; + +// setSession(session: Session) { +// this.#session = session; +// } + +// getSession() { +// return this.#session; +// } + +// clearSession() { +// this.#session = undefined; +// } +// } + +// export const sessionManager = new SessionManager(); + export const sessionAccessor: SessionAccessorFunction = () => { return sessionManager.getSession(); }; diff --git a/sample/host/tsconfig.json b/sample/host/tsconfig.json index fbe17d9ff..71d7e36e9 100644 --- a/sample/host/tsconfig.json +++ b/sample/host/tsconfig.json @@ -6,6 +6,7 @@ "@squide/react-router": ["../../packages/react-router/src/index.ts"], "@squide/webpack-module-federation": ["../../packages/webpack-module-federation/src/index.ts"], "@squide/webpack-module-federation/defineConfig.js": ["../../packages/webpack-module-federation/src/defineConfig.js"], + "@squide/msw": ["../../packages/msw/src/index.ts"], "@squide/fakes": ["../../packages/fakes/src/index.ts"], "@sample/shell": ["../shell/src/index.ts"], "@sample/shared": ["../shared/src/index.ts"], diff --git a/sample/host/webpack.build.js b/sample/host/webpack.build.js index 815e9f443..19e5c23a0 100644 --- a/sample/host/webpack.build.js +++ b/sample/host/webpack.build.js @@ -8,8 +8,17 @@ const publicPath = process.env.NETLIFY === "true" ? "https://squide-host.netlify export default defineBuildHostConfig(swcConfig, "host", publicPath, { sharedDependencies: { + "@tanstack/react-query": { + singleton: true, + eager: true + }, "@sample/shared": { - singleton: true + singleton: true, + eager: true + }, + "axios": { + singleton: true, + eager: true } }, environmentVariables: { diff --git a/sample/host/webpack.dev.js b/sample/host/webpack.dev.js index 5429082fa..d63616d29 100644 --- a/sample/host/webpack.dev.js +++ b/sample/host/webpack.dev.js @@ -5,9 +5,17 @@ import { swcConfig } from "./swc.dev.js"; export default defineDevHostConfig(swcConfig, "host", 8080, { sharedDependencies: { + "@tanstack/react-query": { + singleton: true, + eager: true + }, "@sample/shared": { singleton: true, eager: true + }, + "axios": { + singleton: true, + eager: true } }, environmentVariables: { diff --git a/sample/local-module/src/About.tsx b/sample/local-module/src/About.tsx index 4182599df..580696b49 100644 --- a/sample/local-module/src/About.tsx +++ b/sample/local-module/src/About.tsx @@ -1,6 +1,6 @@ import { Link } from "react-router-dom"; -export default function About() { +export function About() { return (

About

@@ -9,3 +9,5 @@ export default function About() {
); } + +export const Component = About; diff --git a/sample/local-module/src/Message.tsx b/sample/local-module/src/Message.tsx index 399aceb87..ce72df570 100644 --- a/sample/local-module/src/Message.tsx +++ b/sample/local-module/src/Message.tsx @@ -3,7 +3,7 @@ import { useCallback, useState, type ChangeEvent } from "react"; import { useApplicationEventBusDispatcher } from "@sample/shared"; import { Link } from "react-router-dom"; -export default function Message() { +export function Message() { const [message, setMessage] = useState(""); const handleTextChange = useCallback((event: ChangeEvent) => { @@ -29,3 +29,5 @@ export default function Message() {
); } + +export const Component = Message; diff --git a/sample/local-module/src/WorkleapTab.tsx b/sample/local-module/src/WorkleapTab.tsx index ac562d112..6b2e9ec12 100644 --- a/sample/local-module/src/WorkleapTab.tsx +++ b/sample/local-module/src/WorkleapTab.tsx @@ -1,7 +1,9 @@ -export default function WorkleapTab() { +export function WorkleapTab() { return (

This is the Workleap tab from @sample/local-module!

); } + +export const Component = WorkleapTab; diff --git a/sample/local-module/src/dev/App.tsx b/sample/local-module/src/dev/App.tsx index 6a2681bad..707e2c0e6 100644 --- a/sample/local-module/src/dev/App.tsx +++ b/sample/local-module/src/dev/App.tsx @@ -1,10 +1,9 @@ import { useAppRouter } from "@sample/shell"; -import { Suspense, lazy } from "react"; +import { Suspense } from "react"; import { RouterProvider } from "react-router-dom"; +import { DevHome } from "./DevHome.tsx"; import { sessionManager } from "./session.ts"; -const DevHome = lazy(() => import("./DevHome.tsx")); - export function App() { const router = useAppRouter(sessionManager, { managedRoutes: [ diff --git a/sample/local-module/src/dev/DevHome.tsx b/sample/local-module/src/dev/DevHome.tsx index 8b9043ec5..113d1f3d9 100644 --- a/sample/local-module/src/dev/DevHome.tsx +++ b/sample/local-module/src/dev/DevHome.tsx @@ -1,4 +1,4 @@ -export default function DevHome() { +export function DevHome() { return (

Remote module development home page

diff --git a/sample/local-module/src/dev/registerTabsPage.tsx b/sample/local-module/src/dev/registerTabsPage.tsx index 4c4b91daa..a66667cf7 100644 --- a/sample/local-module/src/dev/registerTabsPage.tsx +++ b/sample/local-module/src/dev/registerTabsPage.tsx @@ -1,13 +1,10 @@ import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; -import { lazy } from "react"; - -const FederatedTabsLayout = lazy(() => import("@sample/shared/FederatedTabsLayout.tsx")); export const registerTabsPage: ModuleRegisterFunction = runtime => { runtime.registerRoutes([ { path: "/federated-tabs", - element: + lazy: () => import("@sample/shared/FederatedTabsLayout.tsx") } ]); diff --git a/sample/local-module/src/register.tsx b/sample/local-module/src/register.tsx index 271ecb89e..a158a87ad 100644 --- a/sample/local-module/src/register.tsx +++ b/sample/local-module/src/register.tsx @@ -1,10 +1,5 @@ import type { AppContext } from "@sample/shared"; import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; -import { lazy } from "react"; - -const About = lazy(() => import("./About.tsx")); -const Message = lazy(() => import("./Message.tsx")); -const WorkleapTab = lazy(() => import("./WorkleapTab.tsx")); export const register: ModuleRegisterFunction = (runtime, context) => { console.log("Local module context: ", context); @@ -12,11 +7,11 @@ export const register: ModuleRegisterFunction = (runtime, c runtime.registerRoutes([ { path: "/about", - element: + lazy: () => import("./About.tsx") }, { path: "/message", - element: + lazy: () => import("./Message.tsx") } ]); @@ -42,7 +37,7 @@ export const register: ModuleRegisterFunction = (runtime, c runtime.registerRoutes([ { index: true, - element: + lazy: () => import("./WorkleapTab.tsx") } ], { layoutPath: "/federated-tabs" }); diff --git a/sample/remote-module/package.json b/sample/remote-module/package.json index 7f82d73c0..c5bb14cb1 100644 --- a/sample/remote-module/package.json +++ b/sample/remote-module/package.json @@ -17,7 +17,7 @@ "devDependencies": { "@swc/core": "1.3.86", "@swc/helpers": "0.5.2", - "@tanstack/react-query": "rc", + "@tanstack/react-query-devtools": "rc", "@types/react": "18.2.22", "@types/react-dom": "18.2.7", "@types/webpack": "5.28.2", @@ -43,6 +43,8 @@ "@squide/msw": "workspace:*", "@squide/react-router": "workspace:*", "@squide/webpack-module-federation": "workspace:*", + "@tanstack/react-query": "rc", + "axios": "1.5.1", "react": "18.2.0", "react-dom": "18.2.0", "react-router-dom": "6.16.0" diff --git a/sample/remote-module/src/ColoredPage.tsx b/sample/remote-module/src/ColoredPage.tsx index 54c2b0869..afe2781ac 100644 --- a/sample/remote-module/src/ColoredPage.tsx +++ b/sample/remote-module/src/ColoredPage.tsx @@ -1,6 +1,6 @@ import { useBackgroundColor } from "@sample/shared"; -export default function ColoredPage() { +export function ColoredPage() { const backgroundColor = useBackgroundColor(); return ( @@ -9,3 +9,5 @@ export default function ColoredPage() {
); } + +export const Component = ColoredPage; diff --git a/sample/remote-module/src/CustomLayout.tsx b/sample/remote-module/src/CustomLayout.tsx index 584535d99..b8bd819e6 100644 --- a/sample/remote-module/src/CustomLayout.tsx +++ b/sample/remote-module/src/CustomLayout.tsx @@ -1,9 +1,11 @@ import { Outlet } from "react-router-dom"; -export default function CustomLayout() { +export function CustomLayout() { return (
); } + +export const Component = CustomLayout; diff --git a/sample/remote-module/src/Fetch.tsx b/sample/remote-module/src/Fetch.tsx index 85176a34a..fca552185 100644 --- a/sample/remote-module/src/Fetch.tsx +++ b/sample/remote-module/src/Fetch.tsx @@ -6,7 +6,7 @@ interface Character { species: string; } -export default function Fetch() { +export function Fetch() { const characters = useLoaderData() as Character[]; return ( @@ -29,3 +29,5 @@ export default function Fetch() { ); } + +export const Component = Fetch; diff --git a/sample/remote-module/src/Hoisted.tsx b/sample/remote-module/src/Hoisted.tsx index f2612d277..16c895e0c 100644 --- a/sample/remote-module/src/Hoisted.tsx +++ b/sample/remote-module/src/Hoisted.tsx @@ -1,6 +1,6 @@ import { Link } from "react-router-dom"; -export default function Hoisted() { +export function Hoisted() { return (

Hoisted

@@ -9,3 +9,5 @@ export default function Hoisted() {
); } + +export const Component = Hoisted; diff --git a/sample/remote-module/src/OfficevibeTab.tsx b/sample/remote-module/src/OfficevibeTab.tsx index 4c0369934..687a03cfa 100644 --- a/sample/remote-module/src/OfficevibeTab.tsx +++ b/sample/remote-module/src/OfficevibeTab.tsx @@ -1,7 +1,9 @@ -export default function OfficevibeTab() { +export function OfficevibeTab() { return (

This is the Officevibe tab from @sample/remote-module!

); } + +export const Component = OfficevibeTab; diff --git a/sample/remote-module/src/Remote.tsx b/sample/remote-module/src/Remote.tsx index 459e7cb89..b9a700b24 100644 --- a/sample/remote-module/src/Remote.tsx +++ b/sample/remote-module/src/Remote.tsx @@ -1,6 +1,6 @@ import { Link } from "react-router-dom"; -export default function Remote() { +export function Remote() { return (

Remote

@@ -9,3 +9,5 @@ export default function Remote() {
); } + +export const Component = Remote; diff --git a/sample/remote-module/src/SkillsTab.tsx b/sample/remote-module/src/SkillsTab.tsx index 9ecca7a29..90ea8fcd5 100644 --- a/sample/remote-module/src/SkillsTab.tsx +++ b/sample/remote-module/src/SkillsTab.tsx @@ -1,7 +1,9 @@ -export default function SkillsTab() { +export function SkillsTab() { return (

This is the Skills tab from @sample/remote-module!

); } + +export const Component = SkillsTab; diff --git a/sample/remote-module/src/dev/App.tsx b/sample/remote-module/src/dev/App.tsx index b0bcf1d07..ac0525e99 100644 --- a/sample/remote-module/src/dev/App.tsx +++ b/sample/remote-module/src/dev/App.tsx @@ -1,11 +1,10 @@ import { BackgroundColorContext } from "@sample/shared"; import { useAppRouter } from "@sample/shell"; -import { Suspense, lazy } from "react"; +import { Suspense } from "react"; import { RouterProvider } from "react-router-dom"; +import { DevHome } from "./DevHome.tsx"; import { sessionManager } from "./session.ts"; -const DevHome = lazy(() => import("./DevHome.tsx")); - export function App() { const router = useAppRouter(sessionManager, { managedRoutes: [ diff --git a/sample/remote-module/src/dev/DevHome.tsx b/sample/remote-module/src/dev/DevHome.tsx index 8b9043ec5..113d1f3d9 100644 --- a/sample/remote-module/src/dev/DevHome.tsx +++ b/sample/remote-module/src/dev/DevHome.tsx @@ -1,4 +1,4 @@ -export default function DevHome() { +export function DevHome() { return (

Remote module development home page

diff --git a/sample/remote-module/src/dev/registerTabsPage.tsx b/sample/remote-module/src/dev/registerTabsPage.tsx index 4c4b91daa..a66667cf7 100644 --- a/sample/remote-module/src/dev/registerTabsPage.tsx +++ b/sample/remote-module/src/dev/registerTabsPage.tsx @@ -1,13 +1,10 @@ import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; -import { lazy } from "react"; - -const FederatedTabsLayout = lazy(() => import("@sample/shared/FederatedTabsLayout.tsx")); export const registerTabsPage: ModuleRegisterFunction = runtime => { runtime.registerRoutes([ { path: "/federated-tabs", - element: + lazy: () => import("@sample/shared/FederatedTabsLayout.tsx") } ]); diff --git a/sample/remote-module/src/register.tsx b/sample/remote-module/src/register.tsx index d68934d90..7e4f6fffd 100644 --- a/sample/remote-module/src/register.tsx +++ b/sample/remote-module/src/register.tsx @@ -1,26 +1,17 @@ import { BackgroundColorContext } from "@sample/shared"; import { getMswPlugin } from "@squide/msw"; import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; -import { lazy } from "react"; import { requestHandlers } from "../mocks/handlers.ts"; -const CustomLayout = lazy(() => import("./CustomLayout.tsx")); -const Remote = lazy(() => import("./Remote.tsx")); -const Fetch = lazy(() => import("./Fetch.tsx")); -const Hoisted = lazy(() => import("./Hoisted.tsx")); -const OfficevibeTab = lazy(() => import("./OfficevibeTab.tsx")); -const SkillsTab = lazy(() => import("./SkillsTab.tsx")); -const ColoredPage = lazy(() => import("./ColoredPage.tsx")); - export const register: ModuleRegisterFunction = runtime => { runtime.registerRoutes([ { path: "/remote", - element: + lazy: async () => import("./Remote.tsx") }, { path: "/fetch", - element: , + lazy: () => import("./Fetch.tsx"), loader: async function loader() { return fetch("https://rickandmortyapi.com/api/character/1,2,3,4,5", { method: "GET", @@ -33,25 +24,31 @@ export const register: ModuleRegisterFunction = runtime => { { hoist: true, path: "/hoisted", - element: , + lazy: () => import("./CustomLayout.tsx"), children: [ { index: true, - element: + lazy: () => import("./Hoisted.tsx") } ] }, { path: "/no-context-override", - element: + lazy: () => import("./ColoredPage.tsx") }, { path: "/context-override", - element: ( - - - - ) + lazy: async () => { + const { ColoredPage } = await import("./ColoredPage.tsx"); + + return { + element: ( + + + + ) + }; + } } ]); @@ -96,11 +93,11 @@ export const register: ModuleRegisterFunction = runtime => { runtime.registerRoutes([ { path: "/federated-tabs/officevibe", - element: + lazy: () => import("./OfficevibeTab.tsx") }, { path: "/federated-tabs/skills", - element: + lazy: () => import("./SkillsTab.tsx") } ], { layoutPath: "/federated-tabs" }); @@ -118,7 +115,6 @@ export const register: ModuleRegisterFunction = runtime => { // Register request handlers for MSW. - const mswPlugin = getMswPlugin(runtime.plugins); - + const mswPlugin = getMswPlugin(runtime); mswPlugin.registerRequestHandlers(requestHandlers); }; diff --git a/sample/remote-module/tsconfig.json b/sample/remote-module/tsconfig.json index 7d155fc83..a396d74cb 100644 --- a/sample/remote-module/tsconfig.json +++ b/sample/remote-module/tsconfig.json @@ -6,6 +6,7 @@ "@squide/react-router": ["../../packages/react-router/src/index.ts"], "@squide/webpack-module-federation": ["../../packages/webpack-module-federation/src/index.ts"], "@squide/webpack-module-federation/defineConfig.js": ["../../packages/webpack-module-federation/src/defineConfig.js"], + "@squide/msw": ["../../packages/msw/src/index.ts"], "@sample/shell": ["../shell/src/index.ts"], "@sample/shared": ["../shared/src/index.ts"], "@sample/shared/FederatedTabsLayout.tsx": ["../shared/src/FederatedTabsLayout.tsx"] diff --git a/sample/remote-module/webpack.build.js b/sample/remote-module/webpack.build.js index 9197cf8ab..bf8e8bfe1 100644 --- a/sample/remote-module/webpack.build.js +++ b/sample/remote-module/webpack.build.js @@ -8,8 +8,14 @@ const publicPath = process.env.NETLIFY === "true" ? "https://squide-remote-modul export default defineBuildRemoteModuleConfig(swcConfig, "remote1", publicPath, { sharedDependencies: { + "@tanstack/react-query": { + singleton: true + }, "@sample/shared": { singleton: true + }, + "axios": { + singleton: true } }, environmentVariables: { diff --git a/sample/remote-module/webpack.dev.js b/sample/remote-module/webpack.dev.js index 328ff5927..c26c97a81 100644 --- a/sample/remote-module/webpack.dev.js +++ b/sample/remote-module/webpack.dev.js @@ -10,8 +10,14 @@ let config; if (!process.env.LOCAL) { config = defineDevRemoteModuleConfig(swcConfig, "remote1", 8081, { sharedDependencies: { + "@tanstack/react-query": { + singleton: true + }, "@sample/shared": { singleton: true + }, + "axios": { + singleton: true } }, environmentVariables: { diff --git a/sample/shared/package.json b/sample/shared/package.json index e41d19ca7..b59db700b 100644 --- a/sample/shared/package.json +++ b/sample/shared/package.json @@ -25,6 +25,7 @@ }, "peerDependencies": { "@squide/react-router": "*", + "@tanstack/react-query": "*", "react": "*", "react-dom": "*", "react-router-dom": "*" @@ -32,6 +33,7 @@ "devDependencies": { "@remix-run/router": "1.9.0", "@squide/react-router": "workspace:*", + "@tanstack/react-query": "rc", "@types/react": "18.2.22", "@types/react-dom": "18.2.7", "@workleap/eslint-plugin": "2.1.1", diff --git a/sample/shared/src/FederatedTabsLayout.tsx b/sample/shared/src/FederatedTabsLayout.tsx index 922455d9c..de11ccbe2 100644 --- a/sample/shared/src/FederatedTabsLayout.tsx +++ b/sample/shared/src/FederatedTabsLayout.tsx @@ -22,7 +22,7 @@ const renderSection: RenderSectionFunction = elements => { ); }; -export default function FederatedTabsLayout() { +export function FederatedTabsLayout() { const navigationItems = useNavigationItems("/federated-tabs"); const renderedTabs = useRenderedNavigationItems(navigationItems, renderItem, renderSection); @@ -39,4 +39,6 @@ export default function FederatedTabsLayout() { ); } +export const Component = FederatedTabsLayout; + diff --git a/sample/shared/src/appContext.ts b/sample/shared/src/appContext.ts index 5bec8965b..97d399657 100644 --- a/sample/shared/src/appContext.ts +++ b/sample/shared/src/appContext.ts @@ -1,3 +1,6 @@ +import type { QueryClient } from "@tanstack/react-query"; + export interface AppContext { name: string; + queryClient: QueryClient; } diff --git a/sample/shell/package.json b/sample/shell/package.json index 88faaf723..d06f82fff 100644 --- a/sample/shell/package.json +++ b/sample/shell/package.json @@ -11,6 +11,31 @@ "import": "./dist/index.js", "types": "./dist/index.d.ts", "default": "./dist/index.js" + }, + "./AuthenticatedLayout.tsx": { + "import": "./dist/AuthenticatedLayout.js", + "types": "./dist/AuthenticatedLayout.d.ts", + "default": "./dist/AuthenticatedLayout.js" + }, + "./Login.tsx": { + "import": "./dist/Login.js", + "types": "./dist/Login.d.ts", + "default": "./dist/Login.js" + }, + "./Logout.tsx": { + "import": "./dist/Logout.js", + "types": "./dist/Logout.d.ts", + "default": "./dist/Logout.js" + }, + "./ModuleErrorBoundary.tsx": { + "import": "./dist/ModuleErrorBoundary.js", + "types": "./dist/ModuleErrorBoundary.d.ts", + "default": "./dist/ModuleErrorBoundary.js" + }, + "./NoMatch.tsx": { + "import": "./dist/NoMatch.js", + "types": "./dist/NoMatch.d.ts", + "default": "./dist/NoMatch.js" } }, "scripts": { diff --git a/sample/shell/src/AuthenticatedLayout.tsx b/sample/shell/src/AuthenticatedLayout.tsx index a899f46aa..e0731dcae 100644 --- a/sample/shell/src/AuthenticatedLayout.tsx +++ b/sample/shell/src/AuthenticatedLayout.tsx @@ -40,7 +40,7 @@ const renderSection: RenderSectionFunction = (elements, index, level) => { ); }; -export default function AuthenticatedLayout() { +export function AuthenticatedLayout() { const session = useSession() as Session; const handleModulesMessage = useCallback((data: unknown) => { @@ -72,3 +72,5 @@ export default function AuthenticatedLayout() { ); } + +export const Component = AuthenticatedLayout; diff --git a/sample/shell/src/Login.tsx b/sample/shell/src/Login.tsx index ff6f75a9e..26ee1f160 100644 --- a/sample/shell/src/Login.tsx +++ b/sample/shell/src/Login.tsx @@ -7,7 +7,7 @@ export interface LoginProps { sessionManager: SessionManager; } -export default function Login({ sessionManager }: LoginProps) { +export function Login({ sessionManager }: LoginProps) { const [username, setUserName] = useState(""); const [password, setPassword] = useState(""); @@ -25,7 +25,7 @@ export default function Login({ sessionManager }: LoginProps) { navigate("/"); } - }, [username, password, navigate]); + }, [username, password, sessionManager, navigate]); const handleUserNameChange = useCallback((event: ChangeEvent) => { setUserName(event.target.value); @@ -63,3 +63,5 @@ export default function Login({ sessionManager }: LoginProps) { ); } + +export const Component = Login; diff --git a/sample/shell/src/Logout.tsx b/sample/shell/src/Logout.tsx index f07efb2f7..41a7737a5 100644 --- a/sample/shell/src/Logout.tsx +++ b/sample/shell/src/Logout.tsx @@ -5,7 +5,7 @@ export interface LogoutProps { sessionManager: SessionManager; } -export default function Logout({ sessionManager }: LogoutProps) { +export function Logout({ sessionManager }: LogoutProps) { sessionManager.clearSession(); return ( @@ -16,3 +16,5 @@ export default function Logout({ sessionManager }: LogoutProps) { ); } + +export const Component = Logout; diff --git a/sample/shell/src/ModuleErrorBoundary.tsx b/sample/shell/src/ModuleErrorBoundary.tsx index 4546415b8..fe578bd0d 100644 --- a/sample/shell/src/ModuleErrorBoundary.tsx +++ b/sample/shell/src/ModuleErrorBoundary.tsx @@ -11,7 +11,7 @@ function getErrorMessage(error: unknown) { : JSON.stringify(error); } -export default function ModuleErrorBoundary() { +export function ModuleErrorBoundary() { const error = useRouteError(); const location = useLocation(); const logger = useLogger(); @@ -26,3 +26,5 @@ export default function ModuleErrorBoundary() {
); } + +export const ErrorBoundary = ModuleErrorBoundary; diff --git a/sample/shell/src/NoMatch.tsx b/sample/shell/src/NoMatch.tsx index ed72fe0cf..fdc4deb0c 100644 --- a/sample/shell/src/NoMatch.tsx +++ b/sample/shell/src/NoMatch.tsx @@ -4,7 +4,7 @@ export interface NoMatchProps { path: string; } -export default function NoMatch({ path }: NoMatchProps) { +export function NoMatch({ path }: NoMatchProps) { return (

404

@@ -13,3 +13,5 @@ export default function NoMatch({ path }: NoMatchProps) {
); } + +export const Component = NoMatch; diff --git a/sample/shell/src/index.ts b/sample/shell/src/index.ts index f6cf0a585..fff4e31c8 100644 --- a/sample/shell/src/index.ts +++ b/sample/shell/src/index.ts @@ -1,9 +1,4 @@ -export * from "./AuthenticatedLayout.tsx"; export * from "./AuthenticationBoundary.tsx"; -export * from "./Login.tsx"; -export * from "./Logout.tsx"; -export * from "./ModuleErrorBoundary.tsx"; -export * from "./NoMatch.tsx"; export * from "./RootErrorBoundary.tsx"; export * from "./RootLayout.tsx"; export * from "./useAppRouter.tsx"; diff --git a/sample/shell/src/useAppRouter.tsx b/sample/shell/src/useAppRouter.tsx index a08776f5e..6b24ecdb9 100644 --- a/sample/shell/src/useAppRouter.tsx +++ b/sample/shell/src/useAppRouter.tsx @@ -1,5 +1,5 @@ import { useHoistedRoutes, useRoutes, type Route } from "@squide/react-router"; -import { lazy, useCallback, useMemo, useState } from "react"; +import { useCallback, useMemo, useState } from "react"; import { AuthenticationBoundary } from "./AuthenticationBoundary.tsx"; import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; import { RootLayout } from "./RootLayout.tsx"; @@ -13,12 +13,6 @@ export interface UseAppRouterOptions { rootRoutes?: Route[]; } -const AuthenticatedLayout = lazy(() => import("./AuthenticatedLayout.tsx")); -const ModuleErrorBoundary = lazy(() => import("./ModuleErrorBoundary.tsx")); -const Login = lazy(() => import("./Login.tsx")); -const Logout = lazy(() => import("./Logout.tsx")); -const NoMatch = lazy(() => import("./NoMatch.tsx")); - export function useAppRouter(sessionManager: SessionManager, options: UseAppRouterOptions = {}): Router { const { managedRoutes: hostManagedRoutes = [], @@ -41,11 +35,23 @@ export function useAppRouter(sessionManager: SessionManager, options: UseAppRout children: [ { path: "/login", - element: + lazy: async () => { + const { Login } = await import("./Login.tsx"); + + return { + element: + }; + } }, { path: "/logout", - element: + lazy: async () => { + const { Logout } = await import("./Logout.tsx"); + + return { + element: + }; + } }, { // Pathless route to declare an authenticated boundary. @@ -53,12 +59,12 @@ export function useAppRouter(sessionManager: SessionManager, options: UseAppRout children: [ { // Pathless route to declare an authenticated layout. - element: , + lazy: () => import("./AuthenticatedLayout.tsx"), children: [ { // Pathless route to declare an error boundary inside the layout instead of outside. // It's quite useful to prevent losing the layout when an unmanaged error occurs. - errorElement: , + lazy: () => import("./ModuleErrorBoundary.tsx"), children: [ ...memoizedManagedRoutes, ...managedRoutes @@ -70,13 +76,19 @@ export function useAppRouter(sessionManager: SessionManager, options: UseAppRout }, { path: "*", - element: + lazy: async () => { + const { NoMatch } = await import("./NoMatch.tsx"); + + return { + element: + }; + } } ] } ] }; - }, []); + }, [sessionManager, memoizedManagedRoutes]); // Using the useHoistedRoutes hook allow routes hoisted by modules to be rendered at the root of the router instead of under the root layout. // To disallow the hoisting functionality, remove this hook and add the routes directly. From 193c9601732787438961d275a9f562b64011c3f9 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Fri, 6 Oct 2023 13:53:15 -0400 Subject: [PATCH 03/51] Too many things --- docs/getting-started/create-host.md | 4 +- docs/guides/add-authentication.md | 4 +- docs/guides/develop-a-module-in-isolation.md | 4 +- docs/guides/federated-tabs.md | 24 +- docs/guides/isolate-module-failures.md | 4 +- docs/guides/override-a-react-context.md | 8 +- docs/guides/override-the-host-layout.md | 14 +- docs/reference/plugins/plugin.md | 4 +- .../registration/registerLocalModules.md | 6 +- .../registration/registerRemoteModules.md | 6 +- .../registration/useAreModulesReady.md | 6 +- docs/reference/routing/useHoistedRoutes.md | 48 +- .../routing/useRenderedNavigationItems.md | 2 +- docs/reference/runtime/runtime-class.md | 92 ++- docs/reference/services/service.md | 2 +- packages/core/src/runtime/RuntimeContext.ts | 1 - packages/core/src/runtime/abstractRuntime.ts | 3 +- packages/msw/package.json | 6 + packages/msw/src/index.ts | 1 + packages/msw/src/mswPlugin.ts | 9 + packages/msw/src/useIsMswReady.ts | 37 + packages/react-router/src/routeRegistry.ts | 162 ++-- packages/react-router/src/runtime.ts | 24 +- packages/react-router/tests/runtime.test.tsx | 750 ++++++++++++++---- .../src/useAreModulesReady.ts | 10 +- pnpm-lock.yaml | 15 +- sample/another-remote-module/src/dev/App.tsx | 19 +- .../another-remote-module/src/dev/session.ts | 4 +- sample/host/mocks/handlers.ts | 60 +- sample/host/src/App copy.tsx | 65 ++ sample/host/src/App.tsx | 108 ++- sample/host/src/Home.tsx | 4 +- sample/host/src/bootstrap.tsx | 9 +- sample/host/src/session.ts | 32 +- sample/local-module/src/dev/App.tsx | 19 +- sample/local-module/src/dev/session.ts | 4 +- sample/local-module/src/register.tsx | 2 +- sample/remote-module/src/dev/App.tsx | 20 +- sample/remote-module/src/dev/session.ts | 4 +- sample/remote-module/src/register.tsx | 3 +- sample/shared/package.json | 2 - sample/shared/src/appContext.ts | 3 - sample/shared/src/session.ts | 10 +- sample/shell/package.json | 26 +- sample/shell/src/AuthenticatedLayout.tsx | 2 - sample/shell/src/Login.tsx | 43 +- sample/shell/src/Logout.tsx | 11 +- sample/shell/src/ModuleErrorBoundary.tsx | 2 - sample/shell/src/NoMatch.tsx | 2 - sample/shell/src/index.ts | 5 + sample/shell/src/useAppRouter.tsx | 137 +++- 51 files changed, 1348 insertions(+), 494 deletions(-) create mode 100644 packages/msw/src/useIsMswReady.ts create mode 100644 sample/host/src/App copy.tsx diff --git a/docs/getting-started/create-host.md b/docs/getting-started/create-host.md index 578c1e43a..72b86606a 100644 --- a/docs/getting-started/create-host.md +++ b/docs/getting-started/create-host.md @@ -129,7 +129,7 @@ import { Home } from "./Home.tsx"; export function App() { // Re-render the application once the remote modules are registered. - const isReady = useAreModulesReady(); + const areModulesReady = useAreModulesReady(); // Retrieve the routes registered by the remote modules. const routes = useRoutes(); @@ -152,7 +152,7 @@ export function App() { }, [routes]); // Display a loading until the remote modules are registered. - if (!isReady) { + if (!areModulesReady) { return
Loading...
; } diff --git a/docs/guides/add-authentication.md b/docs/guides/add-authentication.md index a08123467..a360e1bac 100644 --- a/docs/guides/add-authentication.md +++ b/docs/guides/add-authentication.md @@ -262,7 +262,7 @@ import { Login } from "./Login.tsx"; import { Logout } from "./Logout.tsx"; export function App() { - const isReady = useAreModulesReady(); + const areModulesReady = useAreModulesReady(); const routes = useRoutes(); @@ -312,7 +312,7 @@ export function App() { }); }, [routes]); - if (!isReady) { + if (!areModulesReady) { return
Loading...
; } diff --git a/docs/guides/develop-a-module-in-isolation.md b/docs/guides/develop-a-module-in-isolation.md index 37e34c8bf..58a2045e8 100644 --- a/docs/guides/develop-a-module-in-isolation.md +++ b/docs/guides/develop-a-module-in-isolation.md @@ -111,7 +111,7 @@ import { useAppRouter } from "@sample/shell"; import { Home } from "./Home.tsx"; export function App() { - const isReady = useAreModulesReady(); + const areModulesReady = useAreModulesReady(); const router = useAppRouter({ rootRoutes: [ @@ -122,7 +122,7 @@ export function App() { ] }); - if (!isReady) { + if (!areModulesReady) { return
Loading...
; } diff --git a/docs/guides/federated-tabs.md b/docs/guides/federated-tabs.md index 549ee16f5..774647a5b 100644 --- a/docs/guides/federated-tabs.md +++ b/docs/guides/federated-tabs.md @@ -24,7 +24,7 @@ To construct this page while adhering to Squide constraint of exclusively permit import { Suspense } from "react"; import { Link, Outlet } from "react-router-dom"; -export default function FederatedTabsLayout() { +export function FederatedTabsLayout() { return (

Every tab is registered by a different module.

@@ -45,7 +45,7 @@ export default function FederatedTabsLayout() { In the previous code sample, the `FederatedTabsLayout` is similar to the `RootLayout` introduced in previous guides. However, the key distinction is that this layout is nested under the `/federated-tabs` URL segment. By nesting the layout under a specific path, it will only render when the user navigates to one of the federated tab pages (e.g. `/federated-tabs/tab-1`, `/federated-tabs/tab-2`, `/federated-tabs/tab-3`). -To register the newly created layout as a nested layout, use the [registerRemoteModules](../reference/registration/registerRemoteModules.md) function: +To register the newly created layout as a nested layout, use the [registerRoutes](../reference/runtime/runtime-class.md#register-routes) function: ```tsx !#8-9 remote-module-3/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; @@ -75,7 +75,7 @@ As a bonus, each individual tab will have its own dedicated URL! :partying_face: ## Create the tab routes -Next, let's add the actual tab pages to the modules. To do so, we'll use the [layoutPath](../reference/runtime/runtime-class.md#register-routes-under-a-specific-nested-layout-route) option of the [registerRoutes](../reference/runtime/runtime-class.md#register-routes) function to register the routes under the `FederatedTabsLayout`: +Next, let's add the actual tab pages to the modules. To do so, we'll use the [parentPath](../reference/runtime/runtime-class.md#register-nested-routes-under-an-existing-route) option of the [registerRoutes](../reference/runtime/runtime-class.md#register-routes) function to register the routes under the `FederatedTabsLayout`: ```tsx !#8,11 remote-module-1/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; @@ -88,7 +88,7 @@ export const register: ModuleRegisterFunction = (runtime: Runtime) => { index: true element: } - ], { layoutPath: "/federated-tabs" }); + ], { parentPath: "/federated-tabs" }); } ``` @@ -111,7 +111,7 @@ export const register: ModuleRegisterFunction = runtime => { path: "/federated-tabs/tab-2" element: } - ], { layoutPath: "/federated-tabs" }); + ], { parentPath: "/federated-tabs" }); } ``` @@ -134,7 +134,7 @@ export const register: ModuleRegisterFunction = (runtime: Runtime) => { path: "/federated-tabs/tab-3" element: } - ], { layoutPath: "/federated-tabs" }); + ], { parentPath: "/federated-tabs" }); } ``` @@ -167,7 +167,7 @@ export const register: ModuleRegisterFunction = runtime => { index: true element: } - ], { layoutPath: "/federated-tabs" }); + ], { parentPath: "/federated-tabs" }); runtime.registerNavigationItems([ { @@ -191,7 +191,7 @@ export const register: ModuleRegisterFunction = (runtime: Runtime) => { path: "/federated-tabs/tab-2" element: } - ], { layoutPath: "/federated-tabs" }); + ], { parentPath: "/federated-tabs" }); runtime.registerNavigationItems([ { @@ -215,7 +215,7 @@ export const register: ModuleRegisterFunction = runtime => { path: "/federated-tabs/tab-3" element: } - ], { layoutPath: "/federated-tabs" }); + ], { parentPath: "/federated-tabs" }); runtime.registerNavigationItems([ { @@ -261,7 +261,7 @@ const renderSection: RenderSectionFunction = elements => { ); }; -export default function FederatedTabsLayout() { +export function FederatedTabsLayout() { const navigationItems = useNavigationItems("/federated-tabs"); const renderedTabs = useRenderedNavigationItems(navigationItems, renderItem, renderSection); @@ -283,7 +283,7 @@ export default function FederatedTabsLayout() { Similarly to how the display order of regular navigation items can be configured, a federated tab position can be affected with the [priority](http://localhost:5000/wl-squide/reference/runtime/runtime-class/#sort-registered-navigation-items) property. -To force `Tab 3` to be positioned first: +To force `Tab 3` to be positioned first, we'll give him a priority of `999`: ```tsx !#17 local-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; @@ -295,7 +295,7 @@ export const register: ModuleRegisterFunction = runtime => { path: "/federated-tabs/tab-3" element: } - ], { layoutPath: "/federated-tabs" }); + ], { parentPath: "/federated-tabs" }); runtime.registerNavigationItems([ { diff --git a/docs/guides/isolate-module-failures.md b/docs/guides/isolate-module-failures.md index 5b69c32ea..8b4ec5962 100644 --- a/docs/guides/isolate-module-failures.md +++ b/docs/guides/isolate-module-failures.md @@ -23,7 +23,7 @@ import { RootLayout } from "./RootLayout.tsx"; import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; export function App() { - const isReady = useAreModulesReady(); + const areModulesReady = useAreModulesReady(); const routes = useRoutes(); @@ -43,7 +43,7 @@ export function App() { }); }, [routes]); - if (!isReady) { + if (!areModulesReady) { return
Loading...
; } diff --git a/docs/guides/override-a-react-context.md b/docs/guides/override-a-react-context.md index 209c8ac41..bd6e88bc3 100644 --- a/docs/guides/override-a-react-context.md +++ b/docs/guides/override-a-react-context.md @@ -16,11 +16,11 @@ import { useAreModulesReady } from "@squide/webpack-module-federation"; import { RouterProvider } from "react-router-dom"; export function App() { - const isReady = useAreModulesReady(); + const areModulesReady = useAreModulesReady(); const router = useAppRouter(sessionManager); - if (!isReady) { + if (!areModulesReady) { return
Loading...
; } @@ -127,11 +127,11 @@ import { useAreModulesReady } from "@squide/webpack-module-federation"; import { RouterProvider } from "react-router-dom"; export function App() { - const isReady = useAreModulesReady(); + const areModulesReady = useAreModulesReady(); const router = useAppRouter(sessionManager); - if (!isReady) { + if (!areModulesReady) { return
Loading...
; } diff --git a/docs/guides/override-the-host-layout.md b/docs/guides/override-the-host-layout.md index 8281b6ae7..503ff2790 100644 --- a/docs/guides/override-the-host-layout.md +++ b/docs/guides/override-the-host-layout.md @@ -20,7 +20,7 @@ import { AuthenticationBoundary } from "./AuthenticationBoundary.tsx"; import { Home } from "./Home.tsx"; export function App() { - const isReady = useAreModulesReady(); + const areModulesReady = useAreModulesReady(); const routes = useRoutes(); @@ -47,7 +47,7 @@ export function App() { }); }, [routes]); - if (!isReady) { + if (!areModulesReady) { return
Loading...
; } @@ -119,7 +119,7 @@ import { AuthenticationBoundary } from "./AuthenticationBoundary.tsx"; import { Home } from "./Home.tsx"; export function App() { - const isReady = useAreModulesReady(); + const areModulesReady = useAreModulesReady(); const routes = useRoutes(); @@ -153,7 +153,7 @@ export function App() { return createBrowserRouter(hoistedRoutes); }, [hoistedRoutes]); - if (!isReady) { + if (!areModulesReady) { return
Loading...
; } @@ -168,7 +168,7 @@ export function App() { Then, mark the pages as hoisted and optionally use a new layout: -```tsx #12-13,16,19-20 local-module/src/register.tsx +```tsx #11-12,15,18-19 local-module/src/register.tsx import { lazy } from "react"; import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { LocalLayout } from "./LocalLayout.tsx"; @@ -197,6 +197,10 @@ export function register: ModuleRegisterFunction(runtime) { } ``` +!!!warning +By declaring a page as hoisted, the page will be rendered at the root of the router, therefore, most certainly outside the authenticated boundary of the application. If the hoisted page requires an authentication, make sure to **wrap the page with an authentication boundary** or to handle the authentication within the page. +!!! + [!ref text="For additional options, go to the `useHoistedRoutes` hook reference page"](/reference/routing/useHoistedRoutes.md) ## Try it :rocket: diff --git a/docs/reference/plugins/plugin.md b/docs/reference/plugins/plugin.md index e8369e4a7..748f232c0 100644 --- a/docs/reference/plugins/plugin.md +++ b/docs/reference/plugins/plugin.md @@ -11,7 +11,7 @@ An abstract base class to define a plugin. ### Define a plugin -```ts !#3 shared/src/mswPlugin.ts +```ts !#4 shared/src/mswPlugin.ts import { Plugin } from "@squide/react-router"; import type { RestHandler } from "msw"; @@ -52,7 +52,7 @@ mswPlugin.registerRequestHandlers(requetHandlers); We recommend pairing a plugin definition with a custom function to retrieve the plugin from a runtime instance. -```ts !#3 shared/src/mswPlugin.ts +```ts !#14-16 shared/src/mswPlugin.ts import { Plugin, type Runtime } from "@squide/react-router"; import type { RestHandler } from "msw"; diff --git a/docs/reference/registration/registerLocalModules.md b/docs/reference/registration/registerLocalModules.md index b2a838bc4..b26c783df 100644 --- a/docs/reference/registration/registerLocalModules.md +++ b/docs/reference/registration/registerLocalModules.md @@ -42,12 +42,10 @@ const context: AppContext = { registerLocalModules([register], runtime, { context }); ``` -```tsx !#7-21 local-module/src/register.tsx -import { lazy } from "react"; +```tsx !#5-19 local-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import type { AppContext } from "@sample/shared"; - -const About = lazy(() => import("./About.tsx")); +import { About } from "./About.tsx"; export function register: ModuleRegisterFunction(runtime, context) { runtime.registerRoutes([ diff --git a/docs/reference/registration/registerRemoteModules.md b/docs/reference/registration/registerRemoteModules.md index c337d11ff..00200fb84 100644 --- a/docs/reference/registration/registerRemoteModules.md +++ b/docs/reference/registration/registerRemoteModules.md @@ -52,12 +52,10 @@ const Remotes: RemoteDefinition = [ registerRemoteModules(Remotes, runtime, { context }); ``` -```tsx !#7-21 remote-module/src/register.tsx -import { lazy } from "react"; +```tsx !#5-19 remote-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import type { AppContext } from "@sample/shared"; - -const About = lazy(() => import("./About.tsx")); +import { About } from "./About.tsx"; export function register: ModuleRegisterFunction(runtime, context) { runtime.registerRoutes([ diff --git a/docs/reference/registration/useAreModulesReady.md b/docs/reference/registration/useAreModulesReady.md index 88b838039..5f2efba4f 100644 --- a/docs/reference/registration/useAreModulesReady.md +++ b/docs/reference/registration/useAreModulesReady.md @@ -10,7 +10,7 @@ Force the application to re-render once all the modules are registered. Without ## Reference ```ts -const isReady = useAreModulesReady(options?: { interval? }) +const areModulesReady = useAreModulesReady(options?: { interval? }) ``` ### Parameters @@ -57,7 +57,7 @@ export function App() { // Re-render the application once all the modules are registered. // Otherwise, the remotes routes won't be added to the router as the router will be // rendered before the remote modules registered their routes. - const isReady = useAreModulesReady(); + const areModulesReady = useAreModulesReady(); const routes = useRoutes(); @@ -65,7 +65,7 @@ export function App() { return createBrowserRouter(routes); }, [routes]); - if (!isReady) { + if (!areModulesReady) { return
Loading...
; } diff --git a/docs/reference/routing/useHoistedRoutes.md b/docs/reference/routing/useHoistedRoutes.md index d1348e356..e111b68f9 100644 --- a/docs/reference/routing/useHoistedRoutes.md +++ b/docs/reference/routing/useHoistedRoutes.md @@ -8,7 +8,11 @@ toc: Allow modules to register pages outside of the host application's elements boundary. Unlike a regular page, an hoisted page is added at the root of the router, meaning before the host application root layout, root error boundary and even root authentication boundary. Thus, an hoisted page has full control over its rendering. !!!warning -By declaring a page as hoisted, other parts of the application will not be isolated anymore from this page's failures as the page will be rendered outside of the host application's root error boundary. To avoid breaking the entire application when an hoisted page encounters unhandled errors, it is highly recommended to declare a React Router's [errorElement](https://reactrouter.com/en/main/route/error-element) property for each hoisted page. +By declaring a page as hoisted, other parts of the application will not be isolated anymore from this page's failures as the page will be rendered outside of the host application's root error boundary. To **avoid breaking the entire application** when an hoisted page encounters unhandled errors, it is highly recommended to declare a React Router's [errorElement](https://reactrouter.com/en/main/route/error-element) property for each hoisted page. +!!! + +!!!warning +By declaring a page as hoisted, the page will be rendered at the root of the router, therefore, most certainly outside the authenticated boundary of the application. If the hoisted page requires an authentication, make sure to **wrap the page with an authentication boundary** or to handle the authentication within the page. !!! ## Reference @@ -41,7 +45,7 @@ import { RootLayout } from "./RootLayout.tsx"; import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; export function App() { - const isReady = useAreModulesReady(); + const areModulesReady = useAreModulesReady(); const routes = useRoutes(); @@ -69,7 +73,7 @@ export function App() { return createBrowserRouter(hoistedRoutes); }, [hoistedRoutes]); - if (!isReady) { + if (!areModulesReady) { return
Loading...
; } @@ -82,12 +86,10 @@ export function App() { } ``` -```tsx !#11-12 remote-module/src/register.tsx -import { lazy } from "react"; +```tsx !#10-11 remote-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; - -const RemoteErrorBoundary = lazy(() => import("./RemoteErrorBoundary.tsx")); -const About = lazy(() => import("./About.tsx")); +import { RemoteErrorBoundary } from "./RemoteErrorBoundary.tsx"; +import { About } from "./About.tsx"; export function register: ModuleRegisterFunction(runtime) { runtime.registerRoutes([ @@ -123,7 +125,7 @@ import { RootLayout } from "./RootLayout.tsx"; import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; export function App() { - const isReady = useAreModulesReady(); + const areModulesReady = useAreModulesReady(); const routes = useRoutes(); @@ -147,7 +149,7 @@ export function App() { return createBrowserRouter(hoistedRoutes); }, [hoistedRoutes]); - if (!isReady) { + if (!areModulesReady) { return
Loading...
; } @@ -160,13 +162,11 @@ export function App() { } ``` -```tsx !#12,15,18,21-22 remote-module/src/register.tsx -import { lazy } from "react"; +```tsx !#10,13,16,19-20 remote-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; - -const RemoteLayout = lazy(() => import("./RemoteLayout.tsx")); -const RemoteErrorBoundary = lazy(() => import("./RemoteErrorBoundary.tsx")); -const About = lazy(() => import("./About.tsx")); +import { RemoteLayout } from "./RemoteLayout.tsx"; +import { RemoteErrorBoundary} from "./RemoteErrorBoundary.tsx"; +import { About } from "./About.tsx"; export function register: ModuleRegisterFunction(runtime) { runtime.registerRoutes([ @@ -211,7 +211,7 @@ import { RootLayout } from "./RootLayout.tsx"; import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; export function App() { - const isReady = useAreModulesReady(); + const areModulesReady = useAreModulesReady(); const routes = useRoutes(); @@ -241,7 +241,7 @@ export function App() { return createBrowserRouter(hoistedRoutes); }, [hoistedRoutes]); - if (!isReady) { + if (!areModulesReady) { return
Loading...
; } @@ -254,12 +254,10 @@ export function App() { } ``` -```tsx !#11-12,15 remote-module/src/register.tsx -import { lazy } from "react"; +```tsx !#9-10,13 remote-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; - -const RemoteErrorBoundary = lazy(() => import("./RemoteErrorBoundary.tsx")); -const Login = lazy(() => import("./Login.tsx")); +import { RemoteErrorBoundary } from "./RemoteErrorBoundary.tsx"; +import { Login } from "./Login.tsx"; export function register: ModuleRegisterFunction(runtime) { runtime.registerRoutes([ @@ -286,7 +284,7 @@ import { RootLayout } from "./RootLayout.tsx"; import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; export function App() { - const isReady = useAreModulesReady(); + const areModulesReady = useAreModulesReady(); const routes = useRoutes(); @@ -313,7 +311,7 @@ export function App() { return createBrowserRouter(hoistedRoutes); }, [hoistedRoutes]); - if (!isReady) { + if (!areModulesReady) { return
Loading...
; } diff --git a/docs/reference/routing/useRenderedNavigationItems.md b/docs/reference/routing/useRenderedNavigationItems.md index e6ddb7fae..0d15fd5ae 100644 --- a/docs/reference/routing/useRenderedNavigationItems.md +++ b/docs/reference/routing/useRenderedNavigationItems.md @@ -79,7 +79,7 @@ const renderSection: RenderSectionFunction = (elements, index, level) => { ); }; -export default function RootLayout() { +export function RootLayout() { const navigationItems = useNavigationItems(); const navigationElements = useRenderedNavigationItems(navigationItems, renderItem, renderSection); diff --git a/docs/reference/runtime/runtime-class.md b/docs/reference/runtime/runtime-class.md index 38da37716..3b83e50dc 100644 --- a/docs/reference/runtime/runtime-class.md +++ b/docs/reference/runtime/runtime-class.md @@ -57,12 +57,17 @@ const runtime = new Runtime({ ### Register routes -A Squide route accept any properties of a React Router [Route](https://reactrouter.com/en/main/components/route) component with the addition of an `hoist` property. +A Squide route can either be a `RootRoute` or a `Route`. -```tsx -import { lazy } from "react"; +- `RootRoute`: accept any properties of a React Router [Route](https://reactrouter.com/en/main/components/route) component with the addition of: + - `hoist`: An optional boolean property to register the route at the root of the router. The default value is `false`. + - `visibility`: An optional property to register a route as a `public` or `authenticated` route. The default value is `authenticated`. + - `name`: An optional name for the route. +- `Route`: accept any properties of a React Router [Route](https://reactrouter.com/en/main/components/route) component with the addition of: + - `name`: An optional name for the route. -const Page = lazy(() => import("./Page.tsx")); +```tsx +import { Page } from "./Page.tsx" // Register a new route from a local or remote module. runtime.registerRoutes([ @@ -73,14 +78,12 @@ runtime.registerRoutes([ ]); ``` -### Register an hoisted page +### Register an hoisted route Unlike a regular page, a hoisted page is added at the root of the router, outside of the boundaries of the host application's root layout. This means that a hoisted page has full control over its rendering. -```tsx !#9 -import { lazy } from "react"; - -const Page = lazy(() => import("./Page.tsx")); +```tsx !#7 +import { Page } from "./Page.tsx"; runtime.registerRoutes([ { @@ -93,68 +96,75 @@ runtime.registerRoutes([ [!ref text="Setup the host application to accept hoisted routes"](/reference/routing/useHoistedRoutes.md) -### Register routes under a specific nested layout route +### Register a public route -React router [nested routes](https://reactrouter.com/en/main/start/tutorial#nested-routes) enable applications to render nested layouts at various points within the router tree. This is quite helpful for federated applications as it enables composable and decoupled UI. +When registering a route, a hint can be provided to indicate if the route is intended to be displayed as a `public` or `authenticated` route. This is especially useful when dealing with code that conditionally fetch data for authenticated routes. + +```tsx !#7 +import { Page } from "./Page.tsx"; + +runtime.registerRoutes([ + { + path: "/page-1", + element: , + visibility: "public" + } +]); +``` -To fully harness the power of nested routes, the `registerRoutes` function allows a route to be registered **under any** previously registered **nested layout route**, even if that route was registered by another module. +### Register a named route -When registering a new route with the `registerRoutes` function, to render the route under a specific nested layout route, specify a `layoutPath` property that matches the nested layout route's `path` property. The only requirement is that the **nested layout route** must be registered with `registerRoutes`. +The `registerRoutes` function accepts a `parentName` property, allowing a route to be [nested under an existing parent route](#register-nested-routes-under-an-existing-route). When searching for the parent route matching the `parentName` property, the `parentName` will be matched against the `name` property of every route. -```tsx !#10 -import { lazy } from "react"; +> A `name` property should usually only be defined for routes that doesn't have a path. -const Page = lazy(() => import("./Page.tsx")); +```tsx !#5 +import { Page } from "./Page.tsx"; runtime.registerRoutes([ { - path: "/layout/page-1", + name: "page-1", element: } -], { layoutPath: "/layout" }); // Register the page under the "/layout" nested layout. +]); ``` -!!!info -Likewise any other React Router routes, the `path` property of a page rendered under a nested layout must be an absolute path. For example, if a nested layout `path` is `/layout`, the `path` property of a page rendered under that layout route and responding to the `/page-1` url, should be `/layout/page-1`. -!!! +### Register nested routes under an existing route -#### Index routes +React router [nested routes](https://reactrouter.com/en/main/start/tutorial#nested-routes) enable applications to render nested layouts at various points within the router tree. This is quite helpful for federated applications as it enables composable and decoupled UI. -Although nested layout routes that serve as indexes (e.g. `{ index: true, element: }`) are not very common, Squide still supports this scenario. To register a route **under an index route**, set the `layoutPath` property as the concatenation of the index route's parent path and `/$index$`. +To fully harness the power of nested routes, the `registerRoutes` function allows a route to be registered **under any** previously **registered route**, even if that route was registered by another module. The only requirement is that the **parent route** must have been registered with the `registerRoutes` function. -```tsx !#8,12 host/src/register.tsx -import { lazy } from "react"; +When registering a new route with the `registerRoutes` function, to render the route under a parent route, specify a `parentPath` property that matches the parent route's `path` property: -const Page = lazy(() => import("./Page.tsx")); -const Layout = lazy(() => import("./Layout.tsx")); +```tsx !#8 +import { Page } from "./Page.tsx"; runtime.registerRoutes([ { - path: "/page-1", - element: , - children: [ - { - index: true, - element: - } - ] + path: "/layout/page-1", + element: } -]); +], { parentPath: "/layout" }); // Register the page under an existing route having "/layout" as its "path". ``` -```tsx !#10 remote-module/src/register.tsx -import { lazy } from "react"; +Or a `parentName` property that matches the parent route's `name` property: -const Page = lazy(() => import("./Page.tsx")); +```tsx !#8 +import { Page } from "./Page.tsx"; runtime.registerRoutes([ { - path: "/page-1/page-2", + path: "/layout/page-1", element: } -], { layoutPath: "/page-1/$index$" }); // Using $index$ to match "index: true" +], { parentName: "layout" }); // Register the page under an existing route having "layout" as its "name". ``` +!!!info +Likewise any other React Router routes, the `path` property of a page rendered under an existing parent route must be an absolute path. For example, if a parent route `path` is `/layout`, the `path` property of a page rendered under that parent route and responding to the `/page-1` url, should be `/layout/page-1`. +!!! + ### Retrieve routes A federated application routes are accessible from a `Runtime` instance, but keep in mind that the preferred way to retrieve the routes is with the [useRoutes](./useRoutes) hook. diff --git a/docs/reference/services/service.md b/docs/reference/services/service.md index 04e525c98..f0368f0a8 100644 --- a/docs/reference/services/service.md +++ b/docs/reference/services/service.md @@ -52,7 +52,7 @@ const telemetryService = runtime.getService(TelemetryService.name); We recommend pairing a custom service definition with a custom function or hook to retrieve the service from a runtime instance. -```ts !#9-11,13-15 shared/src/telemetryService.ts +```ts !#9-11,13-17 shared/src/telemetryService.ts import { Service, useRuntime, type Runtime } from "@squide/react-router"; export class TelemetryService extends Service { diff --git a/packages/core/src/runtime/RuntimeContext.ts b/packages/core/src/runtime/RuntimeContext.ts index 29e821602..c2af1810e 100644 --- a/packages/core/src/runtime/RuntimeContext.ts +++ b/packages/core/src/runtime/RuntimeContext.ts @@ -1,5 +1,4 @@ import { createContext, useContext } from "react"; - import { isNil } from "../shared/assertions.ts"; import type { AbstractRuntime } from "./abstractRuntime.ts"; diff --git a/packages/core/src/runtime/abstractRuntime.ts b/packages/core/src/runtime/abstractRuntime.ts index 000e0fe1a..b5cb45d88 100644 --- a/packages/core/src/runtime/abstractRuntime.ts +++ b/packages/core/src/runtime/abstractRuntime.ts @@ -17,7 +17,8 @@ export interface RuntimeOptions { } export interface RegisterRoutesOptions { - layoutPath?: string; + parentPath?: string; + parentName?: string; } export interface RegisterNavigationItemsOptions { diff --git a/packages/msw/package.json b/packages/msw/package.json index c20622601..622f6ea80 100644 --- a/packages/msw/package.json +++ b/packages/msw/package.json @@ -31,15 +31,21 @@ "build": "tsup --config ./tsup.build.ts" }, "peerDependencies": { + "react": "*", + "react-dom": "*", "msw": "*" }, "devDependencies": { "@types/jest": "29.5.5", + "@types/react": "18.2.22", + "@types/react-dom": "18.2.7", "@workleap/eslint-plugin": "2.1.1", "@workleap/tsup-configs": "3.0.1", "@workleap/typescript-configs": "3.0.2", "jest": "29.7.0", "msw": "1.3.2", + "react": "18.2.0", + "react-dom": "18.2.0", "tsup": "7.2.0", "typescript": "5.2.2" }, diff --git a/packages/msw/src/index.ts b/packages/msw/src/index.ts index 465ac19e8..00bf92fcb 100644 --- a/packages/msw/src/index.ts +++ b/packages/msw/src/index.ts @@ -1,3 +1,4 @@ export * from "./mswPlugin.ts"; export * from "./requestHandlerRegistry.ts"; +export * from "./useIsMswReady.ts"; diff --git a/packages/msw/src/mswPlugin.ts b/packages/msw/src/mswPlugin.ts index 3a5108bb1..04f75f206 100644 --- a/packages/msw/src/mswPlugin.ts +++ b/packages/msw/src/mswPlugin.ts @@ -4,6 +4,7 @@ import { RequestHandlerRegistry } from "./requestHandlerRegistry.ts"; export class MswPlugin extends Plugin { readonly #requestHandlerRegistry = new RequestHandlerRegistry(); + #isMswStarted = false; constructor() { super(MswPlugin.name); @@ -16,6 +17,14 @@ export class MswPlugin extends Plugin { get requestHandlers(): RestHandler[] { return this.#requestHandlerRegistry.handlers; } + + setAsStarted() { + this.#isMswStarted = true; + } + + get isStarted() { + return this.#isMswStarted; + } } export function getMswPlugin(runtime: AbstractRuntime) { diff --git a/packages/msw/src/useIsMswReady.ts b/packages/msw/src/useIsMswReady.ts new file mode 100644 index 000000000..91d79e894 --- /dev/null +++ b/packages/msw/src/useIsMswReady.ts @@ -0,0 +1,37 @@ +import { useRuntime } from "@squide/core"; +import { useEffect, useState } from "react"; +import { getMswPlugin } from "./mswPlugin.ts"; + +export interface UseIsMswStartedOptions { + // The interval is in milliseconds. + interval?: number; +} + +export function useIsMswStarted(enabled: boolean, { interval = 10 }: UseIsMswStartedOptions = {}) { + const runtime = useRuntime(); + const mswPlugin = getMswPlugin(runtime); + + // Using a state hook to force a rerender once MSW is started. + const [value, setIsStarted] = useState(!enabled); + + // Perform a reload once MSW is started. + useEffect(() => { + if (enabled) { + const intervalId = setInterval(() => { + if (mswPlugin.isStarted) { + // Must clear interval before calling "_completeRegistration" in case there's an error. + clearInterval(intervalId); + setIsStarted(true); + } + }, interval); + + return () => { + if (intervalId) { + clearInterval(intervalId); + } + }; + } + }, [enabled]); + + return value; +} diff --git a/packages/react-router/src/routeRegistry.ts b/packages/react-router/src/routeRegistry.ts index cb4edbab3..93308cba1 100644 --- a/packages/react-router/src/routeRegistry.ts +++ b/packages/react-router/src/routeRegistry.ts @@ -1,28 +1,45 @@ import type { RegisterRoutesOptions } from "@squide/core"; -import type { RouteObject } from "react-router-dom"; +import type { IndexRouteObject, NonIndexRouteObject } from "react-router-dom"; -export type Route = RouteObject; +export type RouteVisibility = "public" | "authenticated"; + +export type RouteType = "root"; + +export interface IndexRoute extends IndexRouteObject { + name?: string; +} + +export interface NonIndexRoute extends Omit { + name?: string; + children?: Route[]; +} + +export type Route = IndexRoute | NonIndexRoute; export type RootRoute = Route & { hoist?: boolean; + visibility?: RouteVisibility; + type?: RouteType; }; -const IndexToken = "$index$"; - -function normalizePath(routePath: string) { - if (routePath !== "/" && routePath.endsWith("/")) { +function normalizePath(routePath?: string) { + if (routePath && routePath !== "/" && routePath.endsWith("/")) { return routePath.substring(0, routePath.length - 1); } return routePath; } -export function createIndexKey(route: Route, layoutPath: string) { - if (route.index) { - return layoutPath.endsWith("/") ? `${layoutPath}${IndexToken}` : `${layoutPath}/${IndexToken}`; +export function createIndexKey(route: Route) { + if (route.path) { + return normalizePath(route.path); } - return normalizePath(route.path!); + if (route.name) { + return route.name; + } + + return undefined; } export type RouteRegistrationStatus = "pending" | "registered"; @@ -40,41 +57,89 @@ export class RouteRegistry { readonly #routesIndex: Map = new Map(); // A collection of pending routes to registered once their layout is registered. - // + // readonly #pendingRegistrations: Map = new Map(); constructor() { this.#routes = []; } - add(routes: RootRoute[] | Route[], { layoutPath }: RegisterRoutesOptions = {}) { - if (layoutPath) { - return this.#addNestedRoutes(routes, layoutPath); + add(routes: RootRoute[] | Route[], { parentPath, parentName }: RegisterRoutesOptions = {}) { + if (parentPath) { + // The normalized path cannot be undefined because it's been provided by the consumer + // (e.g. it cannot be a pathless route). + return this.#addNestedRoutes(routes, normalizePath(parentPath)!); + } + + if (parentName) { + return this.#addNestedRoutes(routes, parentName); } return this.#addRootRoutes(routes); } + #addIndex(route: Route) { + const key = createIndexKey(route); + + if (key) { + if (this.#routesIndex.has(key)) { + throw new Error(`[squide] A route index has already been registered for the key: "${key}". Did you register 2 routes with the same "path" or "name" property?`); + } + + this.#routesIndex.set(key, route); + } + + return key; + } + + #recursivelyAddIndexes(route: Route) { + const newIndexes: string[] = []; + const key = this.#addIndex(route); + + if (key) { + newIndexes.push(key); + } + + if (route.children) { + route.children.forEach(x => { + const indexes = this.#recursivelyAddIndexes(x); + + newIndexes.push(...indexes); + }); + } + + return newIndexes; + } + #addRootRoutes(routes: RootRoute[]): AddRouteReturnType { + // Creates a copy of the route objects and a type to each route to indicate + // that it's a root route. + const _routes: RootRoute[] = routes.map(x => ({ + ...x, + visibility: x.visibility ?? "authenticated", + type: "root" + })); + + const newIndexes: string[] = []; + // Add index entries to speed up the registration of future nested routes. - routes.forEach(x => { - const key = createIndexKey(x, "/"); + // This is done recursively to also register indexes for the nested routes if there are any. + _routes.forEach(x => { + const indexes = this.#recursivelyAddIndexes(x); - this.#routesIndex.set(key, x); + newIndexes.push(...indexes); }); // Create a new array so the routes array is immutable. - this.#routes = [...this.#routes, ...routes]; + this.#routes = [...this.#routes, ..._routes]; - let completedPendingRegistrations; + const completedPendingRegistrations: Route[] = []; - routes.forEach(x => { - // Since the layoutPath is used as the key for pending routes and the - // layoutPath is the same as the index key, an index key for the route must be created - // to retrieve the route pending registrations. - const key = createIndexKey(x, "/"); + // Use the new indexes to retrieve the route pending registrations and complete their registration. + newIndexes.forEach(x => { + const pendingRegistrations = this.#tryRegisterPendingRoutes(x); - completedPendingRegistrations = this.#tryRegisterPendingRoutes(key); + completedPendingRegistrations.push(...pendingRegistrations); }); return { @@ -83,17 +148,19 @@ export class RouteRegistry { }; } - #addNestedRoutes(routes: Route[], layoutPath: string): AddRouteReturnType { - const indexKey = normalizePath(layoutPath); - const layoutRoute = this.#routesIndex.get(indexKey); + #addNestedRoutes(routes: Route[], parentId: string): AddRouteReturnType { + // Creates a copy of the route objects. + const _routes: Route[] = routes.map(x => ({ ...x })); + + const layoutRoute = this.#routesIndex.get(parentId); if (!layoutRoute) { - const pendingRegistration = this.#pendingRegistrations.get(layoutPath); + const pendingRegistration = this.#pendingRegistrations.get(parentId); if (pendingRegistration) { - pendingRegistration.push(...routes); + pendingRegistration.push(..._routes); } else { - this.#pendingRegistrations.set(layoutPath, [...routes]); + this.#pendingRegistrations.set(parentId, [..._routes]); } return { @@ -104,29 +171,30 @@ export class RouteRegistry { // Register new nested routes as children of their layout route. layoutRoute.children = [ ...(layoutRoute.children ?? []), - ...routes + ..._routes ]; + const newIndexes: string[] = []; + // Add index entries to speed up the registration of future nested routes. - routes.forEach(x => { - const key = createIndexKey(x, layoutPath); + // This is done recursively to also register indexes for the nested routes if there are any. + _routes.forEach(x => { + const indexes = this.#recursivelyAddIndexes(x); - this.#routesIndex.set(key, x); + newIndexes.push(...indexes); }); // Create a new array since the routes array is immutable and a nested // object has been updated. this.#routes = [...this.#routes]; - let completedPendingRegistrations; + const completedPendingRegistrations: Route[] = []; - routes.forEach(x => { - // Since the layoutPath is used as the key for pending routes and the - // layoutPath is the same as the index key, an index key for the route must be created - // to retrieve the route pending registrations. - const key = createIndexKey(x, "/"); + // Use the new indexes to retrieve the route pending registrations and complete their registration. + newIndexes.forEach(x => { + const pendingRegistrations = this.#tryRegisterPendingRoutes(x); - this.#tryRegisterPendingRoutes(key); + completedPendingRegistrations.push(...pendingRegistrations); }); return { @@ -135,20 +203,20 @@ export class RouteRegistry { }; } - #tryRegisterPendingRoutes(layoutPath: string) { - const pendingRegistrations = this.#pendingRegistrations.get(layoutPath); + #tryRegisterPendingRoutes(parentId: string) { + const pendingRegistrations = this.#pendingRegistrations.get(parentId); if (pendingRegistrations) { // Try to register the pending routes. - this.#addNestedRoutes(pendingRegistrations, layoutPath); + this.#addNestedRoutes(pendingRegistrations, parentId); // Remove the pending registrations. - this.#pendingRegistrations.delete(layoutPath); + this.#pendingRegistrations.delete(parentId); return pendingRegistrations; } - return undefined; + return []; } get routes() { diff --git a/packages/react-router/src/runtime.ts b/packages/react-router/src/runtime.ts index 31c0ea026..0d56a1a1c 100644 --- a/packages/react-router/src/runtime.ts +++ b/packages/react-router/src/runtime.ts @@ -10,7 +10,8 @@ export class Runtime extends AbstractRuntime 0) { - let message = `[squide] ${pendingRegistrations.size} layout route${pendingRegistrations.size !== 1 ? "s" : ""} were expected to be registered but ${pendingRegistrations.size !== 1 ? "are" : "is"} missing:\r\n\r\n`; + let message = `[squide] ${pendingRegistrations.size} parent route${pendingRegistrations.size !== 1 ? "s" : ""} were expected to be registered but ${pendingRegistrations.size !== 1 ? "are" : "is"} missing:\r\n\r\n`; let index = 0; // It's easier to use for ... of with a Map object. - for (const [layoutPath, nestedRoutes] of pendingRegistrations) { + for (const [parentId, nestedRoutes] of pendingRegistrations) { index++; - message += `${index}/${pendingRegistrations.size} Missing layout path: "${layoutPath}"\r\n`; + message += `${index}/${pendingRegistrations.size} Missing parent route with the following path or name: "${parentId}"\r\n`; message += " Pending registrations:\r\n"; for (const x of nestedRoutes) { - message += ` - "${x.index ? `${layoutPath} (index)` : x.path}"\r\n`; + message += ` - "${x.path ?? x.name ?? "(pathless route)"}"\r\n`; } message += "\r\n"; } - message += `If you are certain that the layout route${pendingRegistrations.size !== 1 ? "s" : ""} has been registered, make sure that the following conditions are met:\r\n`; - message += "- The missing nested layout routes path property perfectly match the provided \"layoutPath\" (make sure that there's no leading or trailing \"/\" that differs).\r\n"; - message += "- The missing nested layout routes has been registered with the \"registerRoutes()\" function. A route cannot be registered under a nested layout route that has not be registered with the \"registerRoutes()\" function.\r\n"; - message += "- If a nested layout route is an index route, make sure that \"/$index$\" string has been appended to the \"layoutPath\".\r\n\r\n"; - message += "For more information about nested layout routes, refers to https://gsoft-inc.github.io/wl-squide/reference/runtime/runtime-class/#register-routes-under-a-specific-nested-layout-route.\r\n"; + message += `If you are certain that the parent route${pendingRegistrations.size !== 1 ? "s" : ""} has been registered, make sure that the following conditions are met:\r\n`; + message += "- The missing parent routes \"path\" or \"name\" property perfectly match the provided \"parentPath\" or \"parentName\" (make sure that there's no leading or trailing \"/\" that differs).\r\n"; + message += "- The missing parent routes has been registered with the \"registerRoutes()\" function. A route cannot be registered under a parent route that has not be registered with the \"registerRoutes()\" function.\r\n"; + message += "For more information about nested routes, refers to https://gsoft-inc.github.io/wl-squide/reference/runtime/runtime-class/#register-routes-under-a-specific-nested-layout-route.\r\n"; if (this._mode === "development") { throw new Error(message); diff --git a/packages/react-router/tests/runtime.test.tsx b/packages/react-router/tests/runtime.test.tsx index 0fa460270..c8fac0a50 100644 --- a/packages/react-router/tests/runtime.test.tsx +++ b/packages/react-router/tests/runtime.test.tsx @@ -2,262 +2,637 @@ import { createIndexKey } from "../src/routeRegistry.ts"; import { Runtime } from "../src/runtime.ts"; describe("createIndexKey", () => { - test("when the route is an index route, append \"index\" to the parent path", () => { - const result1 = createIndexKey({ - index: true, - element:
Hello!
- }, "/"); - - expect(result1).toBe("/$index$"); - - const result2 = createIndexKey({ + test("when the route is an index route, return undefined", () => { + const result = createIndexKey({ index: true, element:
Hello!
- }, "/parent"); + }); - expect(result2).toBe("/parent/$index$"); + expect(result).toBeUndefined(); }); - test("when the route is not an index route, return the route path", () => { + test("when the route has a path, return the route path", () => { const result1 = createIndexKey({ path: "/nested", element:
Hello!
- }, "/"); + }); expect(result1).toBe("/nested"); const result2 = createIndexKey({ path: "/parent/nested", element:
Hello!
- }, "/parent"); + }); expect(result2).toBe("/parent/nested"); }); - test("when the route is not an index route and the path ends with a separator, strip the separator", () => { + test("when the route has a path and the path ends with a separator, strip the separator", () => { const result = createIndexKey({ path: "/parent/nested/", element:
Hello!
- }, "/parent"); + }); expect(result).toBe("/parent/nested"); }); + + test("when the route has a name, return the route name", () => { + const result = createIndexKey({ + name: "foo", + element:
Hello!
+ }); + + expect(result).toBe("foo"); + }); + + test("when this a pathless route, return undefined", () => { + const result = createIndexKey({ + element:
Hello!
+ }); + + expect(result).toBeUndefined(); + }); }); describe("registerRoutes", () => { - test("can register a root route", () => { + test("can register an index route", () => { const runtime = new Runtime(); runtime.registerRoutes([ { - path: "/root", - element:
Hello!
, - hoist: true + index: true, + element:
Hello!
} ]); expect(runtime.routes.length).toBe(1); - expect(runtime.routes[0].path).toBe("/root"); + expect(runtime.routes[0].index).toBeTruthy(); }); - test("when the layout route has already been registered, register the nested route", () => { + test("can register a pathless route", () => { const runtime = new Runtime(); runtime.registerRoutes([ { - path: "/layout", element:
Hello!
} ]); expect(runtime.routes.length).toBe(1); - - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { layoutPath: "/layout" }); - - expect(runtime.routes.length).toBe(1); - expect(runtime.routes[0].children).toBeDefined(); - expect(runtime.routes[0].children?.length).toBe(1); + expect(runtime.routes[0].index).toBeUndefined(); + expect(runtime.routes[0].path).toBeUndefined(); }); - test("when the layout route has not been registered, do not register the nested route", () => { + test("can register multiple pathless routes", () => { const runtime = new Runtime(); runtime.registerRoutes([ { - path: "/layout/nested", element:
Hello!
+ }, + { + element:
How
+ }, + { + element:
Are
+ }, + { + element:
You?
} - ], { layoutPath: "/layout" }); + ]); - expect(runtime.routes.length).toBe(0); + expect(runtime.routes.length).toBe(4); }); - test("when the layout route has not been registered, register the pending route once the layout route is registered", () => { + test("can register a deeply nested route with pathless parent routes", () => { const runtime = new Runtime(); runtime.registerRoutes([ { - path: "/layout/nested", - element:
Hello!
- } - ], { layoutPath: "/layout" }); - - runtime.registerRoutes([ - { - path: "/layout/another-nested", - element:
Hello!
- } - ], { layoutPath: "/layout" }); - - expect(runtime.routes.length).toBe(0); - - runtime.registerRoutes([ - { - path: "/foo", - element:
Hello!
+ element:
Hello
, + children: [ + { + element:
You!
, + children: [ + { + path: "/deeply-nested-route", + element:
Hello from nested!
+ } + ] + } + ] } ]); expect(runtime.routes.length).toBe(1); - expect(runtime.routes[0].children).toBeUndefined(); + expect(runtime.routes[0].children![0].children![0].path).toBe("/deeply-nested-route"); + }); + + test("can register a deeply nested index route with pathless parent routes", () => { + const runtime = new Runtime(); runtime.registerRoutes([ { - path: "/layout", - element:
Hello!
+ element:
Hello
, + children: [ + { + element:
You!
, + children: [ + { + index: true, + element:
Hello from nested!
+ } + ] + } + ] } ]); - expect(runtime.routes.length).toBe(2); - expect(runtime.routes[1].children).toBeDefined(); - expect(runtime.routes[1].children?.length).toBe(2); + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].children![0].children![0].index).toBeTruthy(); }); - test("can register a deeply nested route", () => { + test("can register an hoisted route", () => { const runtime = new Runtime(); runtime.registerRoutes([ { - path: "/layout", - element:
Hello!
+ path: "/root", + element:
Hello!
, + hoist: true } ]); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { layoutPath: "/layout" }); - - runtime.registerRoutes([ - { - path: "/layout/nested/another-level", - element:
Hello!
- } - ], { layoutPath: "/layout/nested" }); - expect(runtime.routes.length).toBe(1); - expect(runtime.routes[0].children![0].children).toBeDefined(); - expect(runtime.routes[0].children![0].children?.length).toBe(1); + expect(runtime.routes[0].path).toBe("/root"); + expect(runtime.routes[0].hoist).toBeTruthy(); }); - test("when the layout path has a trailing separator but the layout route path doesn't have a trailing separator, the nested route is registered", () => { + test("can register a route with a \"public\" visibility", () => { const runtime = new Runtime(); runtime.registerRoutes([ { - path: "/layout", - element:
Hello!
+ path: "/public", + element:
Hello!
, + visibility: "public" } ]); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { layoutPath: "/layout/" }); - - expect(runtime.routes[0].children).toBeDefined(); - expect(runtime.routes[0].children!.length).toBe(1); + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].path).toBe("/public"); + expect(runtime.routes[0].visibility).toBe("public"); }); - test("when the layout path doesn't have a trailing separator but the layout route path have a trailing separator, the nested route is registered", () => { + test("can register a route with a \"authenticated\" visibility", () => { const runtime = new Runtime(); runtime.registerRoutes([ { - path: "/layout/", - element:
Hello!
+ path: "/authenticated", + element:
Hello!
, + visibility: "authenticated" } ]); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { layoutPath: "/layout" }); - - expect(runtime.routes[0].children).toBeDefined(); - expect(runtime.routes[0].children!.length).toBe(1); + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].path).toBe("/authenticated"); + expect(runtime.routes[0].visibility).toBe("authenticated"); }); - test("can register a nested route for an index layout route", () => { + test("can register a root route with a name", () => { const runtime = new Runtime(); runtime.registerRoutes([ { - path: "/layout", + name: "foo", element:
Hello!
} ]); - runtime.registerRoutes([ - { - index: true, - element:
Hello!
- } - ], { layoutPath: "/layout" }); - - runtime.registerRoutes([ - { - path: "/layout/another-level", - element:
Hello!
- } - ], { layoutPath: "/layout/$index$" }); - - expect(runtime.routes[0].children![0].children).toBeDefined(); - expect(runtime.routes[0].children![0].children?.length).toBe(1); - expect(runtime.routes[0].children![0].children![0].path).toBe("/layout/another-level"); + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].name).toBe("foo"); }); - test("can register a nested route for the root index route", () => { + test("can register a nested route with a name", () => { const runtime = new Runtime(); runtime.registerRoutes([ { - index: true, - element:
Hello!
+ element:
Hello
, + children: [ + { + name: "foo", + element:
You!
+ } + ] } ]); - runtime.registerRoutes([ - { - path: "/nested", - element:
Hello!
- } - ], { layoutPath: "/$index$" }); + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].children![0].name).toBe("foo"); + }); + + describe("parentPath", () => { + test("when the parent route has already been registered, register the nested route", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + path: "/layout", + element:
Hello!
+ } + ]); + + expect(runtime.routes.length).toBe(1); + + runtime.registerRoutes([ + { + path: "/layout/nested", + element:
Hello!
+ } + ], { parentPath: "/layout" }); + + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].children![0].path).toBe("/layout/nested"); + }); + + test("when the parent route has not been registered, do not register the nested route", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + path: "/layout/nested", + element:
Hello!
+ } + ], { parentPath: "/layout" }); + + expect(runtime.routes.length).toBe(0); + }); + + test("when the parent route has not been registered, register the pending route once the parent route is registered", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + path: "/layout/nested", + element:
Hello!
+ } + ], { parentPath: "/layout" }); + + runtime.registerRoutes([ + { + path: "/layout/another-nested", + element:
Hello!
+ } + ], { parentPath: "/layout" }); + + expect(runtime.routes.length).toBe(0); + + runtime.registerRoutes([ + { + path: "/foo", + element:
Hello!
+ } + ]); + + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].children).toBeUndefined(); + + runtime.registerRoutes([ + { + path: "/layout", + element:
Hello!
+ } + ]); + + expect(runtime.routes.length).toBe(2); + expect(runtime.routes[1].children?.length).toBe(2); + }); + + test("when the parent route has not been registered, and the parent route is nested in a pending registration single block, register the pending route once the parent route is registered", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + path: "/layout/nested", + element:
Hello!
+ } + ], { parentPath: "/layout" }); + + expect(runtime.routes.length).toBe(0); + + runtime.registerRoutes([ + { + element:
Hello
, + children: [ + { + element:
You!
, + children: [ + { + path: "/layout", + element:
Hello from nested!
+ } + ] + } + ] + } + ]); + + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].path).toBeUndefined(); + expect(runtime.routes[0].children![0].children![0].children![0].path).toBe("/layout/nested"); + }); + + test("can register a route under a deeply nested layout", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + path: "/layout", + element:
Hello!
+ } + ]); + + runtime.registerRoutes([ + { + path: "/layout/nested", + element:
Hello!
+ } + ], { parentPath: "/layout" }); + + runtime.registerRoutes([ + { + path: "/layout/nested/another-level", + element:
Hello!
+ } + ], { parentPath: "/layout/nested" }); + + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].children![0].children![0].path).toBe("/layout/nested/another-level"); + }); + + test("can register a route under a deeply nested layout that has been registered in a single block", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + element:
Hello
, + children: [ + { + element:
You!
, + children: [ + { + path: "/deeply-nested-layout", + element:
Hello from nested!
+ } + ] + } + ] + } + ]); + + runtime.registerRoutes([ + { + path: "/deeply-nested-layout/another-level", + element:
Hello!
+ } + ], { parentPath: "/deeply-nested-layout" }); + + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].children![0].children![0].children![0].path).toBe("/deeply-nested-layout/another-level"); + + runtime.registerRoutes([ + { + path: "/deeply-nested-layout/another-level/yet-another-level", + element:
Hello!
+ } + ], { parentPath: "/deeply-nested-layout/another-level" }); + + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].children![0].children![0].children![0].children![0].path).toBe("/deeply-nested-layout/another-level/yet-another-level"); + }); + + test("when the specified parent path has a trailing separator but the parent route path doesn't have a trailing separator, the nested route is registered", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + path: "/layout", + element:
Hello!
+ } + ]); + + runtime.registerRoutes([ + { + path: "/layout/nested", + element:
Hello!
+ } + ], { parentPath: "/layout/" }); + + expect(runtime.routes[0].children![0].path).toBe("/layout/nested"); + }); + + test("when the specified parent path doesn't have a trailing separator but the parent route path have a trailing separator, the nested route is registered", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + path: "/layout/", + element:
Hello!
+ } + ]); + + runtime.registerRoutes([ + { + path: "/layout/nested", + element:
Hello!
+ } + ], { parentPath: "/layout" }); + + expect(runtime.routes[0].children![0].path).toBe("/layout/nested"); + }); + }); - expect(runtime.routes[0].children).toBeDefined(); - expect(runtime.routes[0].children?.length).toBe(1); - expect(runtime.routes[0].children![0].path).toBe("/nested"); + describe("parentName", () => { + test("when the parent route has already been registered, register the nested route", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + name: "layout", + element:
Hello!
+ } + ]); + + expect(runtime.routes.length).toBe(1); + + runtime.registerRoutes([ + { + path: "/layout/nested", + element:
Hello!
+ } + ], { parentName: "layout" }); + + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].children![0].path).toBe("/layout/nested"); + }); + + test("when the parent route has not been registered, do not register the nested route", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + path: "/layout/nested", + element:
Hello!
+ } + ], { parentName: "layout" }); + + expect(runtime.routes.length).toBe(0); + }); + + test("when the parent route has not been registered, register the pending route once the parent route is registered", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + path: "/layout/nested", + element:
Hello!
+ } + ], { parentName: "layout" }); + + runtime.registerRoutes([ + { + path: "/layout/another-nested", + element:
Hello!
+ } + ], { parentName: "layout" }); + + expect(runtime.routes.length).toBe(0); + + runtime.registerRoutes([ + { + path: "/foo", + element:
Hello!
+ } + ]); + + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].children).toBeUndefined(); + + runtime.registerRoutes([ + { + name: "layout", + element:
Hello!
+ } + ]); + + expect(runtime.routes.length).toBe(2); + expect(runtime.routes[1].children?.length).toBe(2); + }); + + test("when the parent route has not been registered, and the parent route is nested in a pending registration single block, register the pending route once the parent route is registered", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + path: "/layout/nested", + element:
Hello!
+ } + ], { parentName: "layout" }); + + expect(runtime.routes.length).toBe(0); + + runtime.registerRoutes([ + { + element:
Hello
, + children: [ + { + element:
You!
, + children: [ + { + name: "layout", + element:
Hello from nested!
+ } + ] + } + ] + } + ]); + + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].path).toBeUndefined(); + expect(runtime.routes[0].children![0].children![0].children![0].path).toBe("/layout/nested"); + }); + + test("can register a route under a deeply nested layout", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + name: "layout", + element:
Hello!
+ } + ]); + + runtime.registerRoutes([ + { + name: "layout-nested", + element:
Hello!
+ } + ], { parentName: "layout" }); + + runtime.registerRoutes([ + { + path: "/layout/nested/another-level", + element:
Hello!
+ } + ], { parentName: "layout-nested" }); + + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].children![0].children![0].path).toBe("/layout/nested/another-level"); + }); + + test("can register a route under a deeply nested layout that has been registered in a single block", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + element:
Hello
, + children: [ + { + element:
You!
, + children: [ + { + name: "deeply-nested-layout", + element:
Hello from nested!
+ } + ] + } + ] + } + ]); + + runtime.registerRoutes([ + { + name: "deeply-nested-layout/another-level", + element:
Hello!
+ } + ], { parentName: "deeply-nested-layout" }); + + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].children![0].children![0].children![0].name).toBe("deeply-nested-layout/another-level"); + + runtime.registerRoutes([ + { + path: "/deeply-nested-layout/another-level/yet-another-level", + element:
Hello!
+ } + ], { parentName: "deeply-nested-layout/another-level" }); + + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].children![0].children![0].children![0].children![0].path).toBe("/deeply-nested-layout/another-level/yet-another-level"); + }); }); }); @@ -272,7 +647,6 @@ describe("registerNavigationItems", () => { } ]); - expect(runtime.getNavigationItems().length).toBe(1); expect(runtime.getNavigationItems()[0].to).toBe("/root"); }); @@ -291,7 +665,6 @@ describe("registerNavigationItems", () => { } ]); - expect(runtime.getNavigationItems().length).toBe(1); expect(runtime.getNavigationItems()[0].label).toBe("Section"); }); @@ -305,7 +678,6 @@ describe("registerNavigationItems", () => { } ], { menuId: "link-menu" }); - expect(runtime.getNavigationItems("link-menu").length).toBe(1); expect(runtime.getNavigationItems("link-menu")[0].to).toBe("/link"); }); @@ -324,43 +696,79 @@ describe("registerNavigationItems", () => { } ], { menuId: "section-menu" }); - expect(runtime.getNavigationItems("section-menu").length).toBe(1); expect(runtime.getNavigationItems("section-menu")[0].label).toBe("Section"); }); }); describe("_completeRegistration", () => { - test("when the registration is completed and there are no pending registrations, do nothing", () => { - const runtime = new Runtime(); - - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { layoutPath: "/layout" }); - - runtime.registerRoutes([ - { - path: "/layout", - element:
Hello!
- } - ]); - - expect(() => runtime._completeRegistration()).not.toThrow(); + describe("parentPath", () => { + test("when the registration is completed and there are no pending registrations, do nothing", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + path: "/layout/nested", + element:
Hello!
+ } + ], { parentPath: "/layout" }); + + runtime.registerRoutes([ + { + path: "/layout", + element:
Hello!
+ } + ]); + + expect(() => runtime._completeRegistration()).not.toThrow(); + }); + + test("when the registration is completed and there are pending registrations, throw an error", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + path: "/layout/nested", + element:
Hello!
+ } + ], { parentPath: "/layout" }); + + expect(() => runtime._completeRegistration()).toThrow(); + }); }); - test("when the registration is completed and there are pending registrations, throw an error", () => { - const runtime = new Runtime(); - - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { layoutPath: "/layout" }); - - expect(() => runtime._completeRegistration()).toThrow(); + describe("parentName", () => { + test("when the registration is completed and there are no pending registrations, do nothing", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + path: "/layout/nested", + element:
Hello!
+ } + ], { parentName: "layout" }); + + runtime.registerRoutes([ + { + name: "layout", + element:
Hello!
+ } + ]); + + expect(() => runtime._completeRegistration()).not.toThrow(); + }); + + test("when the registration is completed and there are pending registrations, throw an error", () => { + const runtime = new Runtime(); + + runtime.registerRoutes([ + { + path: "/layout/nested", + element:
Hello!
+ } + ], { parentName: "layout" }); + + expect(() => runtime._completeRegistration()).toThrow(); + }); }); }); @@ -400,7 +808,6 @@ describe("getNavigationItems", () => { } ], { menuId: "menu-2" }); - expect(runtime.getNavigationItems().length).toBe(3); expect(runtime.getNavigationItems()[0].to).toBe("/item-1"); expect(runtime.getNavigationItems()[1].to).toBe("/item-2"); expect(runtime.getNavigationItems()[2].to).toBe("/item-3"); @@ -441,7 +848,6 @@ describe("getNavigationItems", () => { } ], { menuId: "menu-2" }); - expect(runtime.getNavigationItems("menu-1").length).toBe(1); expect(runtime.getNavigationItems("menu-1")[0].to).toBe("/item-4"); }); }); diff --git a/packages/webpack-module-federation/src/useAreModulesReady.ts b/packages/webpack-module-federation/src/useAreModulesReady.ts index dcca7c578..6d49a5d17 100644 --- a/packages/webpack-module-federation/src/useAreModulesReady.ts +++ b/packages/webpack-module-federation/src/useAreModulesReady.ts @@ -8,7 +8,7 @@ export interface UseAreModulesReadyOptions { interval?: number; } -function isReady() { +function areModulesReady() { // Validating for "in-progress" instead of "ready" for the local module because "registerLocalModules" // could never be called. return localModulesRegistrationStatus !== "in-progress" && remoteModulesRegistrationStatus === "ready"; @@ -18,18 +18,18 @@ export function useAreModulesReady({ interval = 10 }: UseAreModulesReadyOptions const runtime = useRuntime(); // Using a state hook to force a rerender once ready. - const [, setIsReady] = useState(false); + const [value, setAreModulesReady] = useState(false); // Perform a reload once the modules are registered. useEffect(() => { const intervalId = setInterval(() => { - if (isReady()) { + if (areModulesReady()) { // Must clear interval before calling "_completeRegistration" in case there's an error. clearInterval(intervalId); runtime._completeRegistration(); - setIsReady(true); + setAreModulesReady(true); } }, interval); @@ -40,5 +40,5 @@ export function useAreModulesReady({ interval = 10 }: UseAreModulesReadyOptions }; }, []); - return isReady(); + return value; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d418a80a1..2c833bdac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -116,6 +116,12 @@ importers: '@types/jest': specifier: 29.5.5 version: 29.5.5 + '@types/react': + specifier: 18.2.22 + version: 18.2.22 + '@types/react-dom': + specifier: 18.2.7 + version: 18.2.7 '@workleap/eslint-plugin': specifier: 2.1.1 version: 2.1.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) @@ -131,6 +137,12 @@ importers: msw: specifier: 1.3.2 version: 1.3.2(typescript@5.2.2) + react: + specifier: 18.2.0 + version: 18.2.0 + react-dom: + specifier: 18.2.0 + version: 18.2.0(react@18.2.0) tsup: specifier: 7.2.0 version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) @@ -656,9 +668,6 @@ importers: '@squide/react-router': specifier: workspace:* version: link:../../packages/react-router - '@tanstack/react-query': - specifier: rc - version: 5.0.0-rc.4(react-dom@18.2.0)(react@18.2.0) '@types/react': specifier: 18.2.22 version: 18.2.22 diff --git a/sample/another-remote-module/src/dev/App.tsx b/sample/another-remote-module/src/dev/App.tsx index 707e2c0e6..989ef0d61 100644 --- a/sample/another-remote-module/src/dev/App.tsx +++ b/sample/another-remote-module/src/dev/App.tsx @@ -1,11 +1,26 @@ +import type { Session } from "@sample/shared"; import { useAppRouter } from "@sample/shell"; -import { Suspense } from "react"; +import { Suspense, useCallback } from "react"; import { RouterProvider } from "react-router-dom"; import { DevHome } from "./DevHome.tsx"; import { sessionManager } from "./session.ts"; export function App() { - const router = useAppRouter(sessionManager, { + const onLogin = useCallback(async (username: string) => { + const session: Session = { + user: { + name: username + } + }; + + sessionManager.setSession(session); + }, []); + + const onLogout = useCallback(async () => { + sessionManager.clearSession(); + }, []); + + const router = useAppRouter(onLogin, onLogout, { managedRoutes: [ { index: true, diff --git a/sample/another-remote-module/src/dev/session.ts b/sample/another-remote-module/src/dev/session.ts index de7dd2e90..6f20a918b 100644 --- a/sample/another-remote-module/src/dev/session.ts +++ b/sample/another-remote-module/src/dev/session.ts @@ -1,8 +1,8 @@ -import type { Session, SessionManager } from "@sample/shared"; +import type { Session } from "@sample/shared"; import { LocalStorageSessionManager } from "@squide/fakes"; import type { SessionAccessorFunction } from "@squide/react-router"; -export const sessionManager = new LocalStorageSessionManager() as SessionManager; +export const sessionManager = new LocalStorageSessionManager(); export const sessionAccessor: SessionAccessorFunction = () => { return sessionManager.getSession(); diff --git a/sample/host/mocks/handlers.ts b/sample/host/mocks/handlers.ts index 7da31bba4..74969ff0f 100644 --- a/sample/host/mocks/handlers.ts +++ b/sample/host/mocks/handlers.ts @@ -1,12 +1,66 @@ import { rest, type RestHandler } from "msw"; +interface Session { + username: string; +} + +let currentSession: Session | undefined; + +function simulateDelay(delay: number) { + return new Promise(resolve => { + setTimeout(() => { + resolve(undefined); + }, delay); + }); +} + export const hostRequestHandlers: RestHandler[] = [ - // TODO: If not logged in, return a 401 unauthorized - rest.get("/session", (req, res, ctx) => { + rest.post("/login", async (req, res, ctx) => { + const { username, password } = await req.json(); + + if (username !== "temp" || password !== "temp") { + return res( + ctx.status(401) + ); + } + + await simulateDelay(2000); + + currentSession = { + username: username + }; + + return res( + ctx.status(200) + ); + }), + + rest.post("/logout", async (req, res, ctx) => { + currentSession = undefined; + + return res( + ctx.status(200) + ); + }), + + rest.get("/session", async (req, res, ctx) => { + // if (!currentSession) { + // return res( + // ctx.status(401) + // ); + // } + + await simulateDelay(500); + + // return res( + // ctx.status(200), + // ctx.json(currentSession) + // ); + return res( ctx.status(200), ctx.json({ - name: "John Doe" + username: "John Doe" }) ); }), diff --git a/sample/host/src/App copy.tsx b/sample/host/src/App copy.tsx new file mode 100644 index 000000000..eb33365f4 --- /dev/null +++ b/sample/host/src/App copy.tsx @@ -0,0 +1,65 @@ +// import { BackgroundColorContext, type Session } from "@sample/shared"; +// import { InvalidCredentialsError, useAppRouter } from "@sample/shell"; +// import { useAreModulesReady } from "@squide/webpack-module-federation"; +// import axios from "axios"; +// import { useCallback } from "react"; +// import { RouterProvider } from "react-router-dom"; +// import { sessionManager } from "./session.ts"; + +// export function App() { +// // Re-render the app once all the remotes are registered. +// // Otherwise, the remotes routes won't be added to the router. +// const areModulesReady = useAreModulesReady(); + +// const onLogin = useCallback(async (username: string, password: string) => { +// try { +// await axios.post("/login", { +// username, +// password +// }); + +// // TODO: Removed when the session mecanism is in place. +// const session: Session = { +// user: { +// name: username +// } +// }; + +// sessionManager.setSession(session); +// } catch (error: unknown) { +// if (axios.isAxiosError(error)) { +// if (error.response?.status === 401) { +// throw new InvalidCredentialsError(); +// } +// } + +// throw new Error("An unknown error happened while trying to login a user"); +// } +// }, []); + +// const onLogout = useCallback(async () => { +// sessionManager.clearSession(); +// }, []); + +// const router = useAppRouter(onLogin, onLogout, { +// managedRoutes: [ +// { +// index: true, +// lazy: () => import("./Home.tsx") +// } +// ] +// }); + +// // if (!areModulesReady) { +// // return
Loading...
; +// // } + +// return ( +// +// +// +// ); +// } diff --git a/sample/host/src/App.tsx b/sample/host/src/App.tsx index c84af2a5d..66b2786ff 100644 --- a/sample/host/src/App.tsx +++ b/sample/host/src/App.tsx @@ -1,38 +1,116 @@ -import { BackgroundColorContext } from "@sample/shared"; -import { useAppRouter } from "@sample/shell"; +import { BackgroundColorContext, type Session } from "@sample/shared"; +import { InvalidCredentialsError, useAppRouter } from "@sample/shell"; +import { useIsMswStarted } from "@squide/msw"; +import { useRuntime, type Runtime } from "@squide/react-router"; import { useAreModulesReady } from "@squide/webpack-module-federation"; -import { Suspense, lazy } from "react"; -import { RouterProvider } from "react-router-dom"; +import axios from "axios"; +import { useCallback, useEffect, useState } from "react"; +import { RouterProvider, matchRoutes } from "react-router-dom"; import { sessionManager } from "./session.ts"; -const Home = lazy(() => import("./Home.tsx")); - export function App() { + const [isReady, setIsReady] = useState(false); + + const runtime = useRuntime() as Runtime; + // Re-render the app once all the remotes are registered. // Otherwise, the remotes routes won't be added to the router. const areModulesReady = useAreModulesReady(); - const router = useAppRouter(sessionManager, { + // Re-render the app once MSW is started. + // Otherwise, the API calls will return a 404 status. + const isMswStarted = useIsMswStarted(process.env.USE_MSW as unknown as boolean); + + const onLogin = useCallback(async (username: string, password: string) => { + try { + await axios.post("/login", { + username, + password + }); + } catch (error: unknown) { + if (axios.isAxiosError(error)) { + if (error.response?.status === 401) { + throw new InvalidCredentialsError(); + } + } + + throw new Error("An unknown error happened while trying to login a user"); + } + }, []); + + const onLogout = useCallback(async () => { + sessionManager.clearSession(); + }, []); + + const router = useAppRouter(onLogin, onLogout, { managedRoutes: [ { index: true, - element: + lazy: () => import("./Home.tsx") } ] }); - if (!areModulesReady) { + /* + ISSUES: + - There's no match for the index route + - Doesn't work when we go from a public route to a private route + -> That's because there's no redirect to the login yet. + - What about the NoMatch route? + - Probably that in an unkown scenario that would be best to try to get the session? Otherwise we could render and end up with + unmanaged errors? + - Add a payment status endpoint to showcase having multiple global data + */ + + useEffect(() => { + if (areModulesReady && isMswStarted) { + // getActiveRouteVisibility + + // const location = useLocation(); + const location = window.location; + + console.log("**** location: ", location); + + const matchingRoutes = matchRoutes(runtime.routes, location) ?? []; + + console.log("**** matchingRoutes:", matchingRoutes); + + if (matchingRoutes.length > 0) { + // When a route is nested, it also returns all the parts that constistuate the whole route (for example the layouts). + // We only want to know the visiblity of the deepest root route. + const rootRoute = matchingRoutes.findLast(x => x.route.type === "root"); + + if (rootRoute!.route.visibility === "authenticated") { + axios.get("/session").then(({ data }) => { + const session: Session = { + user: { + name: data.username + } + }; + + sessionManager.setSession(session); + + setIsReady(true); + }); + } else { + setIsReady(true); + } + } else { + throw new Error(`[shell] There's no matching route for the location: "${location}". Did you add routes to React Router without using the runtime.registerRoutes() function?`); + } + } + }, [areModulesReady, isMswStarted, runtime.routes]); + + if (!isReady) { return
Loading...
; } return ( - Loading...
}> - Loading...} - /> - + ); } diff --git a/sample/host/src/Home.tsx b/sample/host/src/Home.tsx index dabd91ccc..49317b3af 100644 --- a/sample/host/src/Home.tsx +++ b/sample/host/src/Home.tsx @@ -1,4 +1,4 @@ -export default function Home() { +export function Home() { return (

Home Page

@@ -6,3 +6,5 @@ export default function Home() {
); } + +export const Component = Home; diff --git a/sample/host/src/bootstrap.tsx b/sample/host/src/bootstrap.tsx index 999a063ff..6580baf40 100644 --- a/sample/host/src/bootstrap.tsx +++ b/sample/host/src/bootstrap.tsx @@ -40,10 +40,7 @@ const runtime = new Runtime({ const queryClient = new QueryClient(); const context: AppContext = { - name: "Test app", - // TODO: Should eventually be a plugin instead of passing it through the context object. - // @squide/react-query. - queryClient + name: "Test app" }; registerLocalModules([registerLocalModule], runtime, { context }); @@ -53,6 +50,10 @@ registerRemoteModules(Remotes, runtime, { context }).then(() => { import("../mocks/browser.ts").then(({ startMsw }) => { // Will start MSW with the request handlers provided by every module. startMsw(mswPlugin.requestHandlers); + + // Indicate to resources that are dependent on MSW + // that the service has been started. + mswPlugin.setAsStarted(); }); } }); diff --git a/sample/host/src/session.ts b/sample/host/src/session.ts index fbc2fd0a0..e2ca4125b 100644 --- a/sample/host/src/session.ts +++ b/sample/host/src/session.ts @@ -1,11 +1,11 @@ -import type { Session, SessionManager } from "@sample/shared"; +import type { Session } from "@sample/shared"; // import type { Session } from "@sample/shared"; -import { LocalStorageSessionManager } from "@squide/fakes"; +// import { LocalStorageSessionManager } from "@squide/fakes"; import type { SessionAccessorFunction } from "@squide/react-router"; // import type { QueryClient } from "@tanstack/react-query"; // import axios from "axios"; -export const sessionManager = new LocalStorageSessionManager() as SessionManager; +// export const sessionManager = new LocalStorageSessionManager() as SessionManager; // export const SessionQueryKey = ["/session"]; @@ -36,23 +36,23 @@ export const sessionManager = new LocalStorageSessionManager() as Sessi // return data; // } -// export class SessionManager { -// #session?: Session; +export class SessionManager { + #session?: Session; -// setSession(session: Session) { -// this.#session = session; -// } + setSession(session: Session) { + this.#session = session; + } -// getSession() { -// return this.#session; -// } + getSession() { + return this.#session; + } -// clearSession() { -// this.#session = undefined; -// } -// } + clearSession() { + this.#session = undefined; + } +} -// export const sessionManager = new SessionManager(); +export const sessionManager = new SessionManager(); export const sessionAccessor: SessionAccessorFunction = () => { return sessionManager.getSession(); diff --git a/sample/local-module/src/dev/App.tsx b/sample/local-module/src/dev/App.tsx index 707e2c0e6..989ef0d61 100644 --- a/sample/local-module/src/dev/App.tsx +++ b/sample/local-module/src/dev/App.tsx @@ -1,11 +1,26 @@ +import type { Session } from "@sample/shared"; import { useAppRouter } from "@sample/shell"; -import { Suspense } from "react"; +import { Suspense, useCallback } from "react"; import { RouterProvider } from "react-router-dom"; import { DevHome } from "./DevHome.tsx"; import { sessionManager } from "./session.ts"; export function App() { - const router = useAppRouter(sessionManager, { + const onLogin = useCallback(async (username: string) => { + const session: Session = { + user: { + name: username + } + }; + + sessionManager.setSession(session); + }, []); + + const onLogout = useCallback(async () => { + sessionManager.clearSession(); + }, []); + + const router = useAppRouter(onLogin, onLogout, { managedRoutes: [ { index: true, diff --git a/sample/local-module/src/dev/session.ts b/sample/local-module/src/dev/session.ts index de7dd2e90..6f20a918b 100644 --- a/sample/local-module/src/dev/session.ts +++ b/sample/local-module/src/dev/session.ts @@ -1,8 +1,8 @@ -import type { Session, SessionManager } from "@sample/shared"; +import type { Session } from "@sample/shared"; import { LocalStorageSessionManager } from "@squide/fakes"; import type { SessionAccessorFunction } from "@squide/react-router"; -export const sessionManager = new LocalStorageSessionManager() as SessionManager; +export const sessionManager = new LocalStorageSessionManager(); export const sessionAccessor: SessionAccessorFunction = () => { return sessionManager.getSession(); diff --git a/sample/local-module/src/register.tsx b/sample/local-module/src/register.tsx index a158a87ad..41f22fe95 100644 --- a/sample/local-module/src/register.tsx +++ b/sample/local-module/src/register.tsx @@ -39,7 +39,7 @@ export const register: ModuleRegisterFunction = (runtime, c index: true, lazy: () => import("./WorkleapTab.tsx") } - ], { layoutPath: "/federated-tabs" }); + ], { parentPath: "/federated-tabs" }); runtime.registerNavigationItems([ { diff --git a/sample/remote-module/src/dev/App.tsx b/sample/remote-module/src/dev/App.tsx index ac0525e99..139fc984c 100644 --- a/sample/remote-module/src/dev/App.tsx +++ b/sample/remote-module/src/dev/App.tsx @@ -1,12 +1,26 @@ -import { BackgroundColorContext } from "@sample/shared"; +import { BackgroundColorContext, type Session } from "@sample/shared"; import { useAppRouter } from "@sample/shell"; -import { Suspense } from "react"; +import { Suspense, useCallback } from "react"; import { RouterProvider } from "react-router-dom"; import { DevHome } from "./DevHome.tsx"; import { sessionManager } from "./session.ts"; export function App() { - const router = useAppRouter(sessionManager, { + const onLogin = useCallback(async (username: string) => { + const session: Session = { + user: { + name: username + } + }; + + sessionManager.setSession(session); + }, []); + + const onLogout = useCallback(async () => { + sessionManager.clearSession(); + }, []); + + const router = useAppRouter(onLogin, onLogout, { managedRoutes: [ { index: true, diff --git a/sample/remote-module/src/dev/session.ts b/sample/remote-module/src/dev/session.ts index de7dd2e90..6f20a918b 100644 --- a/sample/remote-module/src/dev/session.ts +++ b/sample/remote-module/src/dev/session.ts @@ -1,8 +1,8 @@ -import type { Session, SessionManager } from "@sample/shared"; +import type { Session } from "@sample/shared"; import { LocalStorageSessionManager } from "@squide/fakes"; import type { SessionAccessorFunction } from "@squide/react-router"; -export const sessionManager = new LocalStorageSessionManager() as SessionManager; +export const sessionManager = new LocalStorageSessionManager(); export const sessionAccessor: SessionAccessorFunction = () => { return sessionManager.getSession(); diff --git a/sample/remote-module/src/register.tsx b/sample/remote-module/src/register.tsx index 7e4f6fffd..a9a5e9c65 100644 --- a/sample/remote-module/src/register.tsx +++ b/sample/remote-module/src/register.tsx @@ -23,6 +23,7 @@ export const register: ModuleRegisterFunction = runtime => { }, { hoist: true, + visibility: "public", path: "/hoisted", lazy: () => import("./CustomLayout.tsx"), children: [ @@ -99,7 +100,7 @@ export const register: ModuleRegisterFunction = runtime => { path: "/federated-tabs/skills", lazy: () => import("./SkillsTab.tsx") } - ], { layoutPath: "/federated-tabs" }); + ], { parentPath: "/federated-tabs" }); runtime.registerNavigationItems([ { diff --git a/sample/shared/package.json b/sample/shared/package.json index b59db700b..e41d19ca7 100644 --- a/sample/shared/package.json +++ b/sample/shared/package.json @@ -25,7 +25,6 @@ }, "peerDependencies": { "@squide/react-router": "*", - "@tanstack/react-query": "*", "react": "*", "react-dom": "*", "react-router-dom": "*" @@ -33,7 +32,6 @@ "devDependencies": { "@remix-run/router": "1.9.0", "@squide/react-router": "workspace:*", - "@tanstack/react-query": "rc", "@types/react": "18.2.22", "@types/react-dom": "18.2.7", "@workleap/eslint-plugin": "2.1.1", diff --git a/sample/shared/src/appContext.ts b/sample/shared/src/appContext.ts index 97d399657..5bec8965b 100644 --- a/sample/shared/src/appContext.ts +++ b/sample/shared/src/appContext.ts @@ -1,6 +1,3 @@ -import type { QueryClient } from "@tanstack/react-query"; - export interface AppContext { name: string; - queryClient: QueryClient; } diff --git a/sample/shared/src/session.ts b/sample/shared/src/session.ts index a433b0c30..f00af3d2f 100644 --- a/sample/shared/src/session.ts +++ b/sample/shared/src/session.ts @@ -4,8 +4,8 @@ export interface Session { }; } -export interface SessionManager { - setSession: (session: Session) => void; - getSession: () => Session; - clearSession: () => void; -} +// export interface SessionManager { +// setSession: (session: Session) => void; +// getSession: () => Session | undefined; +// clearSession: () => void; +// } diff --git a/sample/shell/package.json b/sample/shell/package.json index d06f82fff..6f88cb2a0 100644 --- a/sample/shell/package.json +++ b/sample/shell/package.json @@ -11,35 +11,11 @@ "import": "./dist/index.js", "types": "./dist/index.d.ts", "default": "./dist/index.js" - }, - "./AuthenticatedLayout.tsx": { - "import": "./dist/AuthenticatedLayout.js", - "types": "./dist/AuthenticatedLayout.d.ts", - "default": "./dist/AuthenticatedLayout.js" - }, - "./Login.tsx": { - "import": "./dist/Login.js", - "types": "./dist/Login.d.ts", - "default": "./dist/Login.js" - }, - "./Logout.tsx": { - "import": "./dist/Logout.js", - "types": "./dist/Logout.d.ts", - "default": "./dist/Logout.js" - }, - "./ModuleErrorBoundary.tsx": { - "import": "./dist/ModuleErrorBoundary.js", - "types": "./dist/ModuleErrorBoundary.d.ts", - "default": "./dist/ModuleErrorBoundary.js" - }, - "./NoMatch.tsx": { - "import": "./dist/NoMatch.js", - "types": "./dist/NoMatch.d.ts", - "default": "./dist/NoMatch.js" } }, "scripts": { "dev": "nodemon", + "dev-msw": "pnpm nodemon", "build": "tsup --config ./tsup.build.ts", "serve-build": "pnpm build" }, diff --git a/sample/shell/src/AuthenticatedLayout.tsx b/sample/shell/src/AuthenticatedLayout.tsx index e0731dcae..b5c25a3b4 100644 --- a/sample/shell/src/AuthenticatedLayout.tsx +++ b/sample/shell/src/AuthenticatedLayout.tsx @@ -72,5 +72,3 @@ export function AuthenticatedLayout() { ); } - -export const Component = AuthenticatedLayout; diff --git a/sample/shell/src/Login.tsx b/sample/shell/src/Login.tsx index 26ee1f160..df919a8cd 100644 --- a/sample/shell/src/Login.tsx +++ b/sample/shell/src/Login.tsx @@ -1,31 +1,48 @@ -import type { SessionManager } from "@sample/shared"; import { useIsAuthenticated } from "@squide/react-router"; import { useCallback, useState, type ChangeEvent, type MouseEvent } from "react"; import { Navigate, useNavigate } from "react-router-dom"; +export class InvalidCredentialsError extends Error { + constructor(message: string = "") { + super(message); + this.name = "InvalidCredentialsError"; + } +} + +export type OnLoginHandler = (username: string, password: string) => Promise; + export interface LoginProps { - sessionManager: SessionManager; + onLogin: OnLoginHandler; } -export function Login({ sessionManager }: LoginProps) { +export function Login({ onLogin }: LoginProps) { const [username, setUserName] = useState(""); const [password, setPassword] = useState(""); + const [errorMessage, setErrorMessage] = useState(); + const [isBusy, setIsBusy] = useState(false); const navigate = useNavigate(); - const handleClick = useCallback((event: MouseEvent) => { + const handleClick = useCallback(async (event: MouseEvent) => { event.preventDefault(); - if (username === "temp" && password === "temp") { - sessionManager.setSession({ - user: { - name: username - } - }); + try { + setIsBusy(true); + setErrorMessage(undefined); + + await onLogin(username, password); navigate("/"); + } catch (error: unknown) { + setIsBusy(false); + + if (error instanceof InvalidCredentialsError) { + setErrorMessage("Invalid credentials, please try again."); + } else { + setErrorMessage("An unknown error occured while trying to log you in, please try again."); + } } - }, [username, password, sessionManager, navigate]); + }, [username, password, onLogin, navigate]); const handleUserNameChange = useCallback((event: ChangeEvent) => { setUserName(event.target.value); @@ -59,9 +76,9 @@ export function Login({ sessionManager }: LoginProps) {
Hint: use temp/temp :)
+ {isBusy &&
Loading...
} + {errorMessage &&
{errorMessage}
} ); } - -export const Component = Login; diff --git a/sample/shell/src/Logout.tsx b/sample/shell/src/Logout.tsx index 41a7737a5..777788c96 100644 --- a/sample/shell/src/Logout.tsx +++ b/sample/shell/src/Logout.tsx @@ -1,12 +1,13 @@ -import type { SessionManager } from "@sample/shared"; import { Link } from "react-router-dom"; +export type onLogoutHandler = () => Promise; + export interface LogoutProps { - sessionManager: SessionManager; + onLogout: onLogoutHandler; } -export function Logout({ sessionManager }: LogoutProps) { - sessionManager.clearSession(); +export function Logout({ onLogout }: LogoutProps) { + onLogout(); return (
@@ -16,5 +17,3 @@ export function Logout({ sessionManager }: LogoutProps) {
); } - -export const Component = Logout; diff --git a/sample/shell/src/ModuleErrorBoundary.tsx b/sample/shell/src/ModuleErrorBoundary.tsx index fe578bd0d..eed247667 100644 --- a/sample/shell/src/ModuleErrorBoundary.tsx +++ b/sample/shell/src/ModuleErrorBoundary.tsx @@ -26,5 +26,3 @@ export function ModuleErrorBoundary() { ); } - -export const ErrorBoundary = ModuleErrorBoundary; diff --git a/sample/shell/src/NoMatch.tsx b/sample/shell/src/NoMatch.tsx index fdc4deb0c..cac3cc455 100644 --- a/sample/shell/src/NoMatch.tsx +++ b/sample/shell/src/NoMatch.tsx @@ -13,5 +13,3 @@ export function NoMatch({ path }: NoMatchProps) { ); } - -export const Component = NoMatch; diff --git a/sample/shell/src/index.ts b/sample/shell/src/index.ts index fff4e31c8..f6cf0a585 100644 --- a/sample/shell/src/index.ts +++ b/sample/shell/src/index.ts @@ -1,4 +1,9 @@ +export * from "./AuthenticatedLayout.tsx"; export * from "./AuthenticationBoundary.tsx"; +export * from "./Login.tsx"; +export * from "./Logout.tsx"; +export * from "./ModuleErrorBoundary.tsx"; +export * from "./NoMatch.tsx"; export * from "./RootErrorBoundary.tsx"; export * from "./RootLayout.tsx"; export * from "./useAppRouter.tsx"; diff --git a/sample/shell/src/useAppRouter.tsx b/sample/shell/src/useAppRouter.tsx index 6b24ecdb9..de7854f1f 100644 --- a/sample/shell/src/useAppRouter.tsx +++ b/sample/shell/src/useAppRouter.tsx @@ -1,19 +1,19 @@ -import { useHoistedRoutes, useRoutes, type Route } from "@squide/react-router"; +import { useHoistedRoutes, useRoutes, useRuntime, type Route } from "@squide/react-router"; import { useCallback, useMemo, useState } from "react"; -import { AuthenticationBoundary } from "./AuthenticationBoundary.tsx"; import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; import { RootLayout } from "./RootLayout.tsx"; // Importing the Router type to prevent: error TS2742: The inferred type of 'useAppRouter' cannot be named without a reference import type { Router } from "@remix-run/router"; -import type { SessionManager } from "@sample/shared"; import { createBrowserRouter } from "react-router-dom"; +import type { OnLoginHandler } from "./Login.tsx"; +import type { onLogoutHandler } from "./Logout.tsx"; export interface UseAppRouterOptions { managedRoutes?: Route[]; rootRoutes?: Route[]; } -export function useAppRouter(sessionManager: SessionManager, options: UseAppRouterOptions = {}): Router { +export function useAppRouter(onLogin: OnLoginHandler, onLogout: onLogoutHandler, options: UseAppRouterOptions = {}): Router { const { managedRoutes: hostManagedRoutes = [], rootRoutes = [] @@ -23,11 +23,16 @@ export function useAppRouter(sessionManager: SessionManager, options: UseAppRout const [memoizedManagedRoutes] = useState(hostManagedRoutes); const [memoizedRootRoutes] = useState(rootRoutes); + const runtime = useRuntime(); const routes = useRoutes(); - const wrapManagedRoutes = useCallback((managedRoutes: Route[]) => { - return { + // All routes must be registered in order to retrieve the active route + // visibility property. + runtime.registerRoutes([ + { // Pathless route to declare a root layout and a root error boundary. + hoist: true, + public: true, element: , children: [ { @@ -39,7 +44,7 @@ export function useAppRouter(sessionManager: SessionManager, options: UseAppRout const { Login } = await import("./Login.tsx"); return { - element: + element: }; } }, @@ -49,31 +54,10 @@ export function useAppRouter(sessionManager: SessionManager, options: UseAppRout const { Logout } = await import("./Logout.tsx"); return { - element: + element: }; } }, - { - // Pathless route to declare an authenticated boundary. - element: , - children: [ - { - // Pathless route to declare an authenticated layout. - lazy: () => import("./AuthenticatedLayout.tsx"), - children: [ - { - // Pathless route to declare an error boundary inside the layout instead of outside. - // It's quite useful to prevent losing the layout when an unmanaged error occurs. - lazy: () => import("./ModuleErrorBoundary.tsx"), - children: [ - ...memoizedManagedRoutes, - ...managedRoutes - ] - } - ] - } - ] - }, { path: "*", lazy: async () => { @@ -87,8 +71,101 @@ export function useAppRouter(sessionManager: SessionManager, options: UseAppRout ] } ] + } + ]); + + const wrapManagedRoutes = useCallback((managedRoutes: Route[]) => { + return { + // Pathless route to declare a root layout and a root error boundary. + element: , + children: [ + { + errorElement: , + children: [ + // { + // path: "/login", + // lazy: async () => { + // const { Login } = await import("./Login.tsx"); + + // return { + // element: + // }; + // } + // }, + // { + // path: "/logout", + // lazy: async () => { + // const { Logout } = await import("./Logout.tsx"); + + // return { + // element: + // }; + // } + // }, + { + // Pathless route to declare an authenticated layout. + lazy: async () => { + const { AuthenticatedLayout } = await import("./AuthenticatedLayout.tsx"); + + return { + element: + }; + }, + children: [ + { + // Pathless route to declare an error boundary inside the layout instead of outside. + // It's quite useful to prevent losing the layout when an unmanaged error occurs. + lazy: async () => { + const { ModuleErrorBoundary } = await import("./ModuleErrorBoundary.tsx"); + + return { + element: + }; + }, + children: [ + ...memoizedManagedRoutes, + ...managedRoutes + ] + } + ] + } + // { + // // Pathless route to declare an authenticated boundary. + // lazy: () => import("./AuthenticationBoundary.tsx"), + // children: [ + // { + // // Pathless route to declare an authenticated layout. + // lazy: () => import("./AuthenticatedLayout.tsx"), + // children: [ + // { + // // Pathless route to declare an error boundary inside the layout instead of outside. + // // It's quite useful to prevent losing the layout when an unmanaged error occurs. + // lazy: () => import("./ModuleErrorBoundary.tsx"), + // children: [ + // ...memoizedManagedRoutes, + // ...managedRoutes + // ] + // } + // ] + // } + // ] + // }, + // { + // path: "*", + // lazy: async () => { + // const { NoMatch } = await import("./NoMatch.tsx"); + + // return { + // element: + // }; + // } + // } + ] + } + ] }; - }, [sessionManager, memoizedManagedRoutes]); + }, [memoizedManagedRoutes]); + // }, [memoizedManagedRoutes, onLogin, onLogout]); // Using the useHoistedRoutes hook allow routes hoisted by modules to be rendered at the root of the router instead of under the root layout. // To disallow the hoisting functionality, remove this hook and add the routes directly. From 07296d89bc4fd785b282298f0841f5af396c7927 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Sat, 7 Oct 2023 00:14:28 -0400 Subject: [PATCH 04/51] Too many changes --- docs/getting-started/create-local-module.md | 26 +- docs/getting-started/create-remote-module.md | 24 +- docs/getting-started/learn-the-api.md | 2 +- docs/guides/federated-tabs.md | 214 +++-- docs/guides/override-a-react-context.md | 54 +- docs/guides/override-the-host-layout.md | 34 +- .../registration/registerLocalModules.md | 22 +- .../registration/registerRemoteModules.md | 22 +- docs/reference/routing/useHoistedRoutes.md | 100 +- docs/reference/runtime/runtime-class.md | 264 +++--- packages/core/src/runtime/abstractRuntime.ts | 8 +- .../src/navigationItemRegistry.ts | 4 +- packages/react-router/src/routeRegistry.ts | 46 +- packages/react-router/src/runtime.ts | 35 +- packages/react-router/src/useHoistedRoutes.ts | 3 +- .../src/useRenderedNavigationItems.tsx | 5 +- packages/react-router/tests/runtime.test.tsx | 876 ++++++++---------- .../tests/useNavigationItems.test.tsx | 132 ++- .../react-router/tests/useRoutes.test.tsx | 27 +- sample/another-remote-module/src/dev/App.tsx | 37 +- .../another-remote-module/src/dev/index.tsx | 8 +- .../src/dev/register.tsx | 9 + .../another-remote-module/src/dev/session.ts | 14 + sample/another-remote-module/src/register.tsx | 20 +- sample/host/package.json | 2 - sample/host/src/App copy.tsx | 65 -- sample/host/src/App.tsx | 101 +- sample/host/src/bootstrap.tsx | 25 +- sample/host/src/register.tsx | 8 + sample/host/src/session.ts | 66 +- sample/local-module/src/dev/App.tsx | 37 +- sample/local-module/src/dev/index.tsx | 11 +- sample/local-module/src/dev/register.tsx | 19 + .../local-module/src/dev/registerTabsPage.tsx | 17 - sample/local-module/src/dev/session.ts | 14 + sample/local-module/src/register.tsx | 76 +- sample/remote-module/src/dev/App.tsx | 39 +- sample/remote-module/src/dev/index.tsx | 9 +- sample/remote-module/src/dev/register.tsx | 19 + .../src/dev/registerTabsPage.tsx | 17 - sample/remote-module/src/dev/session.ts | 14 + sample/remote-module/src/register.tsx | 216 +++-- sample/shared/src/appContext.ts | 2 +- sample/shared/src/session.ts | 6 - sample/shell/src/Login.tsx | 10 +- sample/shell/src/Logout.tsx | 6 +- sample/shell/src/index.ts | 1 + sample/shell/src/register.tsx | 95 ++ sample/shell/src/useAppRouter.tsx | 176 +--- 49 files changed, 1385 insertions(+), 1652 deletions(-) create mode 100644 sample/another-remote-module/src/dev/register.tsx delete mode 100644 sample/host/src/App copy.tsx create mode 100644 sample/host/src/register.tsx create mode 100644 sample/local-module/src/dev/register.tsx delete mode 100644 sample/local-module/src/dev/registerTabsPage.tsx create mode 100644 sample/remote-module/src/dev/register.tsx delete mode 100644 sample/remote-module/src/dev/registerTabsPage.tsx create mode 100644 sample/shell/src/register.tsx diff --git a/docs/getting-started/create-local-module.md b/docs/getting-started/create-local-module.md index 70031a7c5..5dc5b9092 100644 --- a/docs/getting-started/create-local-module.md +++ b/docs/getting-started/create-local-module.md @@ -83,27 +83,23 @@ Finally, configure the package to be shareable by adding the `name`, `version`, ### Routes registration -Next, register the local module routes and navigation items with [registerRoutes](/reference/runtime/runtime-class.md#register-routes) and [registerNavigationItems](/reference/runtime/runtime-class.md#register-navigation-items) functions: +Next, register the local module routes and navigation items with [registerRoute](/reference/runtime/runtime-class.md#register-routes) and [registerNavigationItem](/reference/runtime/runtime-class.md#register-navigation-items) functions: -```tsx !#6-11,13-18 local-module/src/register.tsx +```tsx !#6-9,11-14 local-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import type { AppContext } from "@sample/shared"; import { Page } from "./Page.tsx"; export const register: ModuleRegisterFunction = (runtime, context) => { - runtime.registerRoutes([ - { - path: "/local/page", - element: - } - ]); - - runtime.registerNavigationItems([ - { - to: "/local/page", - content: "Local/Page" - } - ]); + runtime.registerRoute({ + path: "/local/page", + element: + }); + + runtime.registerNavigationItem({ + to: "/local/page", + content: "Local/Page" + }); } ``` diff --git a/docs/getting-started/create-remote-module.md b/docs/getting-started/create-remote-module.md index 583f04648..6d9aceee7 100644 --- a/docs/getting-started/create-remote-module.md +++ b/docs/getting-started/create-remote-module.md @@ -60,7 +60,7 @@ Then, ensure that you are developing your module using [ESM syntax](https://deve ### Routes registration -Next, register the remote module routes and navigation items with the [registerRoutes](/reference/runtime/runtime-class.md#register-routes) and [registerNavigationItems](/reference/runtime/runtime-class.md#register-navigation-items) functions: +Next, register the remote module routes and navigation items with the [registerRoute](/reference/runtime/runtime-class.md#register-routes) and [registerNavigationItem](/reference/runtime/runtime-class.md#register-navigation-items) functions: ```tsx !#6-11,13-18 remote-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; @@ -68,19 +68,15 @@ import type { AppContext } from "@sample/shared"; import { Page } from "./Page.tsx"; export const register: ModuleRegisterFunction = (runtime, context) => { - runtime.registerRoutes([ - { - path: "/remote/page", - element: - } - ]); - - runtime.registerNavigationItems([ - { - to: "/remote/page", - label: "Remote/Page" - } - ]); + runtime.registerRoute({ + path: "/remote/page", + element: + }); + + runtime.registerNavigationItem({ + to: "/remote/page", + label: "Remote/Page" + }); } ``` diff --git a/docs/getting-started/learn-the-api.md b/docs/getting-started/learn-the-api.md index a40b10b29..8e9ac4867 100644 --- a/docs/getting-started/learn-the-api.md +++ b/docs/getting-started/learn-the-api.md @@ -179,7 +179,7 @@ To keep Squide lightweight, not all functionalities should be integrated as a co First, define a plugin by implementing the [Plugin](../reference/plugins/plugin.md) interface: -```ts !#3 shared/src/mswPlugin.ts +```ts !#4 shared/src/mswPlugin.ts import { Plugin } from "@squide/react-router"; import type { RestHandler } from "msw"; diff --git a/docs/guides/federated-tabs.md b/docs/guides/federated-tabs.md index 774647a5b..40bbee019 100644 --- a/docs/guides/federated-tabs.md +++ b/docs/guides/federated-tabs.md @@ -45,27 +45,23 @@ export function FederatedTabsLayout() { In the previous code sample, the `FederatedTabsLayout` is similar to the `RootLayout` introduced in previous guides. However, the key distinction is that this layout is nested under the `/federated-tabs` URL segment. By nesting the layout under a specific path, it will only render when the user navigates to one of the federated tab pages (e.g. `/federated-tabs/tab-1`, `/federated-tabs/tab-2`, `/federated-tabs/tab-3`). -To register the newly created layout as a nested layout, use the [registerRoutes](../reference/runtime/runtime-class.md#register-routes) function: +To register the newly created layout as a nested layout, use the [registerRoute](../reference/runtime/runtime-class.md#register-routes) function: -```tsx !#8-9 remote-module-3/src/register.tsx +```tsx !#7-8 remote-module-3/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { FederatedTabsLayout } from "./FederatedTabsLayout.tsx"; export const register: ModuleRegisterFunction = runtime => { - runtime.registerRoutes([ - { - // Register the layout as a nested layout under the "/federated-tabs" URL segment. - path: "/federated-tabs", - element: - } - ]); - - runtime.registerNavigationItems([ - { - to: "/federated-tabs", - label: "Federated tabs" - } - ]); + runtime.registerRoute({ + // Register the layout as a nested layout under the "/federated-tabs" URL segment. + path: "/federated-tabs", + element: + }); + + runtime.registerNavigationItem({ + to: "/federated-tabs", + label: "Federated tabs" + }); } ``` @@ -75,20 +71,20 @@ As a bonus, each individual tab will have its own dedicated URL! :partying_face: ## Create the tab routes -Next, let's add the actual tab pages to the modules. To do so, we'll use the [parentPath](../reference/runtime/runtime-class.md#register-nested-routes-under-an-existing-route) option of the [registerRoutes](../reference/runtime/runtime-class.md#register-routes) function to register the routes under the `FederatedTabsLayout`: +Next, let's add the actual tab pages to the modules. To do so, we'll use the [parentPath](../reference/runtime/runtime-class.md#register-nested-routes-under-an-existing-route) option of the [registerRoute](../reference/runtime/runtime-class.md#register-routes) function to register the routes under the `FederatedTabsLayout`: -```tsx !#8,11 remote-module-1/src/register.tsx +```tsx !#7,10 remote-module-1/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { Tab1 } from "./Tab1.tsx"; export const register: ModuleRegisterFunction = (runtime: Runtime) => { - runtime.registerRoutes([ - { - // Using "index: true" instead of a path because this is the default active tab. - index: true - element: - } - ], { parentPath: "/federated-tabs" }); + runtime.registerRoute({ + // Using "index: true" instead of a path because this is the default active tab. + index: true + element: + }, { + parentPath: "/federated-tabs" + }); } ``` @@ -100,18 +96,18 @@ export function Tab1() { } ``` -```tsx !#8,11 remote-module-2/src/register.tsx +```tsx !#7,10 remote-module-2/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { Tab2 } from "./Tab2.tsx"; export const register: ModuleRegisterFunction = runtime => { - runtime.registerRoutes([ - { - // The first part of the "path" must be the same as the nested layout path (FederatedTabsLayout). - path: "/federated-tabs/tab-2" - element: - } - ], { parentPath: "/federated-tabs" }); + runtime.registerRoute({ + // The first part of the "path" must be the same as the nested layout path (FederatedTabsLayout). + path: "/federated-tabs/tab-2" + element: + }, { + parentPath: "/federated-tabs" + }); } ``` @@ -123,18 +119,18 @@ export function Tab2() { } ``` -```tsx !#8,11 local-module/src/register.tsx +```tsx !#7,10 local-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { Tab3 } from "./Tab3.tsx"; export const register: ModuleRegisterFunction = (runtime: Runtime) => { - runtime.registerRoutes([ - { - // The first part of the "path" must be the same as the nested layout path (FederatedTabsLayout). - path: "/federated-tabs/tab-3" - element: - } - ], { parentPath: "/federated-tabs" }); + runtime.registerRoute({ + // The first part of the "path" must be the same as the nested layout path (FederatedTabsLayout). + path: "/federated-tabs/tab-3" + element: + }, { + parentPath: "/federated-tabs" + }); } ``` @@ -152,79 +148,79 @@ Now that the tabs have been registered, ensure that all four modules (including Althought it's functional, there are still a few configurations needed since the modules are currently coupled by hardcoded URLs within the `FederatedTabsLayout`. -To decouple the navigation items, similar to what is done for regular federated pages, we'll utilize the [registerNavigationItems](../reference/runtime/runtime-class.md#register-navigation-items) function. In this case, we'll also use the [menuId](../reference/runtime/runtime-class.md#register-navigation-items-for-a-specific-menu) option. Defining the `menuId` option will enable the `FederatedTabsLayout` to retrieve navigation items exclusively for the federated tab component. +To decouple the navigation items, similar to what is done for regular federated pages, we'll utilize the [registerNavigationItem](../reference/runtime/runtime-class.md#register-navigation-items) function. In this case, we'll also use the [menuId](../reference/runtime/runtime-class.md#register-navigation-items-for-a-specific-menu) option. Defining the `menuId` option will enable the `FederatedTabsLayout` to retrieve navigation items exclusively for the federated tab component. First, let's register the navigation items with the `menuId` option: -```tsx !#20 remote-module-1/src/register.tsx +```tsx !#19 remote-module-1/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { Tab1 } from "./Tab1.tsx"; export const register: ModuleRegisterFunction = runtime => { - runtime.registerRoutes([ - { - // Using "index: true" instead of a path because this is the default active tab. - index: true - element: - } - ], { parentPath: "/federated-tabs" }); - - runtime.registerNavigationItems([ - { - to: "/federated-tabs", - label: "Tab 1" - } - // The menu id could be anything, in this example we are using the same path as the nested layout - // path for convenience. - ], { menuId: "/federated-tabs" }); + runtime.registerRoute({ + // Using "index: true" instead of a path because this is the default active tab. + index: true + element: + }, { + parentPath: "/federated-tabs" + }); + + runtime.registerNavigationItem({ + to: "/federated-tabs", + label: "Tab 1" + }, { + // The menu id could be anything, in this example we are using the same path as the nested layout + // path for convenience. + menuId: "/federated-tabs" + }); } ``` -```tsx !#20 remote-module-2/src/register.tsx +```tsx !#19 remote-module-2/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { Tab2 } from "./Tab2.tsx"; export const register: ModuleRegisterFunction = (runtime: Runtime) => { - runtime.registerRoutes([ - { - // The first part of the "path" must be the same as the nested layout path (FederatedTabsLayout). - path: "/federated-tabs/tab-2" - element: - } - ], { parentPath: "/federated-tabs" }); - - runtime.registerNavigationItems([ - { - to: "/federated-tabs/tab-2", - label: "Tab 2" - } - // The menu id could be anything, in this example we are using the same path as the nested layout - // path for convenience. - ], { menuId: "/federated-tabs" }); + runtime.registerRoute({ + // The first part of the "path" must be the same as the nested layout path (FederatedTabsLayout). + path: "/federated-tabs/tab-2" + element: + }, { + parentPath: "/federated-tabs" + }); + + runtime.registerNavigationItem({ + to: "/federated-tabs/tab-2", + label: "Tab 2" + }, { + // The menu id could be anything, in this example we are using the same path as the nested layout + // path for convenience. + menuId: "/federated-tabs" + }); } ``` -```tsx !#20 local-module/src/register.tsx +```tsx !#19 local-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { Tab3 } from "./Tab3.tsx"; export const register: ModuleRegisterFunction = runtime => { - runtime.registerRoutes([ - { - // The first part of the "path" must be the same as the nested layout path (FederatedTabsLayout). - path: "/federated-tabs/tab-3" - element: - } - ], { parentPath: "/federated-tabs" }); - - runtime.registerNavigationItems([ - { - to: "/federated-tabs/tab-3", - label: "Tab 3" - } - // The menu id could be anything, in this example we are using the same path as the nested layout - // path for convenience. - ], { menuId: "/federated-tabs" }); + runtime.registerRoute({ + // The first part of the "path" must be the same as the nested layout path (FederatedTabsLayout). + path: "/federated-tabs/tab-3" + element: + }, { + parentPath: "/federated-tabs" + }); + + runtime.registerNavigationItem({ + to: "/federated-tabs/tab-3", + label: "Tab 3" + }, { + // The menu id could be anything, in this example we are using the same path as the nested layout + // path for convenience. + menuId: "/federated-tabs" + }); } ``` @@ -285,26 +281,26 @@ Similarly to how the display order of regular navigation items can be configured To force `Tab 3` to be positioned first, we'll give him a priority of `999`: -```tsx !#17 local-module/src/register.tsx +```tsx !#16 local-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { Tab3 } from "./Tab3.tsx"; export const register: ModuleRegisterFunction = runtime => { - runtime.registerRoutes([ - { - path: "/federated-tabs/tab-3" - element: - } - ], { parentPath: "/federated-tabs" }); - - runtime.registerNavigationItems([ - { - to: "/federated-tabs/tab-3", - label: "Tab 3", - // Highest priority goes first. - priority: 999 - } - ], { menuId: "/federated-tabs" }); + runtime.registerRoute({ + path: "/federated-tabs/tab-3" + element: + }, { + parentPath: "/federated-tabs" + }); + + runtime.registerNavigationItem({ + to: "/federated-tabs/tab-3", + label: "Tab 3", + // Highest priority goes first. + priority: 999 + }, { + menuId: "/federated-tabs" + }); } ``` diff --git a/docs/guides/override-a-react-context.md b/docs/guides/override-a-react-context.md index bd6e88bc3..d01fa6467 100644 --- a/docs/guides/override-a-react-context.md +++ b/docs/guides/override-a-react-context.md @@ -35,17 +35,15 @@ export function App() { } ``` -```tsx !#8 remote-module/src/register.tsx +```tsx !#7 remote-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { ColoredPage } from "./ColoredPage.tsx"; export const register: ModuleRegisterFunction = runtime => { - runtime.registerRoutes([ - { - path: "/colored-page", - element: - } - ]); + runtime.registerRoute({ + path: "/colored-page", + element: + }); } ``` @@ -69,22 +67,20 @@ In the previous code samples, the host application provides a value for the `Bac Now, suppose the requirements change, and one remote module's pages need to have a `red` background. The context can be overriden for the remote module by declaring a new provider directly in the routes registration: -```tsx !#10-12 remote-module/src/register.tsx +```tsx !#9-11 remote-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { BackgroundColorContext } from "@sample/shared"; import { ColoredPage } from "./ColoredPage.tsx"; export const register: ModuleRegisterFunction = runtime => { - runtime.registerRoutes([ - { - path: "/colored-page", - element: ( - - - - ) - } - ]); + runtime.registerRoute({ + path: "/colored-page", + element: ( + + + + ) + }); } ``` @@ -107,12 +103,10 @@ function withRedBackground(page: ReactElement) { } export const register: ModuleRegisterFunction = runtime => { - runtime.registerRoutes([ - { - path: "/colored-page", - element: withRedBackground() - } - ]); + runtime.registerRoute({ + path: "/colored-page", + element: withRedBackground() + }); } ``` @@ -169,7 +163,7 @@ To update the host application without breaking the remote modules, the recommen As `@hopper/components` expose the `ThemeContext`, the context must be re-declared in each remote module until every part of the federated application has been updated to the latest version of Hopper: -```tsx !#6-12,18 remote-module/src/register.tsx +```tsx !#6-12,17 remote-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { ThemeContext } from "@hopper/components"; import { Page } from "./Page.tsx"; @@ -184,12 +178,10 @@ function withHopperTheme(page: ReactElement) { } export const register: ModuleRegisterFunction = runtime => { - runtime.registerRoutes([ - { - path: "/page", - element: withHopperTheme() - } - ]); + runtime.registerRoute({ + path: "/page", + element: withHopperTheme() + }); } ``` diff --git a/docs/guides/override-the-host-layout.md b/docs/guides/override-the-host-layout.md index 503ff2790..c1713e15e 100644 --- a/docs/guides/override-the-host-layout.md +++ b/docs/guides/override-the-host-layout.md @@ -176,24 +176,22 @@ import { LocalErrorBoundary } from "./LocalErrorBoundary.tsx"; import { Login } from "./Login.tsx"; export function register: ModuleRegisterFunction(runtime) { - runtime.registerRoutes([ - { - path: "/login", - hoist: true, - element: , - children: [ - { - errorElement: , - children: [ - { - index: true, - element: - } - ] - } - ] - } - ]); + runtime.registerRoute({ + path: "/login", + hoist: true, + element: , + children: [ + { + errorElement: , + children: [ + { + index: true, + element: + } + ] + } + ] + }); } ``` diff --git a/docs/reference/registration/registerLocalModules.md b/docs/reference/registration/registerLocalModules.md index b26c783df..96985470f 100644 --- a/docs/reference/registration/registerLocalModules.md +++ b/docs/reference/registration/registerLocalModules.md @@ -48,18 +48,14 @@ import type { AppContext } from "@sample/shared"; import { About } from "./About.tsx"; export function register: ModuleRegisterFunction(runtime, context) { - runtime.registerRoutes([ - { - path: "/about", - element: - } - ]); - - runtime.registerNavigationItems([ - { - to: "/about", - label: "About" - } - ]); + runtime.registerRoute({ + path: "/about", + element: + }); + + runtime.registerNavigationItem({ + to: "/about", + label: "About" + }); } ``` diff --git a/docs/reference/registration/registerRemoteModules.md b/docs/reference/registration/registerRemoteModules.md index 00200fb84..b60525991 100644 --- a/docs/reference/registration/registerRemoteModules.md +++ b/docs/reference/registration/registerRemoteModules.md @@ -58,19 +58,15 @@ import type { AppContext } from "@sample/shared"; import { About } from "./About.tsx"; export function register: ModuleRegisterFunction(runtime, context) { - runtime.registerRoutes([ - { - path: "/about", - element: - } - ]); - - runtime.registerNavigationItems([ - { - to: "/about", - label: "About" - } - ]); + runtime.registerRoute({ + path: "/about", + element: + }); + + runtime.registerNavigationItem({ + to: "/about", + label: "About" + }); } ``` diff --git a/docs/reference/routing/useHoistedRoutes.md b/docs/reference/routing/useHoistedRoutes.md index e111b68f9..591465b2e 100644 --- a/docs/reference/routing/useHoistedRoutes.md +++ b/docs/reference/routing/useHoistedRoutes.md @@ -86,27 +86,23 @@ export function App() { } ``` -```tsx !#10-11 remote-module/src/register.tsx +```tsx !#9-10 remote-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { RemoteErrorBoundary } from "./RemoteErrorBoundary.tsx"; import { About } from "./About.tsx"; export function register: ModuleRegisterFunction(runtime) { - runtime.registerRoutes([ - { - path: "/about", - element: , - errorElement: - hoist: true - } - ]); - - runtime.registerNavigationItems([ - { - to: "/about", - label: "About" - } - ]); + runtime.registerRoute({ + path: "/about", + element: , + errorElement: + hoist: true + }); + + runtime.registerNavigationItem({ + to: "/about", + label: "About" + }); } ``` @@ -162,40 +158,36 @@ export function App() { } ``` -```tsx !#10,13,16,19-20 remote-module/src/register.tsx +```tsx !#9,12,15,18-19 remote-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { RemoteLayout } from "./RemoteLayout.tsx"; import { RemoteErrorBoundary} from "./RemoteErrorBoundary.tsx"; import { About } from "./About.tsx"; export function register: ModuleRegisterFunction(runtime) { - runtime.registerRoutes([ - { - path: "/about", - hoist: true, - // Will render the "About" page inside the "RemoteLayout" rather than the "RootLayout". - // For more information about React Router's nested routes, view https://reactrouter.com/en/main/start/tutorial#nested-routes. - element: , - children: [ - { - errorElement: , - children: [ - { - index: true, - element: - } - ] - } - ] - } - ]); - - runtime.registerNavigationItems([ - { - to: "/about", - label: "About" - } - ]); + runtime.registerRoute({ + path: "/about", + hoist: true, + // Will render the "About" page inside the "RemoteLayout" rather than the "RootLayout". + // For more information about React Router's nested routes, view https://reactrouter.com/en/main/start/tutorial#nested-routes. + element: , + children: [ + { + errorElement: , + children: [ + { + index: true, + element: + } + ] + } + ] + }); + + runtime.registerNavigationItem({ + to: "/about", + label: "About" + }); } ``` @@ -254,22 +246,20 @@ export function App() { } ``` -```tsx !#9-10,13 remote-module/src/register.tsx +```tsx !#8-9,12 remote-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { RemoteErrorBoundary } from "./RemoteErrorBoundary.tsx"; import { Login } from "./Login.tsx"; export function register: ModuleRegisterFunction(runtime) { - runtime.registerRoutes([ - { - path: "/login", - element: , - errorElement: , - // By hoisting the "Login" page, it will now be rendered outside of the default - // authenticated boundary and will therefore be public. - hoist: true - } - ]); + runtime.registerRoute({ + path: "/login", + element: , + errorElement: , + // By hoisting the "Login" page, it will now be rendered outside of the default + // authenticated boundary and will therefore be public. + hoist: true + }); } ``` diff --git a/docs/reference/runtime/runtime-class.md b/docs/reference/runtime/runtime-class.md index 3b83e50dc..296102fd2 100644 --- a/docs/reference/runtime/runtime-class.md +++ b/docs/reference/runtime/runtime-class.md @@ -57,11 +57,19 @@ const runtime = new Runtime({ ### Register routes +```ts +runtime.registerRoute(route, options?: {}) +``` + +- `options`: An optional object literal of options: + - `parentPath`: An optional path of a parent route to register this new route under. + - `parentName`: An optional name of a parent route to register this new route under. + A Squide route can either be a `RootRoute` or a `Route`. - `RootRoute`: accept any properties of a React Router [Route](https://reactrouter.com/en/main/components/route) component with the addition of: - - `hoist`: An optional boolean property to register the route at the root of the router. The default value is `false`. - - `visibility`: An optional property to register a route as a `public` or `authenticated` route. The default value is `authenticated`. + - `hoist`: An optional boolean value to register the route at the root of the router. The default value is `false`. + - `visibility`: An optional visibility indicator for the route. Values are `public` or `authenticated` and the default value is `authenticated`. - `name`: An optional name for the route. - `Route`: accept any properties of a React Router [Route](https://reactrouter.com/en/main/components/route) component with the addition of: - `name`: An optional name for the route. @@ -70,28 +78,24 @@ A Squide route can either be a `RootRoute` or a `Route`. import { Page } from "./Page.tsx" // Register a new route from a local or remote module. -runtime.registerRoutes([ - { - path: "/page-1", - element: - } -]); +runtime.registerRoute({ + path: "/page-1", + element: +}); ``` ### Register an hoisted route Unlike a regular page, a hoisted page is added at the root of the router, outside of the boundaries of the host application's root layout. This means that a hoisted page has full control over its rendering. -```tsx !#7 +```tsx !#6 import { Page } from "./Page.tsx"; -runtime.registerRoutes([ - { - path: "/page-1", - element: , - hoist: true - } -]); +runtime.registerRoute({ + path: "/page-1", + element: , + hoist: true +}); ``` [!ref text="Setup the host application to accept hoisted routes"](/reference/routing/useHoistedRoutes.md) @@ -100,65 +104,61 @@ runtime.registerRoutes([ When registering a route, a hint can be provided to indicate if the route is intended to be displayed as a `public` or `authenticated` route. This is especially useful when dealing with code that conditionally fetch data for authenticated routes. -```tsx !#7 +```tsx !#6 import { Page } from "./Page.tsx"; -runtime.registerRoutes([ - { - path: "/page-1", - element: , - visibility: "public" - } -]); +runtime.registerRoute({ + path: "/page-1", + element: , + visibility: "public" +}); ``` ### Register a named route -The `registerRoutes` function accepts a `parentName` property, allowing a route to be [nested under an existing parent route](#register-nested-routes-under-an-existing-route). When searching for the parent route matching the `parentName` property, the `parentName` will be matched against the `name` property of every route. +The `registerRoute` function accepts a `parentName` property, allowing a route to be [nested under an existing parent route](#register-nested-routes-under-an-existing-route). When searching for the parent route matching the `parentName` property, the `parentName` will be matched against the `name` property of every route. -> A `name` property should usually only be defined for routes that doesn't have a path. +> A `name` property should usually only be defined for routes that doesn't have a path like an error boundary or an authentication boundary. -```tsx !#5 -import { Page } from "./Page.tsx"; +```tsx !#4 +import { RootErrorBoundary } from "./Page.tsx"; -runtime.registerRoutes([ - { - name: "page-1", - element: - } -]); +runtime.registerRoute({ + name: "error-boundary", + element: +}); ``` ### Register nested routes under an existing route React router [nested routes](https://reactrouter.com/en/main/start/tutorial#nested-routes) enable applications to render nested layouts at various points within the router tree. This is quite helpful for federated applications as it enables composable and decoupled UI. -To fully harness the power of nested routes, the `registerRoutes` function allows a route to be registered **under any** previously **registered route**, even if that route was registered by another module. The only requirement is that the **parent route** must have been registered with the `registerRoutes` function. +To fully harness the power of nested routes, the `registerRoute` function allows a route to be registered **under any** previously **registered route**, even if that route was registered by another module. The only requirement is that the **parent route** must have been registered with the `registerRoute` function. -When registering a new route with the `registerRoutes` function, to render the route under a parent route, specify a `parentPath` property that matches the parent route's `path` property: +When registering a new route with the `registerRoute` function, to render the route under a parent route, specify a `parentPath` property that matches the parent route's `path` property: -```tsx !#8 +```tsx !#7 import { Page } from "./Page.tsx"; -runtime.registerRoutes([ - { - path: "/layout/page-1", - element: - } -], { parentPath: "/layout" }); // Register the page under an existing route having "/layout" as its "path". +runtime.registerRoute({ + path: "/layout/page-1", + element: +}, { + parentPath: "/layout" // Register the page under an existing route having "/layout" as its "path". +}); ``` Or a `parentName` property that matches the parent route's `name` property: -```tsx !#8 +```tsx !#7 import { Page } from "./Page.tsx"; -runtime.registerRoutes([ - { - path: "/layout/page-1", - element: - } -], { parentName: "layout" }); // Register the page under an existing route having "layout" as its "name". +runtime.registerRoute({ + path: "/page-1", + element: +}, { + parentName: "error-boundary" // Register the page under an existing route having "error-boundary" as its "name". +}); ``` !!!info @@ -175,6 +175,13 @@ const routes = runtime.routes; ### Register navigation items +```ts +runtime.registerNavigationItem(item, options?: {}) +``` + +- `options`: An optional object literal of options: + - `menuId`: An optional menu id to associate the item with. + A Squide navigation item can either be a `NavigationLink` or a `NavigationSection`. Both types can be intertwined to create a multi-level menu hierarchy. A `NavigationSection` item is used to setup a new level while a `NavigationLink` define a link. - `NavigationSection` accept the following properties: @@ -189,12 +196,10 @@ A Squide navigation item can either be a `NavigationLink` or a `NavigationSectio ```ts // Register a new navigation item from a local or remote module. -runtime.registerNavigationItems([ - { - to: "/page-1", - label: "Page 1" - } -]); +runtime.registerNavigationItem({ + to: "/page-1", + label: "Page 1" +}); ``` [!ref text="Setup the host application to render navigation items"](/reference/routing/useRenderedNavigationItems.md) @@ -209,30 +214,28 @@ runtime.registerNavigationItems([ // ------- Nested Nested Link // --- Nested Link // Link -runtime.registerNavigationItems([ - { - label: "Section", - children: [ - { - label: "Nested Section", - children: [ - { - to: "#", - label: "Nested Nested Link", - } - ] - }, - { - to: "#", - label: "Nested Link" - } - ] - }, - { - to: "#", - label: "Link" - } -]); +runtime.registerNavigationItem({ + label: "Section", + children: [ + { + label: "Nested Section", + children: [ + { + to: "#", + label: "Nested Nested Link", + } + ] + }, + { + to: "#", + label: "Nested Link" + } + ] +}, +{ + to: "#", + label: "Link" +}); ``` ### Sort registered navigation items @@ -244,90 +247,81 @@ A `priority` property can be added to a navigation item to affect it's position - If an item have a priority `> 0`, the item will be positioned before any other items with a lower priority (or without an explicit priority value). - If an item have a priority `< 0`, the item will be positioned after any other items with a higher priority (or without an explicit priority value). -```ts !#5,12 -runtime.registerNavigationItems([ - { - to: "/about", - label: "About", - priority: 10 - }, - { - to: "/home", - label: "Home", - // Because the "Home" navigation item has an higher priority, it will be rendered - // before the "About" navigation item. - priority: 100 - } -]); +```ts !#4,12 +runtime.registerNavigationItem({ + to: "/about", + label: "About", + priority: 10 +}); + +runtime.registerNavigationItem({ + to: "/home", + label: "Home", + // Because the "Home" navigation item has an higher priority, it will be rendered + // before the "About" navigation item. + priority: 100 +}); ``` ### Use a React element as navigation item label -```tsx !#6-9 +```tsx !#5-8 import { QuestionMarkIcon } from "@sample/icons"; -runtime.registerNavigationItems([ - { - to: "/about", - label: ( - - About - ) - } -]); +runtime.registerNavigationItem({ + to: "/about", + label: ( + + About + ) +}); ``` ### Style a navigation item -```ts !#5-7 -runtime.registerNavigationItems([ - { - to: "/about", - label: "About", - style: { - backgroundColor: "#000" - } +```ts !#4-6 +runtime.registerNavigationItem({ + to: "/about", + label: "About", + style: { + backgroundColor: "#000" } -]); +}); ``` ### Open a navigation link in a new tab -```ts !#5 -runtime.registerNavigationItems([ - { - to: "/about", - label: "About", - target: "_blank" - } -]); +```ts !#4 +runtime.registerNavigationItem({ + to: "/about", + label: "About", + target: "_blank" +}); ``` ### Render additional props on a navigation item -```ts !#5-7 -runtime.registerNavigationItems([ - { +```ts !#4-6 +runtime.registerNavigationItem({ to: "/about", label: "About", additionalProps: { highlight: true } - } -]); + }); ``` ### Register navigation items for a specific menu -By default, every navigation item registered with the `registerNavigationItems` function is registered as part of the `root` navigation menu. To register a navigation item for a different navigation menu, specify a `menuId` property when registering the items. +By default, every navigation item registered with the `registerNavigationItem` function is registered as part of the `root` navigation menu. To register a navigation item for a different navigation menu, specify a `menuId` property when registering the items. -```tsx !#6 -runtime.registerNavigationItems([ - { - to: "/layout/page-1", - label: "Page 1" - } -], { menuId: "my-custom-layout" }); +```tsx !#5 +runtime.registerNavigationItem({ + to: "/layout/page-1", + label: "Page 1" +}, { + menuId: "my-custom-layout" +}); ``` ### Retrieve navigation items diff --git a/packages/core/src/runtime/abstractRuntime.ts b/packages/core/src/runtime/abstractRuntime.ts index b5cb45d88..5d11f2e02 100644 --- a/packages/core/src/runtime/abstractRuntime.ts +++ b/packages/core/src/runtime/abstractRuntime.ts @@ -16,12 +16,12 @@ export interface RuntimeOptions { sessionAccessor?: SessionAccessorFunction; } -export interface RegisterRoutesOptions { +export interface RegisterRouteOptions { parentPath?: string; parentName?: string; } -export interface RegisterNavigationItemsOptions { +export interface RegisterNavigationItemOptions { menuId?: string; } @@ -44,11 +44,11 @@ export abstract class AbstractRuntime = new Map(); - add(menuId: string, navigationItems: RootNavigationItem[]) { + add(menuId: string, navigationItem: RootNavigationItem) { // Create a new array so the navigation items array is immutable. const items = [ ...(this.#menus.get(menuId) ?? []), - ...navigationItems + navigationItem ]; this.#menus.set(menuId, items); diff --git a/packages/react-router/src/routeRegistry.ts b/packages/react-router/src/routeRegistry.ts index 93308cba1..7832f8128 100644 --- a/packages/react-router/src/routeRegistry.ts +++ b/packages/react-router/src/routeRegistry.ts @@ -1,4 +1,4 @@ -import type { RegisterRoutesOptions } from "@squide/core"; +import type { RegisterRouteOptions } from "@squide/core"; import type { IndexRouteObject, NonIndexRouteObject } from "react-router-dom"; export type RouteVisibility = "public" | "authenticated"; @@ -22,6 +22,7 @@ export type RootRoute = Route & { type?: RouteType; }; + function normalizePath(routePath?: string) { if (routePath && routePath !== "/" && routePath.endsWith("/")) { return routePath.substring(0, routePath.length - 1); @@ -49,6 +50,12 @@ export interface AddRouteReturnType { completedPendingRegistrations?: Route[]; } +const ManagedRoutesOutletName = "__squide-managed-routes-outlet__"; + +export const ManagedRoutesOutlet: Route = { + name: ManagedRoutesOutletName +}; + export class RouteRegistry { #routes: RootRoute[]; @@ -64,18 +71,43 @@ export class RouteRegistry { this.#routes = []; } - add(routes: RootRoute[] | Route[], { parentPath, parentName }: RegisterRoutesOptions = {}) { + #validateRootRoutes(route: RootRoute, { parentPath, parentName }: RegisterRouteOptions = {}) { + if (route.hoist && parentPath) { + throw new Error(`[squide] A route cannot have the "hoist" property when a "publicPath" option is provided. Route id: "${route.path ?? route.name ?? "(no identifier)"}".`); + } + + if (route.hoist && parentName) { + throw new Error(`[squide] A route cannot have the "hoist" property when a "parentName" option is provided. Route id: "${route.path ?? route.name ?? "(no identifier)"}".`); + } + } + + add(route: RootRoute, options: RegisterRouteOptions = {}) { + const { + parentPath, + parentName + } = options; + + this.#validateRootRoutes(route, options); + if (parentPath) { // The normalized path cannot be undefined because it's been provided by the consumer // (e.g. it cannot be a pathless route). - return this.#addNestedRoutes(routes, normalizePath(parentPath)!); + return this.#addNestedRoutes([route], normalizePath(parentPath)!); } - if (parentName) { - return this.#addNestedRoutes(routes, parentName); + let _parentName = parentName; + + if (!route.hoist && !_parentName) { + _parentName = ManagedRoutesOutletName; + } + + if (_parentName) { + console.log("******* Adding nested route:", route, " for parent name: ", _parentName, " indexes: ", this.#routesIndex); + + return this.#addNestedRoutes([route], _parentName); } - return this.#addRootRoutes(routes); + return this.#addRootRoutes([route]); } #addIndex(route: Route) { @@ -112,7 +144,7 @@ export class RouteRegistry { } #addRootRoutes(routes: RootRoute[]): AddRouteReturnType { - // Creates a copy of the route objects and a type to each route to indicate + // Creates a copy of the route objects and add a "type" property to each route indicating // that it's a root route. const _routes: RootRoute[] = routes.map(x => ({ ...x, diff --git a/packages/react-router/src/runtime.ts b/packages/react-router/src/runtime.ts index 0d56a1a1c..87b370daa 100644 --- a/packages/react-router/src/runtime.ts +++ b/packages/react-router/src/runtime.ts @@ -1,21 +1,21 @@ -import { AbstractRuntime, RootMenuId, type RegisterNavigationItemsOptions, type RegisterRoutesOptions } from "@squide/core"; +import { AbstractRuntime, RootMenuId, type RegisterNavigationItemOptions, type RegisterRouteOptions } from "@squide/core"; import { NavigationItemRegistry, type RootNavigationItem } from "./navigationItemRegistry.ts"; -import { RouteRegistry, type RootRoute, type Route } from "./routeRegistry.ts"; +import { ManagedRoutesOutlet, RouteRegistry, type RootRoute, type Route } from "./routeRegistry.ts"; export class Runtime extends AbstractRuntime { readonly #routeRegistry = new RouteRegistry(); readonly #navigationItemRegistry = new NavigationItemRegistry(); - registerRoutes(routes: RootRoute[] | Route[], options: RegisterRoutesOptions = {}) { - const result = this.#routeRegistry.add(routes, options); + registerRoute(route: RootRoute, options: RegisterRouteOptions = {}) { + const result = this.#routeRegistry.add(route, options); if (result.registrationStatus === "registered") { const parentId = options.parentPath ?? options.parentName; const parentLog = parentId ? ` as children of the "${parentId}" route` : ""; this._logger.debug( - `[squide] The following route${routes.length !== 1 ? "s" : ""} has been %cregistered%c${parentLog} for a total of ${this.#routeRegistry.routes.length} route${this.#routeRegistry.routes.length !== 1 ? "s" : ""}.`, "color: white; background-color: green;", "%s", - "Newly registered routes:", routes, + `[squide] The following route has been %cregistered%c${parentLog}.`, "color: white; background-color: green;", "%s", + "Newly registered route:", route, "All registered routes:", this.#routeRegistry.routes ); @@ -30,8 +30,8 @@ export class Runtime extends AbstractRuntime 0) { - let message = `[squide] ${pendingRegistrations.size} parent route${pendingRegistrations.size !== 1 ? "s" : ""} were expected to be registered but ${pendingRegistrations.size !== 1 ? "are" : "is"} missing:\r\n\r\n`; + if (pendingRegistrations.has(ManagedRoutesOutlet.name!)) { + throw new Error("[squide] The \"ManagedRoutesOutlet\" route is missing from the router configuration. The \"ManagedRoutesOutlet\" route must be added as children of an hoisted route. Did you forget to hoist the parent route that includes the \"ManagedRoutesOutlet\" route?"); + } + let message = `[squide] ${pendingRegistrations.size} parent route${pendingRegistrations.size !== 1 ? "s" : ""} were expected to be registered but ${pendingRegistrations.size !== 1 ? "are" : "is"} missing:\r\n\r\n`; let index = 0; // It's easier to use for ... of with a Map object. @@ -73,7 +76,7 @@ export class Runtime extends AbstractRuntime { }); }); -describe("registerRoutes", () => { +describe("registerRoute", () => { test("can register an index route", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - index: true, - element:
Hello!
- } - ]); + runtime.registerRoute({ + index: true, + element:
Hello!
+ }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].index).toBeTruthy(); @@ -72,11 +70,9 @@ describe("registerRoutes", () => { test("can register a pathless route", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - element:
Hello!
- } - ]); + runtime.registerRoute({ + element:
Hello!
+ }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].index).toBeUndefined(); @@ -86,20 +82,21 @@ describe("registerRoutes", () => { test("can register multiple pathless routes", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - element:
Hello!
- }, - { - element:
How
- }, - { - element:
Are
- }, - { - element:
You?
- } - ]); + runtime.registerRoute({ + element:
Hello!
+ }); + + runtime.registerRoute({ + element:
How
+ }); + + runtime.registerRoute({ + element:
Are
+ }); + + runtime.registerRoute({ + element:
You?
+ }); expect(runtime.routes.length).toBe(4); }); @@ -107,22 +104,20 @@ describe("registerRoutes", () => { test("can register a deeply nested route with pathless parent routes", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - element:
Hello
, - children: [ - { - element:
You!
, - children: [ - { - path: "/deeply-nested-route", - element:
Hello from nested!
- } - ] - } - ] - } - ]); + runtime.registerRoute({ + element:
Hello
, + children: [ + { + element:
You!
, + children: [ + { + path: "/deeply-nested-route", + element:
Hello from nested!
+ } + ] + } + ] + }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].children![0].children![0].path).toBe("/deeply-nested-route"); @@ -131,22 +126,20 @@ describe("registerRoutes", () => { test("can register a deeply nested index route with pathless parent routes", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - element:
Hello
, - children: [ - { - element:
You!
, - children: [ - { - index: true, - element:
Hello from nested!
- } - ] - } - ] - } - ]); + runtime.registerRoute({ + element:
Hello
, + children: [ + { + element:
You!
, + children: [ + { + index: true, + element:
Hello from nested!
+ } + ] + } + ] + }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].children![0].children![0].index).toBeTruthy(); @@ -155,13 +148,11 @@ describe("registerRoutes", () => { test("can register an hoisted route", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - path: "/root", - element:
Hello!
, - hoist: true - } - ]); + runtime.registerRoute({ + path: "/root", + element:
Hello!
, + hoist: true + }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].path).toBe("/root"); @@ -171,13 +162,11 @@ describe("registerRoutes", () => { test("can register a route with a \"public\" visibility", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - path: "/public", - element:
Hello!
, - visibility: "public" - } - ]); + runtime.registerRoute({ + path: "/public", + element:
Hello!
, + visibility: "public" + }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].path).toBe("/public"); @@ -187,13 +176,11 @@ describe("registerRoutes", () => { test("can register a route with a \"authenticated\" visibility", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - path: "/authenticated", - element:
Hello!
, - visibility: "authenticated" - } - ]); + runtime.registerRoute({ + path: "/authenticated", + element:
Hello!
, + visibility: "authenticated" + }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].path).toBe("/authenticated"); @@ -203,12 +190,10 @@ describe("registerRoutes", () => { test("can register a root route with a name", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - name: "foo", - element:
Hello!
- } - ]); + runtime.registerRoute({ + name: "foo", + element:
Hello!
+ }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].name).toBe("foo"); @@ -217,17 +202,15 @@ describe("registerRoutes", () => { test("can register a nested route with a name", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - element:
Hello
, - children: [ - { - name: "foo", - element:
You!
- } - ] - } - ]); + runtime.registerRoute({ + element:
Hello
, + children: [ + { + name: "foo", + element:
You!
+ } + ] + }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].children![0].name).toBe("foo"); @@ -237,21 +220,19 @@ describe("registerRoutes", () => { test("when the parent route has already been registered, register the nested route", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - path: "/layout", - element:
Hello!
- } - ]); + runtime.registerRoute({ + path: "/layout", + element:
Hello!
+ }); expect(runtime.routes.length).toBe(1); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { parentPath: "/layout" }); + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentPath: "/layout" + }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].children![0].path).toBe("/layout/nested"); @@ -260,12 +241,12 @@ describe("registerRoutes", () => { test("when the parent route has not been registered, do not register the nested route", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { parentPath: "/layout" }); + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentPath: "/layout" + }); expect(runtime.routes.length).toBe(0); }); @@ -273,38 +254,34 @@ describe("registerRoutes", () => { test("when the parent route has not been registered, register the pending route once the parent route is registered", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { parentPath: "/layout" }); + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentPath: "/layout" + }); - runtime.registerRoutes([ - { - path: "/layout/another-nested", - element:
Hello!
- } - ], { parentPath: "/layout" }); + runtime.registerRoute({ + path: "/layout/another-nested", + element:
Hello!
+ }, { + parentPath: "/layout" + }); expect(runtime.routes.length).toBe(0); - runtime.registerRoutes([ - { - path: "/foo", - element:
Hello!
- } - ]); + runtime.registerRoute({ + path: "/foo", + element:
Hello!
+ }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].children).toBeUndefined(); - runtime.registerRoutes([ - { - path: "/layout", - element:
Hello!
- } - ]); + runtime.registerRoute({ + path: "/layout", + element:
Hello!
+ }); expect(runtime.routes.length).toBe(2); expect(runtime.routes[1].children?.length).toBe(2); @@ -313,31 +290,29 @@ describe("registerRoutes", () => { test("when the parent route has not been registered, and the parent route is nested in a pending registration single block, register the pending route once the parent route is registered", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { parentPath: "/layout" }); + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentPath: "/layout" + }); expect(runtime.routes.length).toBe(0); - runtime.registerRoutes([ - { - element:
Hello
, - children: [ - { - element:
You!
, - children: [ - { - path: "/layout", - element:
Hello from nested!
- } - ] - } - ] - } - ]); + runtime.registerRoute({ + element:
Hello
, + children: [ + { + element:
You!
, + children: [ + { + path: "/layout", + element:
Hello from nested!
+ } + ] + } + ] + }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].path).toBeUndefined(); @@ -347,26 +322,24 @@ describe("registerRoutes", () => { test("can register a route under a deeply nested layout", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - path: "/layout", - element:
Hello!
- } - ]); + runtime.registerRoute({ + path: "/layout", + element:
Hello!
+ }); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { parentPath: "/layout" }); + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentPath: "/layout" + }); - runtime.registerRoutes([ - { - path: "/layout/nested/another-level", - element:
Hello!
- } - ], { parentPath: "/layout/nested" }); + runtime.registerRoute({ + path: "/layout/nested/another-level", + element:
Hello!
+ }, { + parentPath: "/layout/nested" + }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].children![0].children![0].path).toBe("/layout/nested/another-level"); @@ -375,39 +348,37 @@ describe("registerRoutes", () => { test("can register a route under a deeply nested layout that has been registered in a single block", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - element:
Hello
, - children: [ - { - element:
You!
, - children: [ - { - path: "/deeply-nested-layout", - element:
Hello from nested!
- } - ] - } - ] - } - ]); + runtime.registerRoute({ + element:
Hello
, + children: [ + { + element:
You!
, + children: [ + { + path: "/deeply-nested-layout", + element:
Hello from nested!
+ } + ] + } + ] + }); - runtime.registerRoutes([ - { - path: "/deeply-nested-layout/another-level", - element:
Hello!
- } - ], { parentPath: "/deeply-nested-layout" }); + runtime.registerRoute({ + path: "/deeply-nested-layout/another-level", + element:
Hello!
+ }, { + parentPath: "/deeply-nested-layout" + }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].children![0].children![0].children![0].path).toBe("/deeply-nested-layout/another-level"); - runtime.registerRoutes([ - { - path: "/deeply-nested-layout/another-level/yet-another-level", - element:
Hello!
- } - ], { parentPath: "/deeply-nested-layout/another-level" }); + runtime.registerRoute({ + path: "/deeply-nested-layout/another-level/yet-another-level", + element:
Hello!
+ }, { + parentPath: "/deeply-nested-layout/another-level" + }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].children![0].children![0].children![0].children![0].path).toBe("/deeply-nested-layout/another-level/yet-another-level"); @@ -416,19 +387,17 @@ describe("registerRoutes", () => { test("when the specified parent path has a trailing separator but the parent route path doesn't have a trailing separator, the nested route is registered", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - path: "/layout", - element:
Hello!
- } - ]); + runtime.registerRoute({ + path: "/layout", + element:
Hello!
+ }); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { parentPath: "/layout/" }); + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentPath: "/layout/" + }); expect(runtime.routes[0].children![0].path).toBe("/layout/nested"); }); @@ -436,19 +405,17 @@ describe("registerRoutes", () => { test("when the specified parent path doesn't have a trailing separator but the parent route path have a trailing separator, the nested route is registered", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - path: "/layout/", - element:
Hello!
- } - ]); + runtime.registerRoute({ + path: "/layout/", + element:
Hello!
+ }); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { parentPath: "/layout" }); + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentPath: "/layout" + }); expect(runtime.routes[0].children![0].path).toBe("/layout/nested"); }); @@ -458,21 +425,19 @@ describe("registerRoutes", () => { test("when the parent route has already been registered, register the nested route", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - name: "layout", - element:
Hello!
- } - ]); + runtime.registerRoute({ + name: "layout", + element:
Hello!
+ }); expect(runtime.routes.length).toBe(1); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { parentName: "layout" }); + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentName: "layout" + }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].children![0].path).toBe("/layout/nested"); @@ -481,12 +446,12 @@ describe("registerRoutes", () => { test("when the parent route has not been registered, do not register the nested route", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { parentName: "layout" }); + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentName: "layout" + }); expect(runtime.routes.length).toBe(0); }); @@ -494,38 +459,34 @@ describe("registerRoutes", () => { test("when the parent route has not been registered, register the pending route once the parent route is registered", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { parentName: "layout" }); + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentName: "layout" }); - runtime.registerRoutes([ - { - path: "/layout/another-nested", - element:
Hello!
- } - ], { parentName: "layout" }); + runtime.registerRoute({ + path: "/layout/another-nested", + element:
Hello!
+ } + , { + parentName: "layout" + }); expect(runtime.routes.length).toBe(0); - runtime.registerRoutes([ - { - path: "/foo", - element:
Hello!
- } - ]); + runtime.registerRoute({ + path: "/foo", + element:
Hello!
+ }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].children).toBeUndefined(); - runtime.registerRoutes([ - { - name: "layout", - element:
Hello!
- } - ]); + runtime.registerRoute({ + name: "layout", + element:
Hello!
+ }); expect(runtime.routes.length).toBe(2); expect(runtime.routes[1].children?.length).toBe(2); @@ -534,31 +495,29 @@ describe("registerRoutes", () => { test("when the parent route has not been registered, and the parent route is nested in a pending registration single block, register the pending route once the parent route is registered", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { parentName: "layout" }); + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentName: "layout" + }); expect(runtime.routes.length).toBe(0); - runtime.registerRoutes([ - { - element:
Hello
, - children: [ - { - element:
You!
, - children: [ - { - name: "layout", - element:
Hello from nested!
- } - ] - } - ] - } - ]); + runtime.registerRoute({ + element:
Hello
, + children: [ + { + element:
You!
, + children: [ + { + name: "layout", + element:
Hello from nested!
+ } + ] + } + ] + }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].path).toBeUndefined(); @@ -568,26 +527,25 @@ describe("registerRoutes", () => { test("can register a route under a deeply nested layout", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - name: "layout", - element:
Hello!
- } - ]); + runtime.registerRoute({ + name: "layout", + element:
Hello!
+ }); - runtime.registerRoutes([ - { - name: "layout-nested", - element:
Hello!
- } - ], { parentName: "layout" }); + runtime.registerRoute({ + name: "layout-nested", + element:
Hello!
+ } + , { + parentName: "layout" + }); - runtime.registerRoutes([ - { - path: "/layout/nested/another-level", - element:
Hello!
- } - ], { parentName: "layout-nested" }); + runtime.registerRoute({ + path: "/layout/nested/another-level", + element:
Hello!
+ }, { + parentName: "layout-nested" + }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].children![0].children![0].path).toBe("/layout/nested/another-level"); @@ -596,39 +554,37 @@ describe("registerRoutes", () => { test("can register a route under a deeply nested layout that has been registered in a single block", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - element:
Hello
, - children: [ - { - element:
You!
, - children: [ - { - name: "deeply-nested-layout", - element:
Hello from nested!
- } - ] - } - ] - } - ]); + runtime.registerRoute({ + element:
Hello
, + children: [ + { + element:
You!
, + children: [ + { + name: "deeply-nested-layout", + element:
Hello from nested!
+ } + ] + } + ] + }); - runtime.registerRoutes([ - { - name: "deeply-nested-layout/another-level", - element:
Hello!
- } - ], { parentName: "deeply-nested-layout" }); + runtime.registerRoute({ + name: "deeply-nested-layout/another-level", + element:
Hello!
+ }, { + parentName: "deeply-nested-layout" + }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].children![0].children![0].children![0].name).toBe("deeply-nested-layout/another-level"); - runtime.registerRoutes([ - { - path: "/deeply-nested-layout/another-level/yet-another-level", - element:
Hello!
- } - ], { parentName: "deeply-nested-layout/another-level" }); + runtime.registerRoute({ + path: "/deeply-nested-layout/another-level/yet-another-level", + element:
Hello!
+ }, { + parentName: "deeply-nested-layout/another-level" + }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].children![0].children![0].children![0].children![0].path).toBe("/deeply-nested-layout/another-level/yet-another-level"); @@ -636,16 +592,14 @@ describe("registerRoutes", () => { }); }); -describe("registerNavigationItems", () => { +describe("registerNavigationItem", () => { test("can register a root navigation link", () => { const runtime = new Runtime(); - runtime.registerNavigationItems([ - { - to: "/root", - label: "Root" - } - ]); + runtime.registerNavigationItem({ + to: "/root", + label: "Root" + }); expect(runtime.getNavigationItems()[0].to).toBe("/root"); }); @@ -653,17 +607,15 @@ describe("registerNavigationItems", () => { test("can register a root navigation section", () => { const runtime = new Runtime(); - runtime.registerNavigationItems([ - { - label: "Section", - children: [ - { - to: "/child", - label: "Child" - } - ] - } - ]); + runtime.registerNavigationItem({ + label: "Section", + children: [ + { + to: "/child", + label: "Child" + } + ] + }); expect(runtime.getNavigationItems()[0].label).toBe("Section"); }); @@ -671,12 +623,12 @@ describe("registerNavigationItems", () => { test("can register a navigation link for a specific menu id", () => { const runtime = new Runtime(); - runtime.registerNavigationItems([ - { - to: "/link", - label: "Link" - } - ], { menuId: "link-menu" }); + runtime.registerNavigationItem({ + to: "/link", + label: "Link" + }, { + menuId: "link-menu" + }); expect(runtime.getNavigationItems("link-menu")[0].to).toBe("/link"); }); @@ -684,17 +636,17 @@ describe("registerNavigationItems", () => { test("can register a navigation section for a specific menu id", () => { const runtime = new Runtime(); - runtime.registerNavigationItems([ - { - label: "Section", - children: [ - { - to: "/child", - label: "Child" - } - ] - } - ], { menuId: "section-menu" }); + runtime.registerNavigationItem({ + label: "Section", + children: [ + { + to: "/child", + label: "Child" + } + ] + }, { + menuId: "section-menu" + }); expect(runtime.getNavigationItems("section-menu")[0].label).toBe("Section"); }); @@ -705,19 +657,17 @@ describe("_completeRegistration", () => { test("when the registration is completed and there are no pending registrations, do nothing", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { parentPath: "/layout" }); + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentPath: "/layout" + }); - runtime.registerRoutes([ - { - path: "/layout", - element:
Hello!
- } - ]); + runtime.registerRoute({ + path: "/layout", + element:
Hello!
+ }); expect(() => runtime._completeRegistration()).not.toThrow(); }); @@ -725,12 +675,12 @@ describe("_completeRegistration", () => { test("when the registration is completed and there are pending registrations, throw an error", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { parentPath: "/layout" }); + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentPath: "/layout" + }); expect(() => runtime._completeRegistration()).toThrow(); }); @@ -740,19 +690,17 @@ describe("_completeRegistration", () => { test("when the registration is completed and there are no pending registrations, do nothing", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { parentName: "layout" }); + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentName: "layout" + }); - runtime.registerRoutes([ - { - name: "layout", - element:
Hello!
- } - ]); + runtime.registerRoute({ + name: "layout", + element:
Hello!
+ }); expect(() => runtime._completeRegistration()).not.toThrow(); }); @@ -760,12 +708,12 @@ describe("_completeRegistration", () => { test("when the registration is completed and there are pending registrations, throw an error", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { - path: "/layout/nested", - element:
Hello!
- } - ], { parentName: "layout" }); + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentName: "layout" + }); expect(() => runtime._completeRegistration()).toThrow(); }); @@ -776,37 +724,34 @@ describe("getNavigationItems", () => { test("when no menu id is specified, returns all the registered navigation items for the root menu", () => { const runtime = new Runtime(); - runtime.registerNavigationItems([ - { - to: "/item-1", - label: "Item 1" - }, - { - to: "/item-2", - label: "Item 2" - } - ]); + runtime.registerNavigationItem({ + to: "/item-1", + label: "Item 1" + }); - runtime.registerNavigationItems([ - { - to: "/item-3", - label: "Item 3" - } - ]); + runtime.registerNavigationItem({ + to: "/item-2", + label: "Item 2" + }); - runtime.registerNavigationItems([ - { - to: "/item-4", - label: "Item 4" - } - ], { menuId: "menu-1" }); + runtime.registerNavigationItem({ + to: "/item-3", + label: "Item 3" + }); - runtime.registerNavigationItems([ - { - to: "/item-5", - label: "Item 5" - } - ], { menuId: "menu-2" }); + runtime.registerNavigationItem({ + to: "/item-4", + label: "Item 4" + }, { + menuId: "menu-1" + }); + + runtime.registerNavigationItem({ + to: "/item-5", + label: "Item 5" + }, { + menuId: "menu-2" + }); expect(runtime.getNavigationItems()[0].to).toBe("/item-1"); expect(runtime.getNavigationItems()[1].to).toBe("/item-2"); @@ -816,37 +761,34 @@ describe("getNavigationItems", () => { test("when no menu id is specified, returns all the registered navigation items for that specific menu", () => { const runtime = new Runtime(); - runtime.registerNavigationItems([ - { - to: "/item-1", - label: "Item 1" - }, - { - to: "/item-2", - label: "Item 2" - } - ]); + runtime.registerNavigationItem({ + to: "/item-1", + label: "Item 1" + }); - runtime.registerNavigationItems([ - { - to: "/item-3", - label: "Item 3" - } - ]); + runtime.registerNavigationItem({ + to: "/item-2", + label: "Item 2" + }); - runtime.registerNavigationItems([ - { - to: "/item-4", - label: "Item 4" - } - ], { menuId: "menu-1" }); + runtime.registerNavigationItem({ + to: "/item-3", + label: "Item 3" + }); - runtime.registerNavigationItems([ - { - to: "/item-5", - label: "Item 5" - } - ], { menuId: "menu-2" }); + runtime.registerNavigationItem({ + to: "/item-4", + label: "Item 4" + }, { + menuId: "menu-1" + }); + + runtime.registerNavigationItem({ + to: "/item-5", + label: "Item 5" + }, { + menuId: "menu-2" + }); expect(runtime.getNavigationItems("menu-1")[0].to).toBe("/item-4"); }); diff --git a/packages/react-router/tests/useNavigationItems.test.tsx b/packages/react-router/tests/useNavigationItems.test.tsx index f01411150..a07f9236e 100644 --- a/packages/react-router/tests/useNavigationItems.test.tsx +++ b/packages/react-router/tests/useNavigationItems.test.tsx @@ -17,37 +17,34 @@ function renderWithRuntime(runtime: Runtime, menuId?: string) { test("when no menu id is specified, returns all the registered navigation items for the root menu", () => { const runtime = new Runtime(); - runtime.registerNavigationItems([ - { - to: "/item-1", - label: "Item 1" - }, - { - to: "/item-2", - label: "Item 2" - } - ]); - - runtime.registerNavigationItems([ - { - to: "/item-3", - label: "Item 3" - } - ]); - - runtime.registerNavigationItems([ - { - to: "/item-4", - label: "Item 4" - } - ], { menuId: "menu-1" }); - - runtime.registerNavigationItems([ - { - to: "/item-5", - label: "Item 5" - } - ], { menuId: "menu-2" }); + runtime.registerNavigationItem({ + to: "/item-1", + label: "Item 1" + }); + + runtime.registerNavigationItem({ + to: "/item-2", + label: "Item 2" + }); + + runtime.registerNavigationItem({ + to: "/item-3", + label: "Item 3" + }); + + runtime.registerNavigationItem({ + to: "/item-4", + label: "Item 4" + }, { + menuId: "menu-1" + }); + + runtime.registerNavigationItem({ + to: "/item-5", + label: "Item 5" + }, { + menuId: "menu-2" + }); const { result } = renderWithRuntime(runtime); @@ -57,37 +54,34 @@ test("when no menu id is specified, returns all the registered navigation items test("when a menu id is specified, returns all the registered navigation items for that specific menu", () => { const runtime = new Runtime(); - runtime.registerNavigationItems([ - { - to: "/item-1", - label: "Item 1" - }, - { - to: "/item-2", - label: "Item 2" - } - ]); - - runtime.registerNavigationItems([ - { - to: "/item-3", - label: "Item 3" - } - ]); - - runtime.registerNavigationItems([ - { - to: "/item-4", - label: "Item 4" - } - ], { menuId: "menu-1" }); - - runtime.registerNavigationItems([ - { - to: "/item-5", - label: "Item 5" - } - ], { menuId: "menu-2" }); + runtime.registerNavigationItem({ + to: "/item-1", + label: "Item 1" + }); + + runtime.registerNavigationItem({ + to: "/item-2", + label: "Item 2" + }); + + runtime.registerNavigationItem({ + to: "/item-3", + label: "Item 3" + }); + + runtime.registerNavigationItem({ + to: "/item-4", + label: "Item 4" + }, { + menuId: "menu-1" + }); + + runtime.registerNavigationItem({ + to: "/item-5", + label: "Item 5" + }, { + menuId: "menu-2" + }); const { result } = renderWithRuntime(runtime, "menu-1"); @@ -97,9 +91,10 @@ test("when a menu id is specified, returns all the registered navigation items f test("returned array is immutable", () => { const runtime = new Runtime(); - runtime.registerNavigationItems([ - { to: "/foo", label: "Foo" } - ]); + runtime.registerNavigationItem({ + to: "/foo", + label: "Foo" + }); const { result, rerender } = renderWithRuntime(runtime); @@ -110,9 +105,10 @@ test("returned array is immutable", () => { const array2 = result.current; - runtime.registerNavigationItems([ - { to: "/bar", label: "Bar" } - ]); + runtime.registerNavigationItem({ + to: "/bar", + label: "Bar" + }); // Added a new navigation item, the returned array should be a new instance. rerender(); diff --git a/packages/react-router/tests/useRoutes.test.tsx b/packages/react-router/tests/useRoutes.test.tsx index 90a8b4e08..25f1994c2 100644 --- a/packages/react-router/tests/useRoutes.test.tsx +++ b/packages/react-router/tests/useRoutes.test.tsx @@ -17,10 +17,15 @@ function renderWithRuntime(runtime: Runtime, additionalProps: RenderHook test("returns all the registered routes", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { path: "/foo", element:
Foo
}, - { path: "/bar", element:
Bar
} - ]); + runtime.registerRoute({ + path: "/foo", + element:
Foo
+ }); + + runtime.registerRoute({ + path: "/bar", + element:
Bar
+ }); const { result } = renderWithRuntime(runtime); @@ -30,9 +35,10 @@ test("returns all the registered routes", () => { test("returned array is immutable", () => { const runtime = new Runtime(); - runtime.registerRoutes([ - { path: "/foo", element:
Foo
} - ]); + runtime.registerRoute({ + path: "/foo", + element:
Foo
+ }); const { result, rerender } = renderWithRuntime(runtime); @@ -43,9 +49,10 @@ test("returned array is immutable", () => { const array2 = result.current; - runtime.registerRoutes([ - { path: "/bar", element:
Bar
} - ]); + runtime.registerRoute({ + path: "/bar", + element:
Bar
+ }); // Added a new route, the returned array should be a new instance. rerender(); diff --git a/sample/another-remote-module/src/dev/App.tsx b/sample/another-remote-module/src/dev/App.tsx index 989ef0d61..4ec07d97d 100644 --- a/sample/another-remote-module/src/dev/App.tsx +++ b/sample/another-remote-module/src/dev/App.tsx @@ -1,40 +1,13 @@ -import type { Session } from "@sample/shared"; import { useAppRouter } from "@sample/shell"; -import { Suspense, useCallback } from "react"; import { RouterProvider } from "react-router-dom"; -import { DevHome } from "./DevHome.tsx"; -import { sessionManager } from "./session.ts"; export function App() { - const onLogin = useCallback(async (username: string) => { - const session: Session = { - user: { - name: username - } - }; - - sessionManager.setSession(session); - }, []); - - const onLogout = useCallback(async () => { - sessionManager.clearSession(); - }, []); - - const router = useAppRouter(onLogin, onLogout, { - managedRoutes: [ - { - index: true, - element: - } - ] - }); + const router = useAppRouter(); return ( - Loading...}> - Loading...} - /> - + ); } diff --git a/sample/another-remote-module/src/dev/index.tsx b/sample/another-remote-module/src/dev/index.tsx index c5fc3269a..07718d871 100644 --- a/sample/another-remote-module/src/dev/index.tsx +++ b/sample/another-remote-module/src/dev/index.tsx @@ -1,9 +1,11 @@ +import { registerShell } from "@sample/shell"; import { ConsoleLogger, Runtime, RuntimeContext, registerLocalModules } from "@squide/react-router"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; -import { register } from "../register.tsx"; +import { register as registerModule } from "../register.tsx"; import { App } from "./App.tsx"; -import { sessionAccessor } from "./session.ts"; +import { registerDev } from "./register.tsx"; +import { onLogin, onLogout, sessionAccessor } from "./session.ts"; // Create the shell runtime. // Services, loggers and sessionAccessor could be reuse through a shared packages or faked when in isolation. @@ -14,7 +16,7 @@ const runtime = new Runtime({ // Registering the remote module as a static module because the "register" function // is local when developing in isolation. -registerLocalModules([register], runtime); +registerLocalModules([registerShell(onLogin, onLogout), registerDev, registerModule], runtime); const root = createRoot(document.getElementById("root")!); diff --git a/sample/another-remote-module/src/dev/register.tsx b/sample/another-remote-module/src/dev/register.tsx new file mode 100644 index 000000000..9d3fcbfe8 --- /dev/null +++ b/sample/another-remote-module/src/dev/register.tsx @@ -0,0 +1,9 @@ +import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; +import { DevHome } from "./DevHome.tsx"; + +export const registerDev: ModuleRegisterFunction = runtime => { + runtime.registerRoute({ + index: true, + element: + }); +}; diff --git a/sample/another-remote-module/src/dev/session.ts b/sample/another-remote-module/src/dev/session.ts index 6f20a918b..628209caf 100644 --- a/sample/another-remote-module/src/dev/session.ts +++ b/sample/another-remote-module/src/dev/session.ts @@ -7,3 +7,17 @@ export const sessionManager = new LocalStorageSessionManager(); export const sessionAccessor: SessionAccessorFunction = () => { return sessionManager.getSession(); }; + +export async function onLogin(username: string) { + const session: Session = { + user: { + name: username + } + }; + + sessionManager.setSession(session); +} + +export async function onLogout() { + sessionManager.clearSession(); +} diff --git a/sample/another-remote-module/src/register.tsx b/sample/another-remote-module/src/register.tsx index a173a0fa4..ae21a47a5 100644 --- a/sample/another-remote-module/src/register.tsx +++ b/sample/another-remote-module/src/register.tsx @@ -1,17 +1,13 @@ import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; export const register: ModuleRegisterFunction = runtime => { - runtime.registerRoutes([ - { - path: "/federated-tabs", - lazy: () => import("@sample/shared/FederatedTabsLayout.tsx") - } - ]); + runtime.registerRoute({ + path: "/federated-tabs", + lazy: () => import("@sample/shared/FederatedTabsLayout.tsx") + }); - runtime.registerNavigationItems([ - { - to: "/federated-tabs", - label: "Tabs" - } - ]); + runtime.registerNavigationItem({ + to: "/federated-tabs", + label: "Tabs" + }); }; diff --git a/sample/host/package.json b/sample/host/package.json index 785f60b36..62e0476d4 100644 --- a/sample/host/package.json +++ b/sample/host/package.json @@ -17,7 +17,6 @@ "devDependencies": { "@swc/core": "1.3.86", "@swc/helpers": "0.5.2", - "@tanstack/react-query-devtools": "rc", "@types/react": "18.2.22", "@types/react-dom": "18.2.7", "@types/webpack": "5.28.2", @@ -45,7 +44,6 @@ "@squide/react-router": "workspace:*", "@squide/msw": "workspace:*", "@squide/webpack-module-federation": "workspace:*", - "@tanstack/react-query": "rc", "axios": "1.5.1", "react": "18.2.0", "react-dom": "18.2.0", diff --git a/sample/host/src/App copy.tsx b/sample/host/src/App copy.tsx deleted file mode 100644 index eb33365f4..000000000 --- a/sample/host/src/App copy.tsx +++ /dev/null @@ -1,65 +0,0 @@ -// import { BackgroundColorContext, type Session } from "@sample/shared"; -// import { InvalidCredentialsError, useAppRouter } from "@sample/shell"; -// import { useAreModulesReady } from "@squide/webpack-module-federation"; -// import axios from "axios"; -// import { useCallback } from "react"; -// import { RouterProvider } from "react-router-dom"; -// import { sessionManager } from "./session.ts"; - -// export function App() { -// // Re-render the app once all the remotes are registered. -// // Otherwise, the remotes routes won't be added to the router. -// const areModulesReady = useAreModulesReady(); - -// const onLogin = useCallback(async (username: string, password: string) => { -// try { -// await axios.post("/login", { -// username, -// password -// }); - -// // TODO: Removed when the session mecanism is in place. -// const session: Session = { -// user: { -// name: username -// } -// }; - -// sessionManager.setSession(session); -// } catch (error: unknown) { -// if (axios.isAxiosError(error)) { -// if (error.response?.status === 401) { -// throw new InvalidCredentialsError(); -// } -// } - -// throw new Error("An unknown error happened while trying to login a user"); -// } -// }, []); - -// const onLogout = useCallback(async () => { -// sessionManager.clearSession(); -// }, []); - -// const router = useAppRouter(onLogin, onLogout, { -// managedRoutes: [ -// { -// index: true, -// lazy: () => import("./Home.tsx") -// } -// ] -// }); - -// // if (!areModulesReady) { -// // return
Loading...
; -// // } - -// return ( -// -// -// -// ); -// } diff --git a/sample/host/src/App.tsx b/sample/host/src/App.tsx index 66b2786ff..f8cf5953d 100644 --- a/sample/host/src/App.tsx +++ b/sample/host/src/App.tsx @@ -1,10 +1,10 @@ import { BackgroundColorContext, type Session } from "@sample/shared"; -import { InvalidCredentialsError, useAppRouter } from "@sample/shell"; +import { useAppRouter } from "@sample/shell"; import { useIsMswStarted } from "@squide/msw"; import { useRuntime, type Runtime } from "@squide/react-router"; import { useAreModulesReady } from "@squide/webpack-module-federation"; import axios from "axios"; -import { useCallback, useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { RouterProvider, matchRoutes } from "react-router-dom"; import { sessionManager } from "./session.ts"; @@ -21,86 +21,49 @@ export function App() { // Otherwise, the API calls will return a 404 status. const isMswStarted = useIsMswStarted(process.env.USE_MSW as unknown as boolean); - const onLogin = useCallback(async (username: string, password: string) => { - try { - await axios.post("/login", { - username, - password - }); - } catch (error: unknown) { - if (axios.isAxiosError(error)) { - if (error.response?.status === 401) { - throw new InvalidCredentialsError(); - } - } - - throw new Error("An unknown error happened while trying to login a user"); - } - }, []); - - const onLogout = useCallback(async () => { - sessionManager.clearSession(); - }, []); - - const router = useAppRouter(onLogin, onLogout, { - managedRoutes: [ - { - index: true, - lazy: () => import("./Home.tsx") - } - ] - }); - - /* - ISSUES: - - There's no match for the index route - - Doesn't work when we go from a public route to a private route - -> That's because there's no redirect to the login yet. - - What about the NoMatch route? - - Probably that in an unkown scenario that would be best to try to get the session? Otherwise we could render and end up with - unmanaged errors? - - Add a payment status endpoint to showcase having multiple global data - */ - useEffect(() => { if (areModulesReady && isMswStarted) { - // getActiveRouteVisibility + setIsReady(true); - // const location = useLocation(); - const location = window.location; + // // getActiveRouteVisibility - console.log("**** location: ", location); + // // const location = useLocation(); + // const location = window.location; - const matchingRoutes = matchRoutes(runtime.routes, location) ?? []; + // console.log("**** location: ", location); - console.log("**** matchingRoutes:", matchingRoutes); + // const matchingRoutes = matchRoutes(runtime.routes, location) ?? []; - if (matchingRoutes.length > 0) { - // When a route is nested, it also returns all the parts that constistuate the whole route (for example the layouts). - // We only want to know the visiblity of the deepest root route. - const rootRoute = matchingRoutes.findLast(x => x.route.type === "root"); + // console.log("**** matchingRoutes:", matchingRoutes); - if (rootRoute!.route.visibility === "authenticated") { - axios.get("/session").then(({ data }) => { - const session: Session = { - user: { - name: data.username - } - }; + // if (matchingRoutes.length > 0) { + // // When a route is nested, it also returns all the parts that constistuate the whole route (for example the layouts). + // // We only want to know the visiblity of the deepest root route. + // const rootRoute = matchingRoutes.findLast(x => x.route.type === "root"); - sessionManager.setSession(session); + // if (rootRoute!.route.visibility === "authenticated") { + // axios.get("/session").then(({ data }) => { + // const session: Session = { + // user: { + // name: data.username + // } + // }; - setIsReady(true); - }); - } else { - setIsReady(true); - } - } else { - throw new Error(`[shell] There's no matching route for the location: "${location}". Did you add routes to React Router without using the runtime.registerRoutes() function?`); - } + // sessionManager.setSession(session); + + // setIsReady(true); + // }); + // } else { + // setIsReady(true); + // } + // } else { + // throw new Error(`[shell] There's no matching route for the location: "${location}". Did you add routes to React Router without using the runtime.registerRoute() function?`); + // } } }, [areModulesReady, isMswStarted, runtime.routes]); + const router = useAppRouter(); + if (!isReady) { return
Loading...
; } diff --git a/sample/host/src/bootstrap.tsx b/sample/host/src/bootstrap.tsx index 6580baf40..b017e76c8 100644 --- a/sample/host/src/bootstrap.tsx +++ b/sample/host/src/bootstrap.tsx @@ -1,14 +1,14 @@ -import { register as registerLocalModule } from "@sample/local-module"; +import { registerLocalModule } from "@sample/local-module"; import { isNetlify, type AppContext } from "@sample/shared"; +import { registerShell } from "@sample/shell"; import { MswPlugin } from "@squide/msw"; import { ConsoleLogger, Runtime, RuntimeContext, registerLocalModules } from "@squide/react-router"; import { registerRemoteModules, type RemoteDefinition } from "@squide/webpack-module-federation"; -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import { App } from "./App.tsx"; -import { sessionAccessor } from "./session.ts"; +import { registerHost } from "./register.tsx"; +import { onLogin, onLogout, sessionAccessor } from "./session.ts"; const Remotes: RemoteDefinition[] = [ { @@ -31,19 +31,11 @@ const runtime = new Runtime({ sessionAccessor }); -// Creates a React Query client. -// This client will be shared between the host application and the remote modules. -// It's primary purpose is to keep fresh data in cache but also to serve as a global -// state management solution for the federated application. It works for most global state -// values because with React Query the global state values are synched with the server -// data, which is our single source of truth. -const queryClient = new QueryClient(); - const context: AppContext = { - name: "Test app" + name: "Sample app" }; -registerLocalModules([registerLocalModule], runtime, { context }); +registerLocalModules([registerShell(onLogin, onLogout), registerHost, registerLocalModule], runtime, { context }); registerRemoteModules(Remotes, runtime, { context }).then(() => { if (process.env.USE_MSW) { @@ -63,10 +55,7 @@ const root = createRoot(document.getElementById("root")!); root.render( - - - - + ); diff --git a/sample/host/src/register.tsx b/sample/host/src/register.tsx new file mode 100644 index 000000000..a1cdf61c9 --- /dev/null +++ b/sample/host/src/register.tsx @@ -0,0 +1,8 @@ +import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; + +export const registerHost: ModuleRegisterFunction = runtime => { + runtime.registerRoute({ + index: true, + lazy: () => import("./Home.tsx") + }); +}; diff --git a/sample/host/src/session.ts b/sample/host/src/session.ts index e2ca4125b..e7c09ab6a 100644 --- a/sample/host/src/session.ts +++ b/sample/host/src/session.ts @@ -1,40 +1,7 @@ import type { Session } from "@sample/shared"; -// import type { Session } from "@sample/shared"; -// import { LocalStorageSessionManager } from "@squide/fakes"; +import { InvalidCredentialsError } from "@sample/shell"; import type { SessionAccessorFunction } from "@squide/react-router"; -// import type { QueryClient } from "@tanstack/react-query"; -// import axios from "axios"; - -// export const sessionManager = new LocalStorageSessionManager() as SessionManager; - -// export const SessionQueryKey = ["/session"]; - -// export const sessionQuery = { -// queryKey: SessionQueryKey, -// queryFn: async () => { -// const { data } = await axios.get("/session"); - -// return data; -// } -// }; - -// export class SessionManager { -// readonly #queryClient: QueryClient; - -// constructor(queryClient: QueryClient) { -// this.#queryClient = queryClient; -// } - -// getSession() { -// this.#queryClient.getQueryData(SessionQueryKey); -// } -// } - -// export async function fetchSession() { -// const { data } = await axios.get("/session"); - -// return data; -// } +import axios from "axios"; export class SessionManager { #session?: Session; @@ -44,7 +11,13 @@ export class SessionManager { } getSession() { - return this.#session; + // return this.#session; + + return { + user: { + name: "John Doe" + } + }; } clearSession() { @@ -57,3 +30,24 @@ export const sessionManager = new SessionManager(); export const sessionAccessor: SessionAccessorFunction = () => { return sessionManager.getSession(); }; + +export async function onLogin(username: string, password: string) { + try { + await axios.post("/login", { + username, + password + }); + } catch (error: unknown) { + if (axios.isAxiosError(error)) { + if (error.response?.status === 401) { + throw new InvalidCredentialsError(); + } + } + + throw new Error("An unknown error happened while trying to login a user"); + } +} + +export async function onLogout() { + sessionManager.clearSession(); +} diff --git a/sample/local-module/src/dev/App.tsx b/sample/local-module/src/dev/App.tsx index 989ef0d61..4ec07d97d 100644 --- a/sample/local-module/src/dev/App.tsx +++ b/sample/local-module/src/dev/App.tsx @@ -1,40 +1,13 @@ -import type { Session } from "@sample/shared"; import { useAppRouter } from "@sample/shell"; -import { Suspense, useCallback } from "react"; import { RouterProvider } from "react-router-dom"; -import { DevHome } from "./DevHome.tsx"; -import { sessionManager } from "./session.ts"; export function App() { - const onLogin = useCallback(async (username: string) => { - const session: Session = { - user: { - name: username - } - }; - - sessionManager.setSession(session); - }, []); - - const onLogout = useCallback(async () => { - sessionManager.clearSession(); - }, []); - - const router = useAppRouter(onLogin, onLogout, { - managedRoutes: [ - { - index: true, - element: - } - ] - }); + const router = useAppRouter(); return ( - Loading...}> - Loading...} - /> - + ); } diff --git a/sample/local-module/src/dev/index.tsx b/sample/local-module/src/dev/index.tsx index 046ff94fa..900298917 100644 --- a/sample/local-module/src/dev/index.tsx +++ b/sample/local-module/src/dev/index.tsx @@ -1,10 +1,11 @@ +import { registerShell } from "@sample/shell"; import { ConsoleLogger, Runtime, RuntimeContext, registerLocalModules } from "@squide/react-router"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; -import { register } from "../register.tsx"; +import { registerLocalModule } from "../register.tsx"; import { App } from "./App.tsx"; -import { registerTabsPage } from "./registerTabsPage.tsx"; -import { sessionAccessor } from "./session.ts"; +import { registerDev } from "./register.tsx"; +import { onLogin, onLogout, sessionAccessor } from "./session.ts"; // Create the shell runtime. // Services, loggers and sessionAccessor could be reuse through a shared packages or faked when in isolation. @@ -13,9 +14,7 @@ const runtime = new Runtime({ sessionAccessor }); -// Registering the remote module as a static module because the "register" function -// is local when developing in isolation. -registerLocalModules([register, registerTabsPage], runtime); +registerLocalModules([registerShell(onLogin, onLogout), registerDev, registerLocalModule], runtime); const root = createRoot(document.getElementById("root")!); diff --git a/sample/local-module/src/dev/register.tsx b/sample/local-module/src/dev/register.tsx new file mode 100644 index 000000000..d16f01c45 --- /dev/null +++ b/sample/local-module/src/dev/register.tsx @@ -0,0 +1,19 @@ +import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; +import { DevHome } from "./DevHome.tsx"; + +export const registerDev: ModuleRegisterFunction = runtime => { + runtime.registerRoute({ + index: true, + element: + }); + + runtime.registerRoute({ + path: "/federated-tabs", + lazy: () => import("@sample/shared/FederatedTabsLayout.tsx") + }); + + runtime.registerNavigationItem({ + to: "/federated-tabs", + label: "Tabs" + }); +}; diff --git a/sample/local-module/src/dev/registerTabsPage.tsx b/sample/local-module/src/dev/registerTabsPage.tsx deleted file mode 100644 index a66667cf7..000000000 --- a/sample/local-module/src/dev/registerTabsPage.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; - -export const registerTabsPage: ModuleRegisterFunction = runtime => { - runtime.registerRoutes([ - { - path: "/federated-tabs", - lazy: () => import("@sample/shared/FederatedTabsLayout.tsx") - } - ]); - - runtime.registerNavigationItems([ - { - to: "/federated-tabs", - label: "Tabs" - } - ]); -}; diff --git a/sample/local-module/src/dev/session.ts b/sample/local-module/src/dev/session.ts index 6f20a918b..628209caf 100644 --- a/sample/local-module/src/dev/session.ts +++ b/sample/local-module/src/dev/session.ts @@ -7,3 +7,17 @@ export const sessionManager = new LocalStorageSessionManager(); export const sessionAccessor: SessionAccessorFunction = () => { return sessionManager.getSession(); }; + +export async function onLogin(username: string) { + const session: Session = { + user: { + name: username + } + }; + + sessionManager.setSession(session); +} + +export async function onLogout() { + sessionManager.clearSession(); +} diff --git a/sample/local-module/src/register.tsx b/sample/local-module/src/register.tsx index 41f22fe95..839f934b9 100644 --- a/sample/local-module/src/register.tsx +++ b/sample/local-module/src/register.tsx @@ -1,50 +1,48 @@ import type { AppContext } from "@sample/shared"; import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; -export const register: ModuleRegisterFunction = (runtime, context) => { +export const registerLocalModule: ModuleRegisterFunction = (runtime, context) => { console.log("Local module context: ", context); - runtime.registerRoutes([ - { - path: "/about", - lazy: () => import("./About.tsx") - }, - { - path: "/message", - lazy: () => import("./Message.tsx") - } - ]); - - runtime.registerNavigationItems([ - { - to: "/about", - label: "About" - }, - { - to: "/message", - label: "Message", - // Higher numbers gets rendered first. - priority: 999, - // Will be forwarded to the host application render function. - additionalProps: { - highlight: true - } + runtime.registerRoute({ + path: "/about", + lazy: () => import("./About.tsx") + }); + + runtime.registerRoute({ + path: "/message", + lazy: () => import("./Message.tsx") + }); + + runtime.registerNavigationItem({ + to: "/about", + label: "About" + }); + + runtime.registerNavigationItem({ + to: "/message", + label: "Message", + // Higher numbers gets rendered first. + priority: 999, + // Will be forwarded to the host application render function. + additionalProps: { + highlight: true } - ]); + }); /////// - runtime.registerRoutes([ - { - index: true, - lazy: () => import("./WorkleapTab.tsx") - } - ], { parentPath: "/federated-tabs" }); + runtime.registerRoute({ + index: true, + lazy: () => import("./WorkleapTab.tsx") + }, { + parentPath: "/federated-tabs" + }); - runtime.registerNavigationItems([ - { - to: "/federated-tabs", - label: "Workleap" - } - ], { menuId: "/federated-tabs" }); + runtime.registerNavigationItem({ + to: "/federated-tabs", + label: "Workleap" + }, { + menuId: "/federated-tabs" + }); }; diff --git a/sample/remote-module/src/dev/App.tsx b/sample/remote-module/src/dev/App.tsx index 139fc984c..decc98576 100644 --- a/sample/remote-module/src/dev/App.tsx +++ b/sample/remote-module/src/dev/App.tsx @@ -1,42 +1,17 @@ -import { BackgroundColorContext, type Session } from "@sample/shared"; +import { BackgroundColorContext } from "@sample/shared"; import { useAppRouter } from "@sample/shell"; -import { Suspense, useCallback } from "react"; import { RouterProvider } from "react-router-dom"; -import { DevHome } from "./DevHome.tsx"; -import { sessionManager } from "./session.ts"; export function App() { - const onLogin = useCallback(async (username: string) => { - const session: Session = { - user: { - name: username - } - }; - - sessionManager.setSession(session); - }, []); - - const onLogout = useCallback(async () => { - sessionManager.clearSession(); - }, []); - - const router = useAppRouter(onLogin, onLogout, { - managedRoutes: [ - { - index: true, - element: - } - ] - }); + const router = useAppRouter(); return ( - Loading...}> - Loading...} - /> - + ); } + diff --git a/sample/remote-module/src/dev/index.tsx b/sample/remote-module/src/dev/index.tsx index 4ad5ec39a..8d6a7a5fb 100644 --- a/sample/remote-module/src/dev/index.tsx +++ b/sample/remote-module/src/dev/index.tsx @@ -1,11 +1,12 @@ +import { registerShell } from "@sample/shell"; import { MswPlugin } from "@squide/msw"; import { ConsoleLogger, Runtime, RuntimeContext, registerLocalModules } from "@squide/react-router"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; -import { register } from "../register.tsx"; +import { register as registerModule } from "../register.tsx"; import { App } from "./App.tsx"; -import { registerTabsPage } from "./registerTabsPage.tsx"; -import { sessionAccessor } from "./session.ts"; +import { registerDev } from "./register.tsx"; +import { onLogin, onLogout, sessionAccessor } from "./session.ts"; const mswPlugin = new MswPlugin(); @@ -19,7 +20,7 @@ const runtime = new Runtime({ // Registering the remote module as a static module because the "register" function // is local when developing in isolation. -registerLocalModules([register, registerTabsPage], runtime); +registerLocalModules([registerShell(onLogin, onLogout), registerDev, registerModule], runtime); // Register MSW after the local modules has been registered since the request handlers // will be registered by the modules. diff --git a/sample/remote-module/src/dev/register.tsx b/sample/remote-module/src/dev/register.tsx new file mode 100644 index 000000000..d16f01c45 --- /dev/null +++ b/sample/remote-module/src/dev/register.tsx @@ -0,0 +1,19 @@ +import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; +import { DevHome } from "./DevHome.tsx"; + +export const registerDev: ModuleRegisterFunction = runtime => { + runtime.registerRoute({ + index: true, + element: + }); + + runtime.registerRoute({ + path: "/federated-tabs", + lazy: () => import("@sample/shared/FederatedTabsLayout.tsx") + }); + + runtime.registerNavigationItem({ + to: "/federated-tabs", + label: "Tabs" + }); +}; diff --git a/sample/remote-module/src/dev/registerTabsPage.tsx b/sample/remote-module/src/dev/registerTabsPage.tsx deleted file mode 100644 index a66667cf7..000000000 --- a/sample/remote-module/src/dev/registerTabsPage.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; - -export const registerTabsPage: ModuleRegisterFunction = runtime => { - runtime.registerRoutes([ - { - path: "/federated-tabs", - lazy: () => import("@sample/shared/FederatedTabsLayout.tsx") - } - ]); - - runtime.registerNavigationItems([ - { - to: "/federated-tabs", - label: "Tabs" - } - ]); -}; diff --git a/sample/remote-module/src/dev/session.ts b/sample/remote-module/src/dev/session.ts index 6f20a918b..628209caf 100644 --- a/sample/remote-module/src/dev/session.ts +++ b/sample/remote-module/src/dev/session.ts @@ -7,3 +7,17 @@ export const sessionManager = new LocalStorageSessionManager(); export const sessionAccessor: SessionAccessorFunction = () => { return sessionManager.getSession(); }; + +export async function onLogin(username: string) { + const session: Session = { + user: { + name: username + } + }; + + sessionManager.setSession(session); +} + +export async function onLogout() { + sessionManager.clearSession(); +} diff --git a/sample/remote-module/src/register.tsx b/sample/remote-module/src/register.tsx index a9a5e9c65..f820bfed2 100644 --- a/sample/remote-module/src/register.tsx +++ b/sample/remote-module/src/register.tsx @@ -4,115 +4,125 @@ import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { requestHandlers } from "../mocks/handlers.ts"; export const register: ModuleRegisterFunction = runtime => { - runtime.registerRoutes([ - { - path: "/remote", - lazy: async () => import("./Remote.tsx") - }, - { - path: "/fetch", - lazy: () => import("./Fetch.tsx"), - loader: async function loader() { - return fetch("https://rickandmortyapi.com/api/character/1,2,3,4,5", { - method: "GET", - headers: { - "Accept": "application/json" - } - }); - } - }, - { - hoist: true, - visibility: "public", - path: "/hoisted", - lazy: () => import("./CustomLayout.tsx"), - children: [ - { - index: true, - lazy: () => import("./Hoisted.tsx") + runtime.registerRoute({ + path: "/remote", + lazy: async () => import("./Remote.tsx") + }); + + runtime.registerRoute({ + path: "/fetch", + lazy: () => import("./Fetch.tsx"), + loader: async function loader() { + return fetch("https://rickandmortyapi.com/api/character/1,2,3,4,5", { + method: "GET", + headers: { + "Accept": "application/json" } - ] - }, - { - path: "/no-context-override", - lazy: () => import("./ColoredPage.tsx") - }, - { - path: "/context-override", - lazy: async () => { - const { ColoredPage } = await import("./ColoredPage.tsx"); - - return { - element: ( - - - - ) - }; - } + }); } - ]); - - runtime.registerNavigationItems([ - { - to: "/remote", - label: "Remote" - }, - { - to: "/fetch", - label: "Fetch" - }, - { - to: "/hoisted", - label: Hoisted - }, - { - label: "Section", - children: [ - { - to: "#", - label: "Child 1" - }, - { - to: "#", - label: "Child 2" - } - ] - }, - { - to: "/no-context-override", - label: "No context override" - }, - { - to: "/context-override", - label: "Context override" + }); + + runtime.registerRoute({ + hoist: true, + path: "/hoisted", + lazy: () => import("./CustomLayout.tsx"), + children: [ + { + index: true, + lazy: () => import("./Hoisted.tsx") + } + ] + }); + + runtime.registerRoute({ + path: "/no-context-override", + lazy: () => import("./ColoredPage.tsx") + }); + + runtime.registerRoute({ + path: "/context-override", + lazy: async () => { + const { ColoredPage } = await import("./ColoredPage.tsx"); + + return { + element: ( + + + + ) + }; } - ]); + }); + + runtime.registerNavigationItem({ + to: "/remote", + label: "Remote" + }); + + runtime.registerNavigationItem({ + to: "/fetch", + label: "Fetch" + }); + + runtime.registerNavigationItem({ + to: "/hoisted", + label: Hoisted + }); + + runtime.registerNavigationItem({ + label: "Section", + children: [ + { + to: "#", + label: "Child 1" + }, + { + to: "#", + label: "Child 2" + } + ] + }); + + runtime.registerNavigationItem({ + to: "/no-context-override", + label: "No context override" + }); + + runtime.registerNavigationItem({ + to: "/context-override", + label: "Context override" + }); // Register federated tabs. - runtime.registerRoutes([ - { - path: "/federated-tabs/officevibe", - lazy: () => import("./OfficevibeTab.tsx") - }, - { - path: "/federated-tabs/skills", - lazy: () => import("./SkillsTab.tsx") - } - ], { parentPath: "/federated-tabs" }); - - runtime.registerNavigationItems([ - { - to: "/federated-tabs/officevibe", - label: "Officevibe" - }, - { - to: "/federated-tabs/skills", - label: "Skills", - priority: 999 - } - ], { menuId: "/federated-tabs" }); + runtime.registerRoute({ + path: "/federated-tabs/officevibe", + lazy: () => import("./OfficevibeTab.tsx") + }, { + parentPath: "/federated-tabs" + }); + + runtime.registerRoute({ + path: "/federated-tabs/skills", + lazy: () => import("./SkillsTab.tsx") + }, { + parentPath: "/federated-tabs" + }); + + runtime.registerNavigationItem({ + to: "/federated-tabs/officevibe", + label: "Officevibe" + }, { + menuId: "/federated-tabs" + }); + + runtime.registerNavigationItem({ + to: "/federated-tabs/skills", + label: "Skills", + priority: 999 + }, { + menuId: "/federated-tabs" + }); // Register request handlers for MSW. diff --git a/sample/shared/src/appContext.ts b/sample/shared/src/appContext.ts index 5bec8965b..ea92aee21 100644 --- a/sample/shared/src/appContext.ts +++ b/sample/shared/src/appContext.ts @@ -1,3 +1,3 @@ export interface AppContext { - name: string; + name?: string; } diff --git a/sample/shared/src/session.ts b/sample/shared/src/session.ts index f00af3d2f..c3a3b6e50 100644 --- a/sample/shared/src/session.ts +++ b/sample/shared/src/session.ts @@ -3,9 +3,3 @@ export interface Session { name: string; }; } - -// export interface SessionManager { -// setSession: (session: Session) => void; -// getSession: () => Session | undefined; -// clearSession: () => void; -// } diff --git a/sample/shell/src/Login.tsx b/sample/shell/src/Login.tsx index df919a8cd..73f482776 100644 --- a/sample/shell/src/Login.tsx +++ b/sample/shell/src/Login.tsx @@ -2,6 +2,8 @@ import { useIsAuthenticated } from "@squide/react-router"; import { useCallback, useState, type ChangeEvent, type MouseEvent } from "react"; import { Navigate, useNavigate } from "react-router-dom"; +export type OnLoginHandler = (username: string, password: string) => Promise; + export class InvalidCredentialsError extends Error { constructor(message: string = "") { super(message); @@ -9,10 +11,8 @@ export class InvalidCredentialsError extends Error { } } -export type OnLoginHandler = (username: string, password: string) => Promise; - export interface LoginProps { - onLogin: OnLoginHandler; + onLogin?: OnLoginHandler; } export function Login({ onLogin }: LoginProps) { @@ -30,7 +30,9 @@ export function Login({ onLogin }: LoginProps) { setIsBusy(true); setErrorMessage(undefined); - await onLogin(username, password); + if (onLogin) { + await onLogin(username, password); + } navigate("/"); } catch (error: unknown) { diff --git a/sample/shell/src/Logout.tsx b/sample/shell/src/Logout.tsx index 777788c96..18c01c9cf 100644 --- a/sample/shell/src/Logout.tsx +++ b/sample/shell/src/Logout.tsx @@ -3,11 +3,13 @@ import { Link } from "react-router-dom"; export type onLogoutHandler = () => Promise; export interface LogoutProps { - onLogout: onLogoutHandler; + onLogout?: onLogoutHandler; } export function Logout({ onLogout }: LogoutProps) { - onLogout(); + if (onLogout) { + onLogout(); + } return (
diff --git a/sample/shell/src/index.ts b/sample/shell/src/index.ts index f6cf0a585..3af16ad19 100644 --- a/sample/shell/src/index.ts +++ b/sample/shell/src/index.ts @@ -6,5 +6,6 @@ export * from "./ModuleErrorBoundary.tsx"; export * from "./NoMatch.tsx"; export * from "./RootErrorBoundary.tsx"; export * from "./RootLayout.tsx"; +export * from "./register.tsx"; export * from "./useAppRouter.tsx"; diff --git a/sample/shell/src/register.tsx b/sample/shell/src/register.tsx new file mode 100644 index 000000000..0b3ae27ec --- /dev/null +++ b/sample/shell/src/register.tsx @@ -0,0 +1,95 @@ +import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; +import { ManagedRoutesOutlet } from "@squide/react-router"; +import type { OnLoginHandler } from "./Login.tsx"; +import type { onLogoutHandler } from "./Logout.tsx"; +import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; +import { RootLayout } from "./RootLayout.tsx"; + +export function registerShell(onLogin: OnLoginHandler, onLogout: onLogoutHandler) { + const register: ModuleRegisterFunction = runtime => { + runtime.registerRoute({ + // Pathless route to declare a root layout and a root error boundary. + hoist: true, + element: , + children: [ + { + // Public pages like the login and logout pages will be rendered under this pathless route. + name: "root-error-boundary", + errorElement: , + children: [ + { + // Pathless route to declare an authenticated layout. + lazy: async () => { + const { AuthenticatedLayout } = await import("./AuthenticatedLayout.tsx"); + + return { + element: + }; + }, + children: [ + { + // Pathless route to declare an error boundary inside the layout instead of outside. + // It's quite useful to prevent losing the layout when an unmanaged error occurs. + lazy: async () => { + const { ModuleErrorBoundary } = await import("./ModuleErrorBoundary.tsx"); + + return { + errorElement: + }; + }, + children: [ + ManagedRoutesOutlet + ] + } + ] + } + ] + } + ] + }); + + runtime.registerRoute({ + visibility: "public", + path: "/login", + lazy: async () => { + const { Login } = await import("./Login.tsx"); + + return { + element: + }; + } + }, { + parentName: "root-error-boundary" + }); + + runtime.registerRoute({ + visibility: "public", + path: "/logout", + lazy: async () => { + const { Logout } = await import("./Logout.tsx"); + + return { + element: + }; + } + }, { + parentName: "root-error-boundary" + }); + + runtime.registerRoute({ + visibility: "public", + path: "*", + lazy: async () => { + const { NoMatch } = await import("./NoMatch.tsx"); + + return { + element: + }; + } + }, { + parentName: "root-error-boundary" + }); + }; + + return register; +} diff --git a/sample/shell/src/useAppRouter.tsx b/sample/shell/src/useAppRouter.tsx index de7854f1f..7c82da3b0 100644 --- a/sample/shell/src/useAppRouter.tsx +++ b/sample/shell/src/useAppRouter.tsx @@ -1,179 +1,17 @@ -import { useHoistedRoutes, useRoutes, useRuntime, type Route } from "@squide/react-router"; -import { useCallback, useMemo, useState } from "react"; -import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; -import { RootLayout } from "./RootLayout.tsx"; +import { useRoutes } from "@squide/react-router"; +import { useMemo } from "react"; +import { createBrowserRouter } from "react-router-dom"; // Importing the Router type to prevent: error TS2742: The inferred type of 'useAppRouter' cannot be named without a reference import type { Router } from "@remix-run/router"; -import { createBrowserRouter } from "react-router-dom"; -import type { OnLoginHandler } from "./Login.tsx"; -import type { onLogoutHandler } from "./Logout.tsx"; - -export interface UseAppRouterOptions { - managedRoutes?: Route[]; - rootRoutes?: Route[]; -} -export function useAppRouter(onLogin: OnLoginHandler, onLogout: onLogoutHandler, options: UseAppRouterOptions = {}): Router { - const { - managedRoutes: hostManagedRoutes = [], - rootRoutes = [] - } = options; - - // Reuse the same array reference through re-renders. - const [memoizedManagedRoutes] = useState(hostManagedRoutes); - const [memoizedRootRoutes] = useState(rootRoutes); - - const runtime = useRuntime(); +export function useAppRouter(): Router { const routes = useRoutes(); - // All routes must be registered in order to retrieve the active route - // visibility property. - runtime.registerRoutes([ - { - // Pathless route to declare a root layout and a root error boundary. - hoist: true, - public: true, - element: , - children: [ - { - errorElement: , - children: [ - { - path: "/login", - lazy: async () => { - const { Login } = await import("./Login.tsx"); - - return { - element: - }; - } - }, - { - path: "/logout", - lazy: async () => { - const { Logout } = await import("./Logout.tsx"); - - return { - element: - }; - } - }, - { - path: "*", - lazy: async () => { - const { NoMatch } = await import("./NoMatch.tsx"); - - return { - element: - }; - } - } - ] - } - ] - } - ]); - - const wrapManagedRoutes = useCallback((managedRoutes: Route[]) => { - return { - // Pathless route to declare a root layout and a root error boundary. - element: , - children: [ - { - errorElement: , - children: [ - // { - // path: "/login", - // lazy: async () => { - // const { Login } = await import("./Login.tsx"); - - // return { - // element: - // }; - // } - // }, - // { - // path: "/logout", - // lazy: async () => { - // const { Logout } = await import("./Logout.tsx"); - - // return { - // element: - // }; - // } - // }, - { - // Pathless route to declare an authenticated layout. - lazy: async () => { - const { AuthenticatedLayout } = await import("./AuthenticatedLayout.tsx"); - - return { - element: - }; - }, - children: [ - { - // Pathless route to declare an error boundary inside the layout instead of outside. - // It's quite useful to prevent losing the layout when an unmanaged error occurs. - lazy: async () => { - const { ModuleErrorBoundary } = await import("./ModuleErrorBoundary.tsx"); - - return { - element: - }; - }, - children: [ - ...memoizedManagedRoutes, - ...managedRoutes - ] - } - ] - } - // { - // // Pathless route to declare an authenticated boundary. - // lazy: () => import("./AuthenticationBoundary.tsx"), - // children: [ - // { - // // Pathless route to declare an authenticated layout. - // lazy: () => import("./AuthenticatedLayout.tsx"), - // children: [ - // { - // // Pathless route to declare an error boundary inside the layout instead of outside. - // // It's quite useful to prevent losing the layout when an unmanaged error occurs. - // lazy: () => import("./ModuleErrorBoundary.tsx"), - // children: [ - // ...memoizedManagedRoutes, - // ...managedRoutes - // ] - // } - // ] - // } - // ] - // }, - // { - // path: "*", - // lazy: async () => { - // const { NoMatch } = await import("./NoMatch.tsx"); - - // return { - // element: - // }; - // } - // } - ] - } - ] - }; - }, [memoizedManagedRoutes]); - // }, [memoizedManagedRoutes, onLogin, onLogout]); - - // Using the useHoistedRoutes hook allow routes hoisted by modules to be rendered at the root of the router instead of under the root layout. - // To disallow the hoisting functionality, remove this hook and add the routes directly. - const hoistedRoutes = useHoistedRoutes(routes, wrapManagedRoutes); + console.log(routes); const router = useMemo(() => { - return createBrowserRouter([...hoistedRoutes, ...memoizedRootRoutes]); - }, [hoistedRoutes, memoizedRootRoutes]); + return createBrowserRouter(routes); + }, [routes]); return router; } From 8f6493419a06ff51725f88e8792ccd188e11140b Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Mon, 9 Oct 2023 19:37:27 -0400 Subject: [PATCH 05/51] Fixed the tests --- packages/react-router/src/routeRegistry.ts | 43 +- packages/react-router/src/runtime.ts | 44 +- .../react-router/tests/routeRegistry.test.tsx | 182 +++++ packages/react-router/tests/runtime.test.tsx | 681 ++++++++++++------ sample/host/src/App.tsx | 54 +- sample/shell/src/register.tsx | 4 +- sample/shell/src/useAppRouter.tsx | 2 - 7 files changed, 716 insertions(+), 294 deletions(-) create mode 100644 packages/react-router/tests/routeRegistry.test.tsx diff --git a/packages/react-router/src/routeRegistry.ts b/packages/react-router/src/routeRegistry.ts index 7832f8128..e83cb162a 100644 --- a/packages/react-router/src/routeRegistry.ts +++ b/packages/react-router/src/routeRegistry.ts @@ -22,7 +22,6 @@ export type RootRoute = Route & { type?: RouteType; }; - function normalizePath(routePath?: string) { if (routePath && routePath !== "/" && routePath.endsWith("/")) { return routePath.substring(0, routePath.length - 1); @@ -47,15 +46,9 @@ export type RouteRegistrationStatus = "pending" | "registered"; export interface AddRouteReturnType { registrationStatus: RouteRegistrationStatus; - completedPendingRegistrations?: Route[]; + completedPendingRegistrations: Route[]; } -const ManagedRoutesOutletName = "__squide-managed-routes-outlet__"; - -export const ManagedRoutesOutlet: Route = { - name: ManagedRoutesOutletName -}; - export class RouteRegistry { #routes: RootRoute[]; @@ -71,40 +64,15 @@ export class RouteRegistry { this.#routes = []; } - #validateRootRoutes(route: RootRoute, { parentPath, parentName }: RegisterRouteOptions = {}) { - if (route.hoist && parentPath) { - throw new Error(`[squide] A route cannot have the "hoist" property when a "publicPath" option is provided. Route id: "${route.path ?? route.name ?? "(no identifier)"}".`); - } - - if (route.hoist && parentName) { - throw new Error(`[squide] A route cannot have the "hoist" property when a "parentName" option is provided. Route id: "${route.path ?? route.name ?? "(no identifier)"}".`); - } - } - - add(route: RootRoute, options: RegisterRouteOptions = {}) { - const { - parentPath, - parentName - } = options; - - this.#validateRootRoutes(route, options); - + add(route: RootRoute, { parentPath, parentName }: RegisterRouteOptions = {}) { if (parentPath) { // The normalized path cannot be undefined because it's been provided by the consumer // (e.g. it cannot be a pathless route). return this.#addNestedRoutes([route], normalizePath(parentPath)!); } - let _parentName = parentName; - - if (!route.hoist && !_parentName) { - _parentName = ManagedRoutesOutletName; - } - - if (_parentName) { - console.log("******* Adding nested route:", route, " for parent name: ", _parentName, " indexes: ", this.#routesIndex); - - return this.#addNestedRoutes([route], _parentName); + if (parentName) { + return this.#addNestedRoutes([route], parentName); } return this.#addRootRoutes([route]); @@ -196,7 +164,8 @@ export class RouteRegistry { } return { - registrationStatus: "pending" + registrationStatus: "pending", + completedPendingRegistrations: [] }; } diff --git a/packages/react-router/src/runtime.ts b/packages/react-router/src/runtime.ts index 87b370daa..eb17b6b81 100644 --- a/packages/react-router/src/runtime.ts +++ b/packages/react-router/src/runtime.ts @@ -1,13 +1,46 @@ import { AbstractRuntime, RootMenuId, type RegisterNavigationItemOptions, type RegisterRouteOptions } from "@squide/core"; import { NavigationItemRegistry, type RootNavigationItem } from "./navigationItemRegistry.ts"; -import { ManagedRoutesOutlet, RouteRegistry, type RootRoute, type Route } from "./routeRegistry.ts"; +import { RouteRegistry, type RootRoute, type Route } from "./routeRegistry.ts"; + +const ManagedRoutesOutletName = "__squide-managed-routes-outlet__"; + +export const ManagedRoutes: Route = { + name: ManagedRoutesOutletName +}; + +export function isManagedRoutesOutletRoute(route: Route) { + return route.name === ManagedRoutesOutletName; +} export class Runtime extends AbstractRuntime { readonly #routeRegistry = new RouteRegistry(); readonly #navigationItemRegistry = new NavigationItemRegistry(); + #validateRootRoutes(route: RootRoute, { parentPath, parentName }: RegisterRouteOptions = {}) { + if (route.hoist && parentPath) { + throw new Error(`[squide] A route cannot have the "hoist" property when a "publicPath" option is provided. Route id: "${route.path ?? route.name ?? "(no identifier)"}".`); + } + + if (route.hoist && parentName) { + throw new Error(`[squide] A route cannot have the "hoist" property when a "parentName" option is provided. Route id: "${route.path ?? route.name ?? "(no identifier)"}".`); + } + } + registerRoute(route: RootRoute, options: RegisterRouteOptions = {}) { - const result = this.#routeRegistry.add(route, options); + this.#validateRootRoutes(route, options); + + let parentName = options.parentName; + + // By default, a route that is not hoisted nor nested under a known + // parent will be rendered under the ManagedRoutes outlet. + if (!route.hoist && !parentName && !isManagedRoutesOutletRoute(route)) { + parentName = ManagedRoutesOutletName; + } + + const result = this.#routeRegistry.add(route, { + ...options, + parentName + }); if (result.registrationStatus === "registered") { const parentId = options.parentPath ?? options.parentName; @@ -19,7 +52,7 @@ export class Runtime extends AbstractRuntime 0) { this._logger.debug( `[squide] The pending registration of the following route${result.completedPendingRegistrations.length !== 1 ? "s" : ""} has been %ccompleted%c.`, "color: white; background-color: #26bfa5;", "%s", "Newly registered routes:", result.completedPendingRegistrations, @@ -61,8 +94,9 @@ export class Runtime extends AbstractRuntime 0) { - if (pendingRegistrations.has(ManagedRoutesOutlet.name!)) { - throw new Error("[squide] The \"ManagedRoutesOutlet\" route is missing from the router configuration. The \"ManagedRoutesOutlet\" route must be added as children of an hoisted route. Did you forget to hoist the parent route that includes the \"ManagedRoutesOutlet\" route?"); + if (pendingRegistrations.has(ManagedRoutes.name!)) { + // eslint-disable-next-line max-len + throw new Error("[squide] The \"ManagedRoutes\" outlet route is missing from the router configuration. The \"ManagedRoutes\" outlet route must be added as a children of an hoisted route. Did you forget to include the \"ManagedRoutes\" outlet route or hoist the parent route that includes the \"ManagedRoutes\" outlet route?"); } let message = `[squide] ${pendingRegistrations.size} parent route${pendingRegistrations.size !== 1 ? "s" : ""} were expected to be registered but ${pendingRegistrations.size !== 1 ? "are" : "is"} missing:\r\n\r\n`; diff --git a/packages/react-router/tests/routeRegistry.test.tsx b/packages/react-router/tests/routeRegistry.test.tsx new file mode 100644 index 000000000..1293997ea --- /dev/null +++ b/packages/react-router/tests/routeRegistry.test.tsx @@ -0,0 +1,182 @@ +import { RouteRegistry, createIndexKey } from "../src/routeRegistry.ts"; + +describe("createIndexKey", () => { + test("when the route is an index route, return undefined", () => { + const result = createIndexKey({ + index: true, + element:
Hello!
+ }); + + expect(result).toBeUndefined(); + }); + + test("when the route has a path, return the route path", () => { + const result1 = createIndexKey({ + path: "/nested", + element:
Hello!
+ }); + + expect(result1).toBe("/nested"); + + const result2 = createIndexKey({ + path: "/parent/nested", + element:
Hello!
+ }); + + expect(result2).toBe("/parent/nested"); + }); + + test("when the route has a path and the path ends with a separator, strip the separator", () => { + const result = createIndexKey({ + path: "/parent/nested/", + element:
Hello!
+ }); + + expect(result).toBe("/parent/nested"); + }); + + test("when the route has a name, return the route name", () => { + const result = createIndexKey({ + name: "foo", + element:
Hello!
+ }); + + expect(result).toBe("foo"); + }); + + test("when this a pathless route, return undefined", () => { + const result = createIndexKey({ + element:
Hello!
+ }); + + expect(result).toBeUndefined(); + }); +}); + +describe("add", () => { + test("when a root route is added, return the \"registered\" registration status", () => { + const registry = new RouteRegistry(); + + const result = registry.add({ + path: "/root", + element:
Hello
+ }); + + expect(result.registrationStatus).toBe("registered"); + }); + + test("when a root route is added and complete the pending registration of nested routes, add the registered routes to the returned \"completedPendingRegistrations\" array", () => { + const registry = new RouteRegistry(); + + registry.add({ + path: "/root/another-level-1", + element:
Hello
+ }, { + parentPath: "/root" + }); + + registry.add({ + path: "/root/another-level-2", + element:
Hello
+ }, { + parentPath: "/root" + }); + + const result = registry.add({ + path: "/root", + element:
Hello
+ }); + + expect(result.completedPendingRegistrations![0].path).toBe("/root/another-level-1"); + expect(result.completedPendingRegistrations![1].path).toBe("/root/another-level-2"); + }); + + test("when a root route is added and do not complete any pending registration, return an empty \"completedPendingRegistrations\" array", () => { + const registry = new RouteRegistry(); + + registry.add({ + path: "/root/another-level-1", + element:
Hello
+ }, { + parentPath: "/root" + }); + + registry.add({ + path: "/root/another-level-2", + element:
Hello
+ }, { + parentPath: "/root" + }); + + const result = registry.add({ + path: "/toto", + element:
Hello
+ }); + + expect(result.completedPendingRegistrations!.length).toBe(0); + }); + + test("when a nested route is pending for registration, return the \"pending\" registration status", () => { + const registry = new RouteRegistry(); + + const result = registry.add({ + path: "/root/another-level", + element:
Hello
+ }, { + parentPath: "/root" + }); + + expect(result.registrationStatus).toBe("pending"); + }); + + test("when a nested route is registred, return the \"registered\" registration status", () => { + const registry = new RouteRegistry(); + + registry.add({ + path: "/root", + element:
Hello
+ }); + + const result = registry.add({ + path: "/root/another-level", + element:
Hello
+ }, { + parentPath: "/root" + }); + + expect(result.registrationStatus).toBe("registered"); + }); + + test("when a nested route is added and complete the pending registration of nested routes, add the registered routes to the to the returned \"completedPendingRegistrations\" array", () => { + const registry = new RouteRegistry(); + + registry.add({ + path: "/root/another-level-1", + element:
Hello
+ }, { + parentPath: "/root/another-level/yet-another-level" + }); + + registry.add({ + path: "/root/another-level-2", + element:
Hello
+ }, { + parentPath: "/root/another-level/yet-another-level" + }); + + registry.add({ + path: "/root", + element:
Hello
+ }); + + const result = registry.add({ + path: "/root/another-level/yet-another-level", + element:
Hello
+ }, { + parentPath: "/root" + }); + + expect(result.completedPendingRegistrations![0].path).toBe("/root/another-level-1"); + expect(result.completedPendingRegistrations![1].path).toBe("/root/another-level-2"); + }); +}); diff --git a/packages/react-router/tests/runtime.test.tsx b/packages/react-router/tests/runtime.test.tsx index d7ef2b2b2..d7de6a835 100644 --- a/packages/react-router/tests/runtime.test.tsx +++ b/packages/react-router/tests/runtime.test.tsx @@ -1,219 +1,402 @@ -import { createIndexKey } from "../src/routeRegistry.ts"; -import { Runtime } from "../src/runtime.ts"; +import type { RootRoute, Route } from "../src/routeRegistry.ts"; +import { ManagedRoutes, Runtime, isManagedRoutesOutletRoute } from "../src/runtime.ts"; -describe("createIndexKey", () => { - test("when the route is an index route, return undefined", () => { - const result = createIndexKey({ - index: true, - element:
Hello!
+describe("registerRoute", () => { + describe("managed routes", () => { + function registerManagedRoutesOutlet(runtime: Runtime) { + runtime.registerRoute(ManagedRoutes); + } + + function getManagedRoutes(routes: Route[]): (Route | RootRoute)[] | undefined { + for (const route of routes) { + if (isManagedRoutesOutletRoute(route)) { + return route.children as Route[]; + } + + if (route.children) { + const managedRoutes = getManagedRoutes(route.children); + + if (managedRoutes) { + return managedRoutes as Route[]; + } + } + } + } + + test("when the outlet is not registered, route registrations are pending", () => { + const runtime = new Runtime(); + + runtime.registerRoute({ + path: "/foo", + element:
Hello!
+ }); + + expect(runtime.routes.length).toBe(0); }); - expect(result).toBeUndefined(); - }); + test("when the outlet is registered, pending route registrations are completed", () => { + const runtime = new Runtime(); + + runtime.registerRoute({ + path: "/foo", + element:
Hello!
+ }); + + expect(runtime.routes.length).toBe(0); - test("when the route has a path, return the route path", () => { - const result1 = createIndexKey({ - path: "/nested", - element:
Hello!
+ registerManagedRoutesOutlet(runtime); + + expect(runtime.routes.length).toBe(1); + + const routes = getManagedRoutes(runtime.routes)!; + + expect(routes.length).toBe(1); + expect(routes[0].path).toBe("/foo"); }); - expect(result1).toBe("/nested"); + test("can register an index route", () => { + const runtime = new Runtime(); + + registerManagedRoutesOutlet(runtime); - const result2 = createIndexKey({ - path: "/parent/nested", - element:
Hello!
+ runtime.registerRoute({ + index: true, + element:
Hello!
+ }); + + const routes = getManagedRoutes(runtime.routes)!; + + expect(routes.length).toBe(1); + expect(routes[0].index).toBeTruthy(); }); - expect(result2).toBe("/parent/nested"); - }); + test("can register a pathless route", () => { + const runtime = new Runtime(); - test("when the route has a path and the path ends with a separator, strip the separator", () => { - const result = createIndexKey({ - path: "/parent/nested/", - element:
Hello!
+ registerManagedRoutesOutlet(runtime); + + runtime.registerRoute({ + element:
Hello!
+ }); + + const routes = getManagedRoutes(runtime.routes)!; + + expect(routes.length).toBe(1); + expect(routes[0].index).toBeUndefined(); + expect(routes[0].path).toBeUndefined(); }); - expect(result).toBe("/parent/nested"); - }); + test("can register multiple pathless routes", () => { + const runtime = new Runtime(); + + registerManagedRoutesOutlet(runtime); + + runtime.registerRoute({ + element:
Hello!
+ }); + + runtime.registerRoute({ + element:
How
+ }); + + runtime.registerRoute({ + element:
Are
+ }); - test("when the route has a name, return the route name", () => { - const result = createIndexKey({ - name: "foo", - element:
Hello!
+ runtime.registerRoute({ + element:
You?
+ }); + + const routes = getManagedRoutes(runtime.routes)!; + + expect(routes.length).toBe(4); }); - expect(result).toBe("foo"); - }); + test("can register a deeply nested route with pathless parent routes", () => { + const runtime = new Runtime(); + + registerManagedRoutesOutlet(runtime); + + runtime.registerRoute({ + element:
Hello
, + children: [ + { + element:
You!
, + children: [ + { + path: "/deeply-nested-route", + element:
Hello from nested!
+ } + ] + } + ] + }); + + const routes = getManagedRoutes(runtime.routes)!; - test("when this a pathless route, return undefined", () => { - const result = createIndexKey({ - element:
Hello!
+ expect(routes.length).toBe(1); + expect(routes[0].children![0].children![0].path).toBe("/deeply-nested-route"); }); - expect(result).toBeUndefined(); - }); -}); + test("can register a deeply nested index route with pathless parent routes", () => { + const runtime = new Runtime(); -describe("registerRoute", () => { - test("can register an index route", () => { - const runtime = new Runtime(); + registerManagedRoutesOutlet(runtime); - runtime.registerRoute({ - index: true, - element:
Hello!
+ runtime.registerRoute({ + element:
Hello
, + children: [ + { + element:
You!
, + children: [ + { + index: true, + element:
Hello from nested!
+ } + ] + } + ] + }); + + const routes = getManagedRoutes(runtime.routes)!; + + expect(routes.length).toBe(1); + expect(routes[0].children![0].children![0].index).toBeTruthy(); }); - expect(runtime.routes.length).toBe(1); - expect(runtime.routes[0].index).toBeTruthy(); - }); + test("can register a route with a \"public\" visibility", () => { + const runtime = new Runtime(); - test("can register a pathless route", () => { - const runtime = new Runtime(); + registerManagedRoutesOutlet(runtime); - runtime.registerRoute({ - element:
Hello!
+ runtime.registerRoute({ + path: "/public", + element:
Hello!
, + visibility: "public" + }); + + const routes = getManagedRoutes(runtime.routes)!; + + expect(routes.length).toBe(1); + expect(routes[0].path).toBe("/public"); + expect((routes[0] as RootRoute).visibility).toBe("public"); }); - expect(runtime.routes.length).toBe(1); - expect(runtime.routes[0].index).toBeUndefined(); - expect(runtime.routes[0].path).toBeUndefined(); - }); + test("can register a route with a \"authenticated\" visibility", () => { + const runtime = new Runtime(); - test("can register multiple pathless routes", () => { - const runtime = new Runtime(); + registerManagedRoutesOutlet(runtime); + + runtime.registerRoute({ + path: "/authenticated", + element:
Hello!
, + visibility: "authenticated" + }); + + const routes = getManagedRoutes(runtime.routes)!; - runtime.registerRoute({ - element:
Hello!
+ expect(routes.length).toBe(1); + expect(routes[0].path).toBe("/authenticated"); + expect((routes[0] as RootRoute).visibility).toBe("authenticated"); }); - runtime.registerRoute({ - element:
How
+ test("can register a root route with a name", () => { + const runtime = new Runtime(); + + registerManagedRoutesOutlet(runtime); + + runtime.registerRoute({ + name: "foo", + element:
Hello!
+ }); + + const routes = getManagedRoutes(runtime.routes)!; + + expect(routes.length).toBe(1); + expect(routes[0].name).toBe("foo"); }); - runtime.registerRoute({ - element:
Are
+ test("can register a nested route with a name", () => { + const runtime = new Runtime(); + + registerManagedRoutesOutlet(runtime); + + runtime.registerRoute({ + element:
Hello
, + children: [ + { + name: "foo", + element:
You!
+ } + ] + }); + + const routes = getManagedRoutes(runtime.routes)!; + + expect(routes.length).toBe(1); + expect(routes[0].children![0].name).toBe("foo"); }); + }); + + describe("hoisted", () => { + test("can register an index route", () => { + const runtime = new Runtime(); - runtime.registerRoute({ - element:
You?
+ runtime.registerRoute({ + hoist: true, + index: true, + element:
Hello!
+ }); + + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].index).toBeTruthy(); }); - expect(runtime.routes.length).toBe(4); - }); + test("can register a pathless route", () => { + const runtime = new Runtime(); - test("can register a deeply nested route with pathless parent routes", () => { - const runtime = new Runtime(); + runtime.registerRoute({ + hoist: true, + element:
Hello!
+ }); - runtime.registerRoute({ - element:
Hello
, - children: [ - { - element:
You!
, - children: [ - { - path: "/deeply-nested-route", - element:
Hello from nested!
- } - ] - } - ] + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].index).toBeUndefined(); + expect(runtime.routes[0].path).toBeUndefined(); }); - expect(runtime.routes.length).toBe(1); - expect(runtime.routes[0].children![0].children![0].path).toBe("/deeply-nested-route"); - }); + test("can register multiple pathless routes", () => { + const runtime = new Runtime(); - test("can register a deeply nested index route with pathless parent routes", () => { - const runtime = new Runtime(); + runtime.registerRoute({ + hoist: true, + element:
Hello!
+ }); - runtime.registerRoute({ - element:
Hello
, - children: [ - { - element:
You!
, - children: [ - { - index: true, - element:
Hello from nested!
- } - ] - } - ] + runtime.registerRoute({ + hoist: true, + element:
How
+ }); + + runtime.registerRoute({ + hoist: true, + element:
Are
+ }); + + runtime.registerRoute({ + hoist: true, + element:
You?
+ }); + + expect(runtime.routes.length).toBe(4); }); - expect(runtime.routes.length).toBe(1); - expect(runtime.routes[0].children![0].children![0].index).toBeTruthy(); - }); + test("can register a deeply nested route with pathless parent routes", () => { + const runtime = new Runtime(); - test("can register an hoisted route", () => { - const runtime = new Runtime(); + runtime.registerRoute({ + hoist: true, + element:
Hello
, + children: [ + { + element:
You!
, + children: [ + { + path: "/deeply-nested-route", + element:
Hello from nested!
+ } + ] + } + ] + }); - runtime.registerRoute({ - path: "/root", - element:
Hello!
, - hoist: true + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].children![0].children![0].path).toBe("/deeply-nested-route"); }); - expect(runtime.routes.length).toBe(1); - expect(runtime.routes[0].path).toBe("/root"); - expect(runtime.routes[0].hoist).toBeTruthy(); - }); + test("can register a deeply nested index route with pathless parent routes", () => { + const runtime = new Runtime(); - test("can register a route with a \"public\" visibility", () => { - const runtime = new Runtime(); + runtime.registerRoute({ + hoist: true, + element:
Hello
, + children: [ + { + element:
You!
, + children: [ + { + index: true, + element:
Hello from nested!
+ } + ] + } + ] + }); - runtime.registerRoute({ - path: "/public", - element:
Hello!
, - visibility: "public" + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].children![0].children![0].index).toBeTruthy(); }); - expect(runtime.routes.length).toBe(1); - expect(runtime.routes[0].path).toBe("/public"); - expect(runtime.routes[0].visibility).toBe("public"); - }); + test("can register a route with a \"public\" visibility", () => { + const runtime = new Runtime(); - test("can register a route with a \"authenticated\" visibility", () => { - const runtime = new Runtime(); + runtime.registerRoute({ + hoist: true, + path: "/public", + element:
Hello!
, + visibility: "public" + }); - runtime.registerRoute({ - path: "/authenticated", - element:
Hello!
, - visibility: "authenticated" + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].path).toBe("/public"); + expect((runtime.routes[0] as RootRoute).visibility).toBe("public"); }); - expect(runtime.routes.length).toBe(1); - expect(runtime.routes[0].path).toBe("/authenticated"); - expect(runtime.routes[0].visibility).toBe("authenticated"); - }); + test("can register a route with a \"authenticated\" visibility", () => { + const runtime = new Runtime(); - test("can register a root route with a name", () => { - const runtime = new Runtime(); + runtime.registerRoute({ + hoist: true, + path: "/authenticated", + element:
Hello!
, + visibility: "authenticated" + }); - runtime.registerRoute({ - name: "foo", - element:
Hello!
+ expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].path).toBe("/authenticated"); + expect((runtime.routes[0] as RootRoute).visibility).toBe("authenticated"); }); - expect(runtime.routes.length).toBe(1); - expect(runtime.routes[0].name).toBe("foo"); - }); + test("can register a root route with a name", () => { + const runtime = new Runtime(); - test("can register a nested route with a name", () => { - const runtime = new Runtime(); + runtime.registerRoute({ + hoist: true, + name: "foo", + element:
Hello!
+ }); - runtime.registerRoute({ - element:
Hello
, - children: [ - { - name: "foo", - element:
You!
- } - ] + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].name).toBe("foo"); }); - expect(runtime.routes.length).toBe(1); - expect(runtime.routes[0].children![0].name).toBe("foo"); + test("can register a nested route with a name", () => { + const runtime = new Runtime(); + + runtime.registerRoute({ + hoist: true, + element:
Hello
, + children: [ + { + name: "foo", + element:
You!
+ } + ] + }); + + expect(runtime.routes.length).toBe(1); + expect(runtime.routes[0].children![0].name).toBe("foo"); + }); }); describe("parentPath", () => { @@ -221,6 +404,7 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ + hoist: true, path: "/layout", element:
Hello!
}); @@ -271,6 +455,7 @@ describe("registerRoute", () => { expect(runtime.routes.length).toBe(0); runtime.registerRoute({ + hoist: true, path: "/foo", element:
Hello!
}); @@ -279,6 +464,7 @@ describe("registerRoute", () => { expect(runtime.routes[0].children).toBeUndefined(); runtime.registerRoute({ + hoist: true, path: "/layout", element:
Hello!
}); @@ -300,6 +486,7 @@ describe("registerRoute", () => { expect(runtime.routes.length).toBe(0); runtime.registerRoute({ + hoist: true, element:
Hello
, children: [ { @@ -323,6 +510,7 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ + hoist: true, path: "/layout", element:
Hello!
}); @@ -349,6 +537,7 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ + hoist: true, element:
Hello
, children: [ { @@ -388,6 +577,7 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ + hoist: true, path: "/layout", element:
Hello!
}); @@ -406,6 +596,7 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ + hoist: true, path: "/layout/", element:
Hello!
}); @@ -419,6 +610,17 @@ describe("registerRoute", () => { expect(runtime.routes[0].children![0].path).toBe("/layout/nested"); }); + + test("when a route is hoisted, it cannot be nested under another route", () => { + const runtime = new Runtime(); + + expect(() => runtime.registerRoute({ + hoist: true, + element:
Hello
+ }, { + parentPath: "/foo" + })).toThrow(); + }); }); describe("parentName", () => { @@ -426,6 +628,7 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ + hoist: true, name: "layout", element:
Hello!
}); @@ -476,6 +679,7 @@ describe("registerRoute", () => { expect(runtime.routes.length).toBe(0); runtime.registerRoute({ + hoist: true, path: "/foo", element:
Hello!
}); @@ -484,6 +688,7 @@ describe("registerRoute", () => { expect(runtime.routes[0].children).toBeUndefined(); runtime.registerRoute({ + hoist: true, name: "layout", element:
Hello!
}); @@ -505,6 +710,7 @@ describe("registerRoute", () => { expect(runtime.routes.length).toBe(0); runtime.registerRoute({ + hoist: true, element:
Hello
, children: [ { @@ -528,6 +734,7 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ + hoist: true, name: "layout", element:
Hello!
}); @@ -555,6 +762,7 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ + hoist: true, element:
Hello
, children: [ { @@ -589,6 +797,17 @@ describe("registerRoute", () => { expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].children![0].children![0].children![0].children![0].path).toBe("/deeply-nested-layout/another-level/yet-another-level"); }); + + test("when a route is hoisted, it cannot be nested under another route", () => { + const runtime = new Runtime(); + + expect(() => runtime.registerRoute({ + hoist: true, + element:
Hello
+ }, { + parentName: "foo" + })).toThrow(); + }); }); }); @@ -652,74 +871,6 @@ describe("registerNavigationItem", () => { }); }); -describe("_completeRegistration", () => { - describe("parentPath", () => { - test("when the registration is completed and there are no pending registrations, do nothing", () => { - const runtime = new Runtime(); - - runtime.registerRoute({ - path: "/layout/nested", - element:
Hello!
- }, { - parentPath: "/layout" - }); - - runtime.registerRoute({ - path: "/layout", - element:
Hello!
- }); - - expect(() => runtime._completeRegistration()).not.toThrow(); - }); - - test("when the registration is completed and there are pending registrations, throw an error", () => { - const runtime = new Runtime(); - - runtime.registerRoute({ - path: "/layout/nested", - element:
Hello!
- }, { - parentPath: "/layout" - }); - - expect(() => runtime._completeRegistration()).toThrow(); - }); - }); - - describe("parentName", () => { - test("when the registration is completed and there are no pending registrations, do nothing", () => { - const runtime = new Runtime(); - - runtime.registerRoute({ - path: "/layout/nested", - element:
Hello!
- }, { - parentName: "layout" - }); - - runtime.registerRoute({ - name: "layout", - element:
Hello!
- }); - - expect(() => runtime._completeRegistration()).not.toThrow(); - }); - - test("when the registration is completed and there are pending registrations, throw an error", () => { - const runtime = new Runtime(); - - runtime.registerRoute({ - path: "/layout/nested", - element:
Hello!
- }, { - parentName: "layout" - }); - - expect(() => runtime._completeRegistration()).toThrow(); - }); - }); -}); - describe("getNavigationItems", () => { test("when no menu id is specified, returns all the registered navigation items for the root menu", () => { const runtime = new Runtime(); @@ -793,3 +944,93 @@ describe("getNavigationItems", () => { expect(runtime.getNavigationItems("menu-1")[0].to).toBe("/item-4"); }); }); + +describe("_completeRegistration", () => { + describe("managed routes", () => { + test("when the outlet is missing, the error message mentions the ManagedRoutes outlet", () => { + const runtime = new Runtime(); + let errorMessage; + + runtime.registerRoute({ + path: "/layout", + element:
Hello!
+ }); + + try { + runtime._completeRegistration(); + } catch (error: unknown) { + errorMessage = (error as Error).message; + } + + expect(errorMessage).toContain("ManagedRoutes"); + }); + }); + + describe("parentPath", () => { + test("when the registration is completed and there are no pending registrations, do nothing", () => { + const runtime = new Runtime(); + + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentPath: "/layout" + }); + + runtime.registerRoute({ + hoist: true, + path: "/layout", + element:
Hello!
+ }); + + expect(() => runtime._completeRegistration()).not.toThrow(); + }); + + test("when the registration is completed and there are pending registrations, throw an error", () => { + const runtime = new Runtime(); + + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentPath: "/layout" + }); + + expect(() => runtime._completeRegistration()).toThrow(); + }); + }); + + describe("parentName", () => { + test("when the registration is completed and there are no pending registrations, do nothing", () => { + const runtime = new Runtime(); + + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentName: "layout" + }); + + runtime.registerRoute({ + hoist: true, + name: "layout", + element:
Hello!
+ }); + + expect(() => runtime._completeRegistration()).not.toThrow(); + }); + + test("when the registration is completed and there are pending registrations, throw an error", () => { + const runtime = new Runtime(); + + runtime.registerRoute({ + path: "/layout/nested", + element:
Hello!
+ }, { + parentName: "layout" + }); + + expect(() => runtime._completeRegistration()).toThrow(); + }); + }); +}); diff --git a/sample/host/src/App.tsx b/sample/host/src/App.tsx index f8cf5953d..437379226 100644 --- a/sample/host/src/App.tsx +++ b/sample/host/src/App.tsx @@ -23,42 +23,40 @@ export function App() { useEffect(() => { if (areModulesReady && isMswStarted) { - setIsReady(true); + // getActiveRouteVisibility - // // getActiveRouteVisibility + // const location = useLocation(); + const location = window.location; - // // const location = useLocation(); - // const location = window.location; + console.log("**** location: ", location); - // console.log("**** location: ", location); + const matchingRoutes = matchRoutes(runtime.routes, location) ?? []; - // const matchingRoutes = matchRoutes(runtime.routes, location) ?? []; + console.log("**** matchingRoutes:", matchingRoutes); - // console.log("**** matchingRoutes:", matchingRoutes); + if (matchingRoutes.length > 0) { + // When a route is nested, it also returns all the parts that constistuate the whole route (for example the layouts). + // We only want to know the visiblity of the deepest root route. + const rootRoute = matchingRoutes.findLast(x => x.route.type === "root"); - // if (matchingRoutes.length > 0) { - // // When a route is nested, it also returns all the parts that constistuate the whole route (for example the layouts). - // // We only want to know the visiblity of the deepest root route. - // const rootRoute = matchingRoutes.findLast(x => x.route.type === "root"); + if (rootRoute!.route.visibility === "authenticated") { + axios.get("/session").then(({ data }) => { + const session: Session = { + user: { + name: data.username + } + }; - // if (rootRoute!.route.visibility === "authenticated") { - // axios.get("/session").then(({ data }) => { - // const session: Session = { - // user: { - // name: data.username - // } - // }; + sessionManager.setSession(session); - // sessionManager.setSession(session); - - // setIsReady(true); - // }); - // } else { - // setIsReady(true); - // } - // } else { - // throw new Error(`[shell] There's no matching route for the location: "${location}". Did you add routes to React Router without using the runtime.registerRoute() function?`); - // } + setIsReady(true); + }); + } else { + setIsReady(true); + } + } else { + throw new Error(`[shell] There's no matching route for the location: "${location}". Did you add routes to React Router without using the runtime.registerRoute() function?`); + } } }, [areModulesReady, isMswStarted, runtime.routes]); diff --git a/sample/shell/src/register.tsx b/sample/shell/src/register.tsx index 0b3ae27ec..a26def052 100644 --- a/sample/shell/src/register.tsx +++ b/sample/shell/src/register.tsx @@ -1,5 +1,5 @@ import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; -import { ManagedRoutesOutlet } from "@squide/react-router"; +import { ManagedRoutes } from "@squide/react-router"; import type { OnLoginHandler } from "./Login.tsx"; import type { onLogoutHandler } from "./Logout.tsx"; import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; @@ -38,7 +38,7 @@ export function registerShell(onLogin: OnLoginHandler, onLogout: onLogoutHandler }; }, children: [ - ManagedRoutesOutlet + ManagedRoutes ] } ] diff --git a/sample/shell/src/useAppRouter.tsx b/sample/shell/src/useAppRouter.tsx index 7c82da3b0..c1f568e8d 100644 --- a/sample/shell/src/useAppRouter.tsx +++ b/sample/shell/src/useAppRouter.tsx @@ -7,8 +7,6 @@ import type { Router } from "@remix-run/router"; export function useAppRouter(): Router { const routes = useRoutes(); - console.log(routes); - const router = useMemo(() => { return createBrowserRouter(routes); }, [routes]); From abd9fc6043f1f408b3ea30e6e5ef58a6d186cf20 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Tue, 10 Oct 2023 10:28:36 -0400 Subject: [PATCH 06/51] Added back authentication --- docs/guides/override-the-host-layout.md | 3 +- docs/reference/routing/useHoistedRoutes.md | 9 +- docs/reference/runtime/runtime-class.md | 3 +- packages/core/src/runtime/abstractRuntime.ts | 1 + packages/msw/src/useIsMswReady.ts | 6 +- packages/react-router/src/index.ts | 11 +- packages/react-router/src/routeRegistry.ts | 1 - packages/react-router/src/runtime.ts | 8 +- packages/react-router/src/useHoistedRoutes.ts | 64 ---- packages/react-router/tests/runtime.test.tsx | 88 +++-- .../tests/useHoistedRoutes.test.tsx | 131 ------- .../react-router/tests/useRoutes.test.tsx | 8 + pnpm-lock.yaml | 321 +++++++++--------- sample/another-remote-module/package.json | 1 - .../another-remote-module/src/dev/index.tsx | 4 +- .../another-remote-module/src/dev/session.ts | 14 - sample/another-remote-module/webpack.build.js | 3 - sample/another-remote-module/webpack.dev.js | 3 - sample/host/mocks/browser.ts | 3 +- sample/host/mocks/handlers.ts | 77 ----- sample/host/src/App.tsx | 42 ++- sample/host/src/bootstrap.tsx | 4 +- sample/host/src/session.ts | 41 +-- sample/local-module/src/dev/index.tsx | 4 +- sample/local-module/src/dev/session.ts | 14 - sample/local-module/src/register.tsx | 2 +- sample/remote-module/package.json | 1 - sample/remote-module/src/dev/index.tsx | 4 +- sample/remote-module/src/dev/session.ts | 14 - sample/remote-module/src/register.tsx | 15 +- sample/remote-module/webpack.build.js | 3 - sample/remote-module/webpack.dev.js | 3 - sample/shared/src/session.ts | 6 + sample/shell/mocks/authenticationHandlers.ts | 58 ++++ sample/shell/package.json | 6 + sample/shell/src/AuthenticatedLayout.tsx | 32 +- sample/shell/src/AuthenticationBoundary.tsx | 12 +- sample/shell/src/Login.tsx | 57 ++-- sample/shell/src/Logout.tsx | 12 +- sample/shell/src/register.tsx | 181 +++++----- 40 files changed, 540 insertions(+), 730 deletions(-) delete mode 100644 packages/react-router/src/useHoistedRoutes.ts delete mode 100644 packages/react-router/tests/useHoistedRoutes.test.tsx delete mode 100644 sample/host/mocks/handlers.ts create mode 100644 sample/shell/mocks/authenticationHandlers.ts diff --git a/docs/guides/override-the-host-layout.md b/docs/guides/override-the-host-layout.md index c1713e15e..51ac89d2b 100644 --- a/docs/guides/override-the-host-layout.md +++ b/docs/guides/override-the-host-layout.md @@ -178,7 +178,6 @@ import { Login } from "./Login.tsx"; export function register: ModuleRegisterFunction(runtime) { runtime.registerRoute({ path: "/login", - hoist: true, element: , children: [ { @@ -191,6 +190,8 @@ export function register: ModuleRegisterFunction(runtime) { ] } ] + }, { + hoist: true }); } ``` diff --git a/docs/reference/routing/useHoistedRoutes.md b/docs/reference/routing/useHoistedRoutes.md index 591465b2e..b152ec6cf 100644 --- a/docs/reference/routing/useHoistedRoutes.md +++ b/docs/reference/routing/useHoistedRoutes.md @@ -96,8 +96,9 @@ export function register: ModuleRegisterFunction(runtime) { path: "/about", element: , errorElement: + }), { hoist: true - }); + }; runtime.registerNavigationItem({ to: "/about", @@ -167,7 +168,6 @@ import { About } from "./About.tsx"; export function register: ModuleRegisterFunction(runtime) { runtime.registerRoute({ path: "/about", - hoist: true, // Will render the "About" page inside the "RemoteLayout" rather than the "RootLayout". // For more information about React Router's nested routes, view https://reactrouter.com/en/main/start/tutorial#nested-routes. element: , @@ -182,6 +182,8 @@ export function register: ModuleRegisterFunction(runtime) { ] } ] + }, { + hoist: true }); runtime.registerNavigationItem({ @@ -255,7 +257,8 @@ export function register: ModuleRegisterFunction(runtime) { runtime.registerRoute({ path: "/login", element: , - errorElement: , + errorElement: + }, { // By hoisting the "Login" page, it will now be rendered outside of the default // authenticated boundary and will therefore be public. hoist: true diff --git a/docs/reference/runtime/runtime-class.md b/docs/reference/runtime/runtime-class.md index 296102fd2..1d4a586de 100644 --- a/docs/reference/runtime/runtime-class.md +++ b/docs/reference/runtime/runtime-class.md @@ -93,7 +93,8 @@ import { Page } from "./Page.tsx"; runtime.registerRoute({ path: "/page-1", - element: , + element: +}, { hoist: true }); ``` diff --git a/packages/core/src/runtime/abstractRuntime.ts b/packages/core/src/runtime/abstractRuntime.ts index 5d11f2e02..bc6c0ed89 100644 --- a/packages/core/src/runtime/abstractRuntime.ts +++ b/packages/core/src/runtime/abstractRuntime.ts @@ -17,6 +17,7 @@ export interface RuntimeOptions { } export interface RegisterRouteOptions { + hoist?: true; parentPath?: string; parentName?: string; } diff --git a/packages/msw/src/useIsMswReady.ts b/packages/msw/src/useIsMswReady.ts index 91d79e894..be0c88a1f 100644 --- a/packages/msw/src/useIsMswReady.ts +++ b/packages/msw/src/useIsMswReady.ts @@ -1,4 +1,4 @@ -import { useRuntime } from "@squide/core"; +import { useLogger, useRuntime } from "@squide/core"; import { useEffect, useState } from "react"; import { getMswPlugin } from "./mswPlugin.ts"; @@ -9,6 +9,8 @@ export interface UseIsMswStartedOptions { export function useIsMswStarted(enabled: boolean, { interval = 10 }: UseIsMswStartedOptions = {}) { const runtime = useRuntime(); + const logger = useLogger(); + const mswPlugin = getMswPlugin(runtime); // Using a state hook to force a rerender once MSW is started. @@ -19,6 +21,8 @@ export function useIsMswStarted(enabled: boolean, { interval = 10 }: UseIsMswSta if (enabled) { const intervalId = setInterval(() => { if (mswPlugin.isStarted) { + logger.debug("[squide] MSW is ready."); + // Must clear interval before calling "_completeRegistration" in case there's an error. clearInterval(intervalId); setIsStarted(true); diff --git a/packages/react-router/src/index.ts b/packages/react-router/src/index.ts index af27493ef..6421be76c 100644 --- a/packages/react-router/src/index.ts +++ b/packages/react-router/src/index.ts @@ -1,13 +1,8 @@ export * from "@squide/core"; - -export * from "./runtime.ts"; - -export * from "./routeRegistry.ts"; - -export * from "./useHoistedRoutes.ts"; -export * from "./useRoutes.ts"; - export * from "./navigationItemRegistry.ts"; +export * from "./routeRegistry.ts"; +export * from "./runtime.ts"; export * from "./useNavigationItems.ts"; export * from "./useRenderedNavigationItems.tsx"; +export * from "./useRoutes.ts"; diff --git a/packages/react-router/src/routeRegistry.ts b/packages/react-router/src/routeRegistry.ts index e83cb162a..7cc119140 100644 --- a/packages/react-router/src/routeRegistry.ts +++ b/packages/react-router/src/routeRegistry.ts @@ -17,7 +17,6 @@ export interface NonIndexRoute extends Omit { export type Route = IndexRoute | NonIndexRoute; export type RootRoute = Route & { - hoist?: boolean; visibility?: RouteVisibility; type?: RouteType; }; diff --git a/packages/react-router/src/runtime.ts b/packages/react-router/src/runtime.ts index eb17b6b81..5ae19a037 100644 --- a/packages/react-router/src/runtime.ts +++ b/packages/react-router/src/runtime.ts @@ -16,12 +16,12 @@ export class Runtime extends AbstractRuntime { - acc.push(...getAllRoutePaths(x)); - - return acc; - }, []); - - if (!isNil(current)) { - return [current, ...childPaths]; - } - - return childPaths; - } - - return !isNil(current) ? [current] : []; -} - -export function useHoistedRoutes(routes: RootRoute[], wrapManagedRoutes: (routes: Route[]) => Route, { allowedPaths }: UseHoistedRoutesOptions = {}): Route[] { - // Hack to reuse the same array reference through re-renders. - const [memoizedAllowedPaths] = useState(allowedPaths); - - return useMemo(() => { - const hoistedRoutes: Route[] = []; - const managedRoutes: Route[] = []; - - routes.forEach(({ hoist, ...route }) => { - if (hoist === true) { - hoistedRoutes.push(route); - } else { - managedRoutes.push(route); - } - }); - - if (memoizedAllowedPaths) { - // Find hoisted routes which are not included in allowedPaths. - hoistedRoutes.forEach(x => { - const allRoutePaths = getAllRoutePaths(x); - const restrictedPaths = allRoutePaths.filter(y => !memoizedAllowedPaths.includes(y)); - - if (restrictedPaths.length > 0) { - throw new Error(`[squide] A module is hoisting the following routes [${restrictedPaths.map(y => `"${y}"`).join(", ")}] which are not included in the provided "allowedRoutes" option: [${allowedPaths?.map(y => `"${y}"`).join(", ")}].`); - } - }); - } - - const allRoutes = [ - ...hoistedRoutes, - ...(wrapManagedRoutes ? [wrapManagedRoutes(managedRoutes)] : managedRoutes) - ]; - - return allRoutes; - }, [routes, wrapManagedRoutes, memoizedAllowedPaths]); -} diff --git a/packages/react-router/tests/runtime.test.tsx b/packages/react-router/tests/runtime.test.tsx index d7de6a835..30d5476e5 100644 --- a/packages/react-router/tests/runtime.test.tsx +++ b/packages/react-router/tests/runtime.test.tsx @@ -243,9 +243,10 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - hoist: true, index: true, element:
Hello!
+ }, { + hoist: true }); expect(runtime.routes.length).toBe(1); @@ -256,8 +257,9 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - hoist: true, element:
Hello!
+ }, { + hoist: true }); expect(runtime.routes.length).toBe(1); @@ -269,23 +271,27 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - hoist: true, element:
Hello!
+ }, { + hoist: true }); runtime.registerRoute({ - hoist: true, element:
How
+ }, { + hoist: true }); runtime.registerRoute({ - hoist: true, element:
Are
+ }, { + hoist: true }); runtime.registerRoute({ - hoist: true, element:
You?
+ }, { + hoist: true }); expect(runtime.routes.length).toBe(4); @@ -295,7 +301,6 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - hoist: true, element:
Hello
, children: [ { @@ -308,6 +313,8 @@ describe("registerRoute", () => { ] } ] + }, { + hoist: true }); expect(runtime.routes.length).toBe(1); @@ -318,7 +325,6 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - hoist: true, element:
Hello
, children: [ { @@ -331,6 +337,8 @@ describe("registerRoute", () => { ] } ] + }, { + hoist: true }); expect(runtime.routes.length).toBe(1); @@ -341,10 +349,11 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - hoist: true, path: "/public", element:
Hello!
, visibility: "public" + }, { + hoist: true }); expect(runtime.routes.length).toBe(1); @@ -356,10 +365,11 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - hoist: true, path: "/authenticated", element:
Hello!
, visibility: "authenticated" + }, { + hoist: true }); expect(runtime.routes.length).toBe(1); @@ -371,9 +381,10 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - hoist: true, name: "foo", element:
Hello!
+ }, { + hoist: true }); expect(runtime.routes.length).toBe(1); @@ -384,7 +395,6 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - hoist: true, element:
Hello
, children: [ { @@ -392,6 +402,8 @@ describe("registerRoute", () => { element:
You!
} ] + }, { + hoist: true }); expect(runtime.routes.length).toBe(1); @@ -404,9 +416,10 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - hoist: true, path: "/layout", element:
Hello!
+ }, { + hoist: true }); expect(runtime.routes.length).toBe(1); @@ -455,18 +468,20 @@ describe("registerRoute", () => { expect(runtime.routes.length).toBe(0); runtime.registerRoute({ - hoist: true, path: "/foo", element:
Hello!
+ }, { + hoist: true }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].children).toBeUndefined(); runtime.registerRoute({ - hoist: true, path: "/layout", element:
Hello!
+ }, { + hoist: true }); expect(runtime.routes.length).toBe(2); @@ -486,7 +501,6 @@ describe("registerRoute", () => { expect(runtime.routes.length).toBe(0); runtime.registerRoute({ - hoist: true, element:
Hello
, children: [ { @@ -499,6 +513,8 @@ describe("registerRoute", () => { ] } ] + }, { + hoist: true }); expect(runtime.routes.length).toBe(1); @@ -510,9 +526,10 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - hoist: true, path: "/layout", element:
Hello!
+ }, { + hoist: true }); runtime.registerRoute({ @@ -537,7 +554,6 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - hoist: true, element:
Hello
, children: [ { @@ -550,6 +566,8 @@ describe("registerRoute", () => { ] } ] + }, { + hoist: true }); runtime.registerRoute({ @@ -577,9 +595,10 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - hoist: true, path: "/layout", element:
Hello!
+ }, { + hoist: true }); runtime.registerRoute({ @@ -596,9 +615,10 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - hoist: true, path: "/layout/", element:
Hello!
+ }, { + hoist: true }); runtime.registerRoute({ @@ -615,9 +635,9 @@ describe("registerRoute", () => { const runtime = new Runtime(); expect(() => runtime.registerRoute({ - hoist: true, element:
Hello
}, { + hoist: true, parentPath: "/foo" })).toThrow(); }); @@ -628,9 +648,10 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - hoist: true, name: "layout", element:
Hello!
+ }, { + hoist: true }); expect(runtime.routes.length).toBe(1); @@ -679,18 +700,20 @@ describe("registerRoute", () => { expect(runtime.routes.length).toBe(0); runtime.registerRoute({ - hoist: true, path: "/foo", element:
Hello!
+ }, { + hoist: true }); expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].children).toBeUndefined(); runtime.registerRoute({ - hoist: true, name: "layout", element:
Hello!
+ }, { + hoist: true }); expect(runtime.routes.length).toBe(2); @@ -710,7 +733,6 @@ describe("registerRoute", () => { expect(runtime.routes.length).toBe(0); runtime.registerRoute({ - hoist: true, element:
Hello
, children: [ { @@ -723,6 +745,8 @@ describe("registerRoute", () => { ] } ] + }, { + hoist: true }); expect(runtime.routes.length).toBe(1); @@ -734,9 +758,10 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - hoist: true, name: "layout", element:
Hello!
+ }, { + hoist: true }); runtime.registerRoute({ @@ -762,7 +787,6 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - hoist: true, element:
Hello
, children: [ { @@ -775,6 +799,8 @@ describe("registerRoute", () => { ] } ] + }, { + hoist: true }); runtime.registerRoute({ @@ -802,9 +828,9 @@ describe("registerRoute", () => { const runtime = new Runtime(); expect(() => runtime.registerRoute({ - hoist: true, element:
Hello
}, { + hoist: true, parentName: "foo" })).toThrow(); }); @@ -978,9 +1004,10 @@ describe("_completeRegistration", () => { }); runtime.registerRoute({ - hoist: true, path: "/layout", element:
Hello!
+ }, { + hoist: true }); expect(() => runtime._completeRegistration()).not.toThrow(); @@ -1012,9 +1039,10 @@ describe("_completeRegistration", () => { }); runtime.registerRoute({ - hoist: true, name: "layout", element:
Hello!
+ }, { + hoist: true }); expect(() => runtime._completeRegistration()).not.toThrow(); diff --git a/packages/react-router/tests/useHoistedRoutes.test.tsx b/packages/react-router/tests/useHoistedRoutes.test.tsx deleted file mode 100644 index d17bfd49a..000000000 --- a/packages/react-router/tests/useHoistedRoutes.test.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { renderHook } from "@testing-library/react"; -import type { RootRoute, Route } from "../src/routeRegistry.ts"; -import { useHoistedRoutes, type UseHoistedRoutesOptions } from "../src/useHoistedRoutes.ts"; - -test("hoisted routes are at the root", () => { - const routes: RootRoute[] = [ - { path: "/foo", element:
Foo
}, - { path: "/hoisted", element:
Hoisted
, hoist: true } - ]; - - const wrapManagedRoutes = (managedRoutes: Route[]) => { - return { - path: "/wrapper", - children: [ - ...managedRoutes - ] - }; - }; - - const { result } = renderHook(({ routes: x, wrapManagedRoutes: y }) => useHoistedRoutes(x, y), { - initialProps: { routes, wrapManagedRoutes } - }); - - expect(result.current.length).toBe(2); - expect(result.current[0].path).toBe("/hoisted"); -}); - -test("managed routes are wrapped", () => { - const routes: RootRoute[] = [ - { path: "/foo", element:
Foo
}, - { path: "/hoisted", element:
Hoisted
, hoist: true } - ]; - - const wrapManagedRoutes = (managedRoutes: Route[]) => { - return { - path: "/wrapper", - children: [ - ...managedRoutes - ] - }; - }; - - const { result } = renderHook(({ routes: x, wrapManagedRoutes: y }) => useHoistedRoutes(x, y), { - initialProps: { - routes, - wrapManagedRoutes - } - }); - - expect(result.current.length).toBe(2); - expect(result.current[1].path).toBe("/wrapper"); - // eslint-disable-next-line testing-library/no-node-access - expect(result.current[1].children![0].path).toBe("/foo"); -}); - -test("when a restricted route is hoisted, throw an error", () => { - // Prevent the expected exception from printing in the console. - const consoleMock = jest.spyOn(console, "error").mockImplementation(jest.fn()); - - const routes: RootRoute[] = [ - { path: "/foo", element:
Foo
}, - { path: "/hoisted", element:
Hoisted
, hoist: true }, - { path: "/bar", element:
Bar
, hoist: true } - ]; - - const wrapManagedRoutes = (managedRoutes: Route[]) => { - return { - path: "/wrapper", - children: [ - ...managedRoutes - ] - }; - }; - - expect(() => renderHook(({ routes: x, wrapManagedRoutes: y, options }) => useHoistedRoutes(x, y, options), { - initialProps: { - routes, - wrapManagedRoutes, - options: { - allowedPaths: [ - "/hoisted" - ] - } satisfies UseHoistedRoutesOptions - } - })).toThrow(/\/bar/); - - consoleMock.mockRestore(); -}); - -test("returned array is immutable", () => { - const routes: RootRoute[] = [ - { path: "/foo", element:
Foo
}, - { path: "/hoisted", element:
Hoisted
, hoist: true } - ]; - - const wrapManagedRoutes = (managedRoutes: Route[]) => { - return { - path: "/wrapper", - children: [ - ...managedRoutes - ] - }; - }; - - const { result, rerender } = renderHook(({ routes: x, wrapManagedRoutes: y }) => useHoistedRoutes(x, y), { - initialProps: { - routes, - wrapManagedRoutes - } - }); - - const array1 = result.current; - - // Haven't updated the routes, the returned array should be "array1". - rerender({ - routes, - wrapManagedRoutes - }); - - const array2 = result.current; - - rerender({ - routes: [...routes, { path: "/bar", element:
Bar
}], - wrapManagedRoutes - }); - - const array3 = result.current; - - expect(array1).toEqual(array2); - expect(array1).not.toEqual(array3); -}); diff --git a/packages/react-router/tests/useRoutes.test.tsx b/packages/react-router/tests/useRoutes.test.tsx index 25f1994c2..e10fc1eaa 100644 --- a/packages/react-router/tests/useRoutes.test.tsx +++ b/packages/react-router/tests/useRoutes.test.tsx @@ -20,11 +20,15 @@ test("returns all the registered routes", () => { runtime.registerRoute({ path: "/foo", element:
Foo
+ }, { + hoist: true }); runtime.registerRoute({ path: "/bar", element:
Bar
+ }, { + hoist: true }); const { result } = renderWithRuntime(runtime); @@ -38,6 +42,8 @@ test("returned array is immutable", () => { runtime.registerRoute({ path: "/foo", element:
Foo
+ }, { + hoist: true }); const { result, rerender } = renderWithRuntime(runtime); @@ -52,6 +58,8 @@ test("returned array is immutable", () => { runtime.registerRoute({ path: "/bar", element:
Bar
+ }, { + hoist: true }); // Added a new route, the returned array should be a new instance. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2c833bdac..cb02006bd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -309,10 +309,7 @@ importers: version: link:../../packages/webpack-module-federation '@tanstack/react-query': specifier: rc - version: 5.0.0-rc.4(react-dom@18.2.0)(react@18.2.0) - axios: - specifier: 1.5.1 - version: 1.5.1(debug@4.3.4) + version: 5.0.0-rc.5(react-dom@18.2.0)(react@18.2.0) react: specifier: 18.2.0 version: 18.2.0 @@ -331,7 +328,7 @@ importers: version: 0.5.2 '@tanstack/react-query-devtools': specifier: rc - version: 5.0.0-rc.4(@tanstack/react-query@5.0.0-rc.4)(react-dom@18.2.0)(react@18.2.0) + version: 5.0.0-rc.5(@tanstack/react-query@5.0.0-rc.5)(react-dom@18.2.0)(react@18.2.0) '@types/react': specifier: 18.2.22 version: 18.2.22 @@ -401,9 +398,6 @@ importers: '@squide/webpack-module-federation': specifier: workspace:* version: link:../../packages/webpack-module-federation - '@tanstack/react-query': - specifier: rc - version: 5.0.0-rc.4(react-dom@18.2.0)(react@18.2.0) axios: specifier: 1.5.1 version: 1.5.1(debug@4.3.4) @@ -423,9 +417,6 @@ importers: '@swc/helpers': specifier: 0.5.2 version: 0.5.2 - '@tanstack/react-query-devtools': - specifier: rc - version: 5.0.0-rc.4(@tanstack/react-query@5.0.0-rc.4)(react-dom@18.2.0)(react@18.2.0) '@types/react': specifier: 18.2.22 version: 18.2.22 @@ -585,10 +576,7 @@ importers: version: link:../../packages/webpack-module-federation '@tanstack/react-query': specifier: rc - version: 5.0.0-rc.4(react-dom@18.2.0)(react@18.2.0) - axios: - specifier: 1.5.1 - version: 1.5.1(debug@4.3.4) + version: 5.0.0-rc.5(react-dom@18.2.0)(react@18.2.0) react: specifier: 18.2.0 version: 18.2.0 @@ -607,7 +595,7 @@ importers: version: 0.5.2 '@tanstack/react-query-devtools': specifier: rc - version: 5.0.0-rc.4(@tanstack/react-query@5.0.0-rc.4)(react-dom@18.2.0)(react@18.2.0) + version: 5.0.0-rc.5(@tanstack/react-query@5.0.0-rc.5)(react-dom@18.2.0)(react@18.2.0) '@types/react': specifier: 18.2.22 version: 18.2.22 @@ -710,6 +698,9 @@ importers: '@sample/shared': specifier: workspace:* version: link:../shared + '@squide/msw': + specifier: workspace:* + version: link:../../packages/msw '@squide/react-router': specifier: workspace:* version: link:../../packages/react-router @@ -728,6 +719,12 @@ importers: '@workleap/typescript-configs': specifier: 3.0.2 version: 3.0.2(typescript@5.2.2) + axios: + specifier: 1.5.1 + version: 1.5.1(debug@4.3.4) + msw: + specifier: 1.3.2 + version: 1.3.2(typescript@5.2.2) nodemon: specifier: 3.0.1 version: 3.0.1 @@ -2296,6 +2293,11 @@ packages: engines: {node: '>=0.1.90'} dev: true + /@colors/colors@1.6.0: + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + dev: true + /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -2719,7 +2721,7 @@ packages: ajv: 6.12.6 debug: 4.3.4(supports-color@9.4.0) espree: 9.6.1 - globals: 13.22.0 + globals: 13.23.0 ignore: 5.2.4 import-fresh: 3.3.0 js-yaml: 4.1.0 @@ -2751,8 +2753,8 @@ packages: resolution: {integrity: sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A==} dev: true - /@fastify/error@3.3.0: - resolution: {integrity: sha512-dj7vjIn1Ar8sVXj2yAXiMNCJDmS9MQ9XMlIecX2dIzzhjSHCyKo4DdXjXMs7wKW2kj6yvVRSpuQjOZ3YLrh56w==} + /@fastify/error@3.4.0: + resolution: {integrity: sha512-e/mafFwbK3MNqxUcFBLgHhgxsF8UT1m8aj0dAlqEa2nJEgPsRtpHTZ3ObgrgkZ2M1eJHPTwgyUl/tXkvabsZdQ==} dev: true /@fastify/fast-json-stringify-compiler@4.3.0: @@ -2909,7 +2911,7 @@ packages: '@types/node': 20.6.3 ansi-escapes: 4.3.2 chalk: 4.1.2 - ci-info: 3.8.0 + ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 @@ -3015,7 +3017,7 @@ packages: glob: 7.2.3 graceful-fs: 4.2.11 istanbul-lib-coverage: 3.2.0 - istanbul-lib-instrument: 6.0.0 + istanbul-lib-instrument: 6.0.1 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 4.0.1 istanbul-reports: 3.1.6 @@ -3025,7 +3027,7 @@ packages: slash: 3.0.0 string-length: 4.0.2 strip-ansi: 6.0.1 - v8-to-istanbul: 9.1.0 + v8-to-istanbul: 9.1.3 transitivePeerDependencies: - supports-color dev: true @@ -3108,7 +3110,7 @@ packages: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.2 '@types/node': 20.6.3 - '@types/yargs': 17.0.26 + '@types/yargs': 17.0.28 chalk: 4.1.2 dev: true @@ -3707,12 +3709,12 @@ packages: fastq: 1.15.0 dev: true - /@npmcli/config@6.3.0: - resolution: {integrity: sha512-gV64pm5cQ7F2oeoSJ5HTfaKxjFsvC4dAbCsQbtbOkEOymM6iZI62yNGCOLjcq/rfYX9+wVn34ThxK7GZpUwWFg==} + /@npmcli/config@6.4.0: + resolution: {integrity: sha512-/fQjIbuNVIT/PbXvw178Tm97bxV0E0nVUFKHivMKtSI2pcs8xKdaWkHJxf9dTI0G/y5hp/KuCvgcUu5HwAtI1w==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: '@npmcli/map-workspaces': 3.0.4 - ci-info: 3.8.0 + ci-info: 3.9.0 ini: 4.1.1 nopt: 7.2.0 proc-log: 3.0.0 @@ -4922,28 +4924,28 @@ packages: defer-to-connect: 2.0.1 dev: true - /@tanstack/query-core@5.0.0-rc.4: - resolution: {integrity: sha512-d5QHSpR+kmutkJfXmyaiNvchVQctuY3d3vWBbAS4M3JTEQuOt+Gg2v7oghnDG05Ks7M0pq7ofNFuAJZ3s7p1yg==} + /@tanstack/query-core@5.0.0-rc.5: + resolution: {integrity: sha512-Rht0Krzo7iYXmYTaa4YTS0OAFY4ZGFhvQ4p1t5IobUkuP9zGsFBb8EUh55GKZeRNuHb9WvLQWXCVgfY40PQYjA==} - /@tanstack/query-devtools@5.0.0-rc.1: - resolution: {integrity: sha512-33ECTglWAllVT6IfUhqvgKbJulOtOP8+z9W/i0FlmWKNaEwzaR8lpa4pOCyLCZA2p5XW97nSJ/JfPOzeYQN/jQ==} + /@tanstack/query-devtools@5.0.0-rc.5: + resolution: {integrity: sha512-1MBzK9LwKPYxzxm+4oScgxN0Cqn3af4ULsNBK5GTAG7WtxkJjUHAK9Qd6PoXlrzymSGH6xtYdFwdY1zgQ84Vpw==} dev: true - /@tanstack/react-query-devtools@5.0.0-rc.4(@tanstack/react-query@5.0.0-rc.4)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-ILdxaONmNW7hWC6S9oL99NhrBbK/Iv8juA7O3JhtdwF/DxDnnt/KihhzalRNtVLC8P/E2k5fDfwV4r/ofJ2QGQ==} + /@tanstack/react-query-devtools@5.0.0-rc.5(@tanstack/react-query@5.0.0-rc.5)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-V8VZgelfIhRDD69sF+T7PFIAKmR/T2kjXP2KdVmBt52Jh1XExAu89/MJlPrB0LuBqgHTV7ZKRhMGjEVG7+CbHA==} peerDependencies: - '@tanstack/react-query': ^5.0.0-rc.4 + '@tanstack/react-query': ^5.0.0-rc.5 react: ^18.0.0 react-dom: ^18.0.0 dependencies: - '@tanstack/query-devtools': 5.0.0-rc.1 - '@tanstack/react-query': 5.0.0-rc.4(react-dom@18.2.0)(react@18.2.0) + '@tanstack/query-devtools': 5.0.0-rc.5 + '@tanstack/react-query': 5.0.0-rc.5(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true - /@tanstack/react-query@5.0.0-rc.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-nN88B6SFvLneav7ZWegjHRhjv46SSAgGnjE/m1aeG9IcfjBGLTP32YkimoDw71F3tRFYzJyrtQ1y6FwB+UymGA==} + /@tanstack/react-query@5.0.0-rc.5(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Iwg5d88RRC1mU1U8nQnm3C7KsIYDzhbNB4EtYniuVvcLeZ3fqrFT7hzuvbR+jLgbERupn9+YpDlNXj2PmcULYA==} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 @@ -4954,7 +4956,7 @@ packages: react-native: optional: true dependencies: - '@tanstack/query-core': 5.0.0-rc.4 + '@tanstack/query-core': 5.0.0-rc.5 client-only: 0.0.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -5161,7 +5163,7 @@ packages: /@types/is-ci@3.0.1: resolution: {integrity: sha512-mnb1ngaGQPm6LFZaNdh3xPOoQMkrQb/KBPhPPN2p2Wk8XgeUqWj6xPnvyQ8rvcK/VFritVmQG8tvQuy7g+9/nQ==} dependencies: - ci-info: 3.8.0 + ci-info: 3.9.0 dev: true /@types/is-empty@1.2.1: @@ -5234,8 +5236,8 @@ packages: resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} dev: true - /@types/node@18.18.3: - resolution: {integrity: sha512-0OVfGupTl3NBFr8+iXpfZ8NR7jfFO+P1Q+IO/q0wbo02wYkP5gy36phojeYWpLQ6WAMjl+VfmqUk2YbUfp0irA==} + /@types/node@18.18.4: + resolution: {integrity: sha512-t3rNFBgJRugIhackit2mVcLfF6IRc0JE4oeizPQL8Zrm8n2WY/0wOdpOPhdtG0V9Q2TlW/axbF1MJ6z+Yj/kKQ==} dev: true /@types/node@20.6.3: @@ -5380,8 +5382,8 @@ packages: '@types/yargs-parser': 21.0.1 dev: true - /@types/yargs@17.0.26: - resolution: {integrity: sha512-Y3vDy2X6zw/ZCumcwLpdhM5L7jmyGpmBCTYMHDLqT2IKVMYRRLdv6ZakA+wxhra6Z/3bwhNbNl9bDGXaFU+6rw==} + /@types/yargs@17.0.28: + resolution: {integrity: sha512-N3e3fkS86hNhtk6BEnc0rj3zcehaxx8QWhCROJkqpl5Zaoi7nAic3jH8q94jVD3zu5LGk+PUB6KAiDmimYOEQw==} dependencies: '@types/yargs-parser': 21.0.1 dev: true @@ -5394,8 +5396,8 @@ packages: dev: true optional: true - /@typescript-eslint/eslint-plugin@6.7.4(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2): - resolution: {integrity: sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==} + /@typescript-eslint/eslint-plugin@6.7.5(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2): + resolution: {integrity: sha512-JhtAwTRhOUcP96D0Y6KYnwig/MRQbOoLGXTON2+LlyB/N35SP9j1boai2zzwXb7ypKELXMx3DVk9UTaEq1vHEw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -5407,10 +5409,10 @@ packages: dependencies: '@eslint-community/regexpp': 4.9.1 '@typescript-eslint/parser': 6.7.2(eslint@8.49.0)(typescript@5.2.2) - '@typescript-eslint/scope-manager': 6.7.4 - '@typescript-eslint/type-utils': 6.7.4(eslint@8.49.0)(typescript@5.2.2) - '@typescript-eslint/utils': 6.7.4(eslint@8.49.0)(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.7.4 + '@typescript-eslint/scope-manager': 6.7.5 + '@typescript-eslint/type-utils': 6.7.5(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.5(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.7.5 debug: 4.3.4(supports-color@9.4.0) eslint: 8.49.0 graphemer: 1.4.0 @@ -5460,16 +5462,16 @@ packages: '@typescript-eslint/visitor-keys': 6.7.2 dev: true - /@typescript-eslint/scope-manager@6.7.4: - resolution: {integrity: sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==} + /@typescript-eslint/scope-manager@6.7.5: + resolution: {integrity: sha512-GAlk3eQIwWOJeb9F7MKQ6Jbah/vx1zETSDw8likab/eFcqkjSD7BI75SDAeC5N2L0MmConMoPvTsmkrg71+B1A==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.7.4 - '@typescript-eslint/visitor-keys': 6.7.4 + '@typescript-eslint/types': 6.7.5 + '@typescript-eslint/visitor-keys': 6.7.5 dev: true - /@typescript-eslint/type-utils@6.7.4(eslint@8.49.0)(typescript@5.2.2): - resolution: {integrity: sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ==} + /@typescript-eslint/type-utils@6.7.5(eslint@8.49.0)(typescript@5.2.2): + resolution: {integrity: sha512-Gs0qos5wqxnQrvpYv+pf3XfcRXW6jiAn9zE/K+DlmYf6FcpxeNYN0AIETaPR7rHO4K2UY+D0CIbDP9Ut0U4m1g==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -5478,8 +5480,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.7.4(typescript@5.2.2) - '@typescript-eslint/utils': 6.7.4(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.7.5(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.5(eslint@8.49.0)(typescript@5.2.2) debug: 4.3.4(supports-color@9.4.0) eslint: 8.49.0 ts-api-utils: 1.0.3(typescript@5.2.2) @@ -5498,8 +5500,8 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/types@6.7.4: - resolution: {integrity: sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==} + /@typescript-eslint/types@6.7.5: + resolution: {integrity: sha512-WboQBlOXtdj1tDFPyIthpKrUb+kZf2VroLZhxKa/VlwLlLyqv/PwUNgL30BlTVZV1Wu4Asu2mMYPqarSO4L5ZQ==} engines: {node: ^16.0.0 || >=18.0.0} dev: true @@ -5545,8 +5547,8 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree@6.7.4(typescript@5.2.2): - resolution: {integrity: sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==} + /@typescript-eslint/typescript-estree@6.7.5(typescript@5.2.2): + resolution: {integrity: sha512-NhJiJ4KdtwBIxrKl0BqG1Ur+uw7FiOnOThcYx9DpOGJ/Abc9z2xNzLeirCG02Ig3vkvrc2qFLmYSSsaITbKjlg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -5554,8 +5556,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.7.4 - '@typescript-eslint/visitor-keys': 6.7.4 + '@typescript-eslint/types': 6.7.5 + '@typescript-eslint/visitor-keys': 6.7.5 debug: 4.3.4(supports-color@9.4.0) globby: 11.1.0 is-glob: 4.0.3 @@ -5586,8 +5588,8 @@ packages: - typescript dev: true - /@typescript-eslint/utils@6.7.4(eslint@8.49.0)(typescript@5.2.2): - resolution: {integrity: sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA==} + /@typescript-eslint/utils@6.7.5(eslint@8.49.0)(typescript@5.2.2): + resolution: {integrity: sha512-pfRRrH20thJbzPPlPc4j0UNGvH1PjPlhlCMq4Yx7EGjV7lvEeGX0U6MJYe8+SyFutWgSHsdbJ3BXzZccYggezA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -5595,9 +5597,9 @@ packages: '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) '@types/json-schema': 7.0.13 '@types/semver': 7.5.3 - '@typescript-eslint/scope-manager': 6.7.4 - '@typescript-eslint/types': 6.7.4 - '@typescript-eslint/typescript-estree': 6.7.4(typescript@5.2.2) + '@typescript-eslint/scope-manager': 6.7.5 + '@typescript-eslint/types': 6.7.5 + '@typescript-eslint/typescript-estree': 6.7.5(typescript@5.2.2) eslint: 8.49.0 semver: 7.5.4 transitivePeerDependencies: @@ -5621,11 +5623,11 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@typescript-eslint/visitor-keys@6.7.4: - resolution: {integrity: sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==} + /@typescript-eslint/visitor-keys@6.7.5: + resolution: {integrity: sha512-3MaWdDZtLlsexZzDSdQWsFQ9l9nL8B80Z4fImSpyllFC/KLqWQRdEcB+gGGO+N3Q2uL40EsG66wZLsohPxNXvg==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.7.4 + '@typescript-eslint/types': 6.7.5 eslint-visitor-keys: 3.4.3 dev: true @@ -5794,16 +5796,16 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 6.7.4(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/eslint-plugin': 6.7.5(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2) '@typescript-eslint/parser': 6.7.2(eslint@8.49.0)(typescript@5.2.2) eslint: 8.49.0 eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0) - eslint-plugin-jest: 27.4.2(@typescript-eslint/eslint-plugin@6.7.4)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) + eslint-plugin-jest: 27.4.2(@typescript-eslint/eslint-plugin@6.7.5)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) eslint-plugin-jsx-a11y: 6.7.1(eslint@8.49.0) eslint-plugin-mdx: 2.2.0(eslint@8.49.0) eslint-plugin-react: 7.33.2(eslint@8.49.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.49.0) - eslint-plugin-storybook: 0.6.14(eslint@8.49.0)(typescript@5.2.2) + eslint-plugin-storybook: 0.6.15(eslint@8.49.0)(typescript@5.2.2) eslint-plugin-testing-library: 6.0.2(eslint@8.49.0)(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: @@ -6936,8 +6938,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001542 - electron-to-chromium: 1.4.539 + caniuse-lite: 1.0.30001546 + electron-to-chromium: 1.4.547 node-releases: 2.0.13 update-browserslist-db: 1.0.13(browserslist@4.21.10) @@ -6946,8 +6948,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001542 - electron-to-chromium: 1.4.539 + caniuse-lite: 1.0.30001546 + electron-to-chromium: 1.4.547 node-releases: 2.0.13 update-browserslist-db: 1.0.13(browserslist@4.22.1) dev: true @@ -7066,14 +7068,14 @@ packages: engines: {node: '>=14.16'} dev: true - /cacheable-request@10.2.13: - resolution: {integrity: sha512-3SD4rrMu1msNGEtNSt8Od6enwdo//U9s4ykmXfA2TD58kcLkCobtCDiby7kNyj7a/Q7lz/mAesAFI54rTdnvBA==} + /cacheable-request@10.2.14: + resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} engines: {node: '>=14.16'} dependencies: '@types/http-cache-semantics': 4.0.2 get-stream: 6.0.1 http-cache-semantics: 4.1.1 - keyv: 4.5.3 + keyv: 4.5.4 mimic-response: 4.0.0 normalize-url: 8.0.0 responselike: 3.0.0 @@ -7129,8 +7131,8 @@ packages: engines: {node: '>=14.16'} dev: true - /caniuse-lite@1.0.30001542: - resolution: {integrity: sha512-UrtAXVcj1mvPBFQ4sKd38daP8dEcXXr5sQe6QNNinaPd0iA/cxg9/l3VrSdL73jgw5sKyuQ6jNgiKO12W3SsVA==} + /caniuse-lite@1.0.30001546: + resolution: {integrity: sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw==} /ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -7234,6 +7236,11 @@ packages: engines: {node: '>=8'} dev: true + /ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + dev: true + /cjs-module-lexer@1.2.3: resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} dev: true @@ -7605,10 +7612,6 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} - /convert-source-map@1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} - dev: true - /convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} dev: true @@ -8423,8 +8426,8 @@ packages: /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - /electron-to-chromium@1.4.539: - resolution: {integrity: sha512-wRmWJ8F7rgmINuI32S6r2SLrw/h/bJQsDSvBiq9GBfvc2Lh73qTOwn73r3Cf67mjVgFGJYcYtmERzySa5jIWlg==} + /electron-to-chromium@1.4.547: + resolution: {integrity: sha512-QC99aDjzNir+D4c1jE7QWt3RybAxBWYXBdCoaUaAumkD7nDwPLgyt03qNgpVscn18UIGJIZZbFlzNaUSwhPLiA==} /elegant-spinner@1.0.1: resolution: {integrity: sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ==} @@ -8536,7 +8539,7 @@ packages: get-symbol-description: 1.0.0 globalthis: 1.0.3 gopd: 1.0.1 - has: 1.0.3 + has: 1.0.4 has-property-descriptors: 1.0.0 has-proto: 1.0.1 has-symbols: 1.0.3 @@ -8607,14 +8610,14 @@ packages: engines: {node: '>= 0.4'} dependencies: get-intrinsic: 1.2.1 - has: 1.0.3 + has: 1.0.4 has-tostringtag: 1.0.0 dev: true /es-shim-unscopables@1.0.0: resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} dependencies: - has: 1.0.3 + has: 1.0.4 dev: true /es-to-primitive@1.2.1: @@ -8817,7 +8820,7 @@ packages: eslint: 8.49.0 eslint-import-resolver-node: 0.3.9 eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.2)(eslint-import-resolver-node@0.3.9)(eslint@8.49.0) - has: 1.0.3 + has: 1.0.4 is-core-module: 2.13.0 is-glob: 4.0.3 minimatch: 3.1.2 @@ -8832,7 +8835,7 @@ packages: - supports-color dev: true - /eslint-plugin-jest@27.4.2(@typescript-eslint/eslint-plugin@6.7.4)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2): + /eslint-plugin-jest@27.4.2(@typescript-eslint/eslint-plugin@6.7.5)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2): resolution: {integrity: sha512-3Nfvv3wbq2+PZlRTf2oaAWXWwbdBejFRBR2O8tAO67o+P8zno+QGbcDYaAXODlreXVg+9gvWhKKmG2rgfb8GEg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -8845,7 +8848,7 @@ packages: jest: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 6.7.4(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/eslint-plugin': 6.7.5(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2) '@typescript-eslint/utils': 5.62.0(eslint@8.49.0)(typescript@5.2.2) eslint: 8.49.0 jest: 29.7.0(@types/node@20.6.3)(ts-node@10.9.1) @@ -8870,7 +8873,7 @@ packages: damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 eslint: 8.49.0 - has: 1.0.3 + has: 1.0.4 jsx-ast-utils: 3.3.5 language-tags: 1.0.5 minimatch: 3.1.2 @@ -8944,8 +8947,8 @@ packages: string.prototype.matchall: 4.0.10 dev: true - /eslint-plugin-storybook@0.6.14(eslint@8.49.0)(typescript@5.2.2): - resolution: {integrity: sha512-IeYigPur/MvESNDo43Z+Z5UvlcEVnt0dDZmnw1odi9X2Th1R3bpGyOZsHXb9bp1pFecOpRUuoMG5xdID2TwwOg==} + /eslint-plugin-storybook@0.6.15(eslint@8.49.0)(typescript@5.2.2): + resolution: {integrity: sha512-lAGqVAJGob47Griu29KXYowI4G7KwMoJDOkEip8ujikuDLxU+oWJ1l0WL6F2oDO4QiyUFXvtDkEkISMOPzo+7w==} engines: {node: 12.x || 14.x || >= 16} peerDependencies: eslint: '>=6' @@ -9020,7 +9023,7 @@ packages: file-entry-cache: 6.0.1 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.22.0 + globals: 13.23.0 graphemer: 1.4.0 ignore: 5.2.4 imurmurhash: 0.1.4 @@ -9395,21 +9398,21 @@ packages: resolution: {integrity: sha512-tzuY1tgWJo2Y6qEKwmLhFvACUmr68Io2pqP/sDKU71KRM6A6R3DrCDqLGqANbeLZcKUfdfY58ut35CGqemcTgg==} dependencies: '@fastify/ajv-compiler': 3.5.0 - '@fastify/error': 3.3.0 + '@fastify/error': 3.4.0 '@fastify/fast-json-stringify-compiler': 4.3.0 abstract-logging: 2.0.1 avvio: 8.2.1 fast-content-type-parse: 1.1.0 fast-json-stringify: 5.8.0 - find-my-way: 7.6.2 + find-my-way: 7.7.0 light-my-request: 5.11.0 - pino: 8.15.3 + pino: 8.16.0 process-warning: 2.2.0 proxy-addr: 2.0.7 rfdc: 1.3.0 secure-json-parse: 2.7.0 semver: 7.5.4 - tiny-lru: 11.2.0 + tiny-lru: 11.2.2 transitivePeerDependencies: - supports-color dev: true @@ -9518,7 +9521,7 @@ packages: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: - flat-cache: 3.1.0 + flat-cache: 3.1.1 dev: true /file-type@18.5.0: @@ -9588,8 +9591,8 @@ packages: transitivePeerDependencies: - supports-color - /find-my-way@7.6.2: - resolution: {integrity: sha512-0OjHn1b1nCX3eVbm9ByeEHiscPYiHLfhei1wOUU9qffQkk98wE0Lo8VrVYfSGMgnSnDh86DxedduAnBf4nwUEw==} + /find-my-way@7.7.0: + resolution: {integrity: sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==} engines: {node: '>=14'} dependencies: fast-deep-equal: 3.1.3 @@ -9627,12 +9630,12 @@ packages: pkg-dir: 4.2.0 dev: true - /flat-cache@3.1.0: - resolution: {integrity: sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==} + /flat-cache@3.1.1: + resolution: {integrity: sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==} engines: {node: '>=12.0.0'} dependencies: flatted: 3.2.9 - keyv: 4.5.3 + keyv: 4.5.4 rimraf: 3.0.2 dev: true @@ -9837,7 +9840,7 @@ packages: resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} dependencies: function-bind: 1.1.1 - has: 1.0.3 + has: 1.0.4 has-proto: 1.0.1 has-symbols: 1.0.3 @@ -9986,8 +9989,8 @@ packages: engines: {node: '>=4'} dev: true - /globals@13.22.0: - resolution: {integrity: sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==} + /globals@13.23.0: + resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==} engines: {node: '>=8'} dependencies: type-fest: 0.20.2 @@ -10044,7 +10047,7 @@ packages: '@sindresorhus/is': 5.6.0 '@szmarczak/http-timer': 5.0.1 cacheable-lookup: 7.0.0 - cacheable-request: 10.2.13 + cacheable-request: 10.2.14 decompress-response: 6.0.0 form-data-encoder: 2.1.4 get-stream: 6.0.1 @@ -10168,11 +10171,9 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true - /has@1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + /has@1.0.4: + resolution: {integrity: sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==} engines: {node: '>= 0.4.0'} - dependencies: - function-bind: 1.1.1 /hasbin@1.2.3: resolution: {integrity: sha512-CCd8e/w2w28G8DyZvKgiHnQJ/5XXDz6qiUHnthvtag/6T5acUeN5lqq+HMoBqcmgWueWDhiCplrw0Kb1zDACRg==} @@ -10259,7 +10260,7 @@ packages: he: 1.2.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.20.0 + terser: 5.21.0 /html-webpack-plugin@5.5.3(webpack@5.88.2): resolution: {integrity: sha512-6YrDKTuqaP/TquFH7h4srYWsZx+x6k6+FbsTm0ziCwGHDP78Unr1r9F/H4+sGmMbX08GQcJ+K64x55b+7VM/jg==} @@ -10609,7 +10610,7 @@ packages: engines: {node: '>= 0.4'} dependencies: get-intrinsic: 1.2.1 - has: 1.0.3 + has: 1.0.4 side-channel: 1.0.4 dev: true @@ -10737,13 +10738,13 @@ packages: resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} hasBin: true dependencies: - ci-info: 3.8.0 + ci-info: 3.9.0 dev: true /is-core-module@2.13.0: resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} dependencies: - has: 1.0.3 + has: 1.0.4 /is-data-descriptor@0.1.4: resolution: {integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==} @@ -11162,8 +11163,8 @@ packages: - supports-color dev: true - /istanbul-lib-instrument@6.0.0: - resolution: {integrity: sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==} + /istanbul-lib-instrument@6.0.1: + resolution: {integrity: sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==} engines: {node: '>=10'} dependencies: '@babel/core': 7.23.0 @@ -11317,7 +11318,7 @@ packages: '@types/node': 20.6.3 babel-jest: 29.7.0(@babel/core@7.23.0) chalk: 4.1.2 - ci-info: 3.8.0 + ci-info: 3.9.0 deepmerge: 4.3.1 glob: 7.2.3 graceful-fs: 4.2.11 @@ -11610,7 +11611,7 @@ packages: '@jest/types': 29.6.3 '@types/node': 20.6.3 chalk: 4.1.2 - ci-info: 3.8.0 + ci-info: 3.9.0 graceful-fs: 4.2.11 picomatch: 2.3.1 dev: true @@ -11883,8 +11884,8 @@ packages: mimic-fn: 4.0.0 dev: true - /keyv@4.5.3: - resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} dependencies: json-buffer: 3.0.1 dev: true @@ -11933,7 +11934,7 @@ packages: dependencies: commander: 10.0.1 dotenv: 16.3.1 - winston: 3.10.0 + winston: 3.11.0 dev: true /language-subtag-registry@0.3.22: @@ -11953,8 +11954,8 @@ packages: package-json: 8.1.1 dev: true - /launch-editor@2.6.0: - resolution: {integrity: sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==} + /launch-editor@2.6.1: + resolution: {integrity: sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==} dependencies: picocolors: 1.0.0 shell-quote: 1.8.1 @@ -12054,7 +12055,7 @@ packages: /load-plugin@5.1.0: resolution: {integrity: sha512-Lg1CZa1CFj2CbNaxijTL6PCbzd4qGTlZov+iH2p5Xwy/ApcZJh+i6jMN2cYePouTfjJfrNu3nXFdEw8LvbjPFQ==} dependencies: - '@npmcli/config': 6.3.0 + '@npmcli/config': 6.4.0 import-meta-resolve: 2.2.2 dev: true @@ -13630,8 +13631,8 @@ packages: resolution: {integrity: sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg==} dev: true - /on-exit-leak-free@2.1.1: - resolution: {integrity: sha512-IPTBZ175tI0sSg0ikDcCDfa5dPgcFbJgABsTHsY+Mkdm6Y2VKGuchubXSvTuu5tSPl4mqt53o3nLI74HTs8UgQ==} + /on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} dev: true @@ -14113,21 +14114,21 @@ packages: resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} dev: true - /pino@8.15.3: - resolution: {integrity: sha512-wDds1+DH8VaREe4fpLEKttGnDoLiX3KR3AP5bHsrRwEZ93y+Z/HFC03zkGSxpIGWKDHg24sloVqGcIWoLCkTLQ==} + /pino@8.16.0: + resolution: {integrity: sha512-UUmvQ/7KTZt/vHjhRrnyS7h+J7qPBQnpG80V56xmIC+o9IqYmQOw/UIny9S9zYDfRBR0ClouCr464EkBMIT7Fw==} hasBin: true dependencies: atomic-sleep: 1.0.0 fast-redact: 3.3.0 - on-exit-leak-free: 2.1.1 + on-exit-leak-free: 2.1.2 pino-abstract-transport: 1.1.0 pino-std-serializers: 6.2.2 process-warning: 2.2.0 quick-format-unescaped: 4.0.4 real-require: 0.2.0 safe-stable-stringify: 2.4.3 - sonic-boom: 3.4.0 - thread-stream: 2.4.0 + sonic-boom: 3.7.0 + thread-stream: 2.4.1 dev: true /pirates@4.0.6: @@ -15476,8 +15477,8 @@ packages: uuid: 8.3.2 websocket-driver: 0.7.4 - /sonic-boom@3.4.0: - resolution: {integrity: sha512-zSe9QQW30nPzjkSJ0glFQO5T9lHsk39tz+2bAAwCj8CNgEG8ItZiX7Wb2ZgA8I04dwRGCcf1m3ABJa8AYm12Fw==} + /sonic-boom@3.7.0: + resolution: {integrity: sha512-IudtNvSqA/ObjN97tfgNmOKyDOs4dNcg4cUUsHDebqsgb8wGBBwb31LIgShNO8fye0dFI52X1+tFoKKI6Rq1Gg==} dependencies: atomic-sleep: 1.0.0 dev: true @@ -15562,7 +15563,7 @@ packages: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.15 + spdx-license-ids: 3.0.16 dev: true /spdx-exceptions@2.3.0: @@ -15573,11 +15574,11 @@ packages: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} dependencies: spdx-exceptions: 2.3.0 - spdx-license-ids: 3.0.15 + spdx-license-ids: 3.0.16 dev: true - /spdx-license-ids@3.0.15: - resolution: {integrity: sha512-lpT8hSQp9jAKp9mhtBU4Xjon8LPGBvLIuBiSVhMEtmLecTh2mO0tlqrAMp47tBXzMr13NJMQ2lf7RpQGLJ3HsQ==} + /spdx-license-ids@3.0.16: + resolution: {integrity: sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==} dev: true /spdy-transport@3.0.0: @@ -16096,7 +16097,7 @@ packages: jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.1 - terser: 5.20.0 + terser: 5.21.0 webpack: 5.88.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4) /terser-webpack-plugin@5.3.9(@swc/core@1.3.86)(webpack@5.88.2): @@ -16120,11 +16121,11 @@ packages: jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.1 - terser: 5.20.0 + terser: 5.21.0 webpack: 5.88.2(@swc/core@1.3.86)(webpack-cli@5.1.4) - /terser@5.20.0: - resolution: {integrity: sha512-e56ETryaQDyebBwJIWYB2TT6f2EZ0fL0sW/JRXNMN26zZdKi2u/E/5my5lG6jNxym6qsrVXfFRmOdV42zlAgLQ==} + /terser@5.21.0: + resolution: {integrity: sha512-WtnFKrxu9kaoXuiZFSGrcAvvBqAdmKx0SFNmVNYdJamMu9yyN3I/QF0FbH4QcqJQ+y1CJnzxGIKH0cSj+FGYRw==} engines: {node: '>=10'} hasBin: true dependencies: @@ -16163,8 +16164,8 @@ packages: any-promise: 1.3.0 dev: true - /thread-stream@2.4.0: - resolution: {integrity: sha512-xZYtOtmnA63zj04Q+F9bdEay5r47bvpo1CaNqsKi7TpoJHcotUez8Fkfo2RJWpW91lnnaApdpRbVwCWsy+ifcw==} + /thread-stream@2.4.1: + resolution: {integrity: sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==} dependencies: real-require: 0.2.0 dev: true @@ -16212,8 +16213,8 @@ packages: engines: {node: '>=4'} dev: true - /tiny-lru@11.2.0: - resolution: {integrity: sha512-DTmmE5orth9hNGwBrblQ8D+RMFHr93LawLJCBidSOX3TEFAEYCskzhAOS4UgtnKFGylkGYGX/IgtzKTAOrxy8A==} + /tiny-lru@11.2.2: + resolution: {integrity: sha512-1An82KBJOUKCfRhsk4tHe4rNblnhAUzisesWBGf/0pztaVoNYM7eJxouN+mq6WkV/qNG21AFbrsgQfvBk0X9ng==} engines: {node: '>=12'} dev: true @@ -16710,7 +16711,7 @@ packages: '@types/concat-stream': 2.0.0 '@types/debug': 4.1.9 '@types/is-empty': 1.2.1 - '@types/node': 18.18.3 + '@types/node': 18.18.4 '@types/unist': 2.0.8 concat-stream: 2.0.0 debug: 4.3.4(supports-color@9.4.0) @@ -16989,13 +16990,13 @@ packages: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true - /v8-to-istanbul@9.1.0: - resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} + /v8-to-istanbul@9.1.3: + resolution: {integrity: sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==} engines: {node: '>=10.12.0'} dependencies: '@jridgewell/trace-mapping': 0.3.19 '@types/istanbul-lib-coverage': 2.0.4 - convert-source-map: 1.9.0 + convert-source-map: 2.0.0 dev: true /validate-npm-package-license@3.0.4: @@ -17210,7 +17211,7 @@ packages: html-entities: 2.4.0 http-proxy-middleware: 2.0.6(@types/express@4.17.18) ipaddr.js: 2.1.0 - launch-editor: 2.6.0 + launch-editor: 2.6.1 open: 8.4.2 p-retry: 4.6.2 rimraf: 3.0.2 @@ -17478,11 +17479,11 @@ packages: triple-beam: 1.4.1 dev: true - /winston@3.10.0: - resolution: {integrity: sha512-nT6SIDaE9B7ZRO0u3UvdrimG0HkB7dSTAgInQnNR2SOPJ4bvq5q79+pXLftKmP52lJGW15+H5MCK0nM9D3KB/g==} + /winston@3.11.0: + resolution: {integrity: sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==} engines: {node: '>= 12.0.0'} dependencies: - '@colors/colors': 1.5.0 + '@colors/colors': 1.6.0 '@dabh/diagnostics': 2.0.3 async: 3.2.4 is-stream: 2.0.1 diff --git a/sample/another-remote-module/package.json b/sample/another-remote-module/package.json index 050bd4d48..3aed9cc89 100644 --- a/sample/another-remote-module/package.json +++ b/sample/another-remote-module/package.json @@ -40,7 +40,6 @@ "@squide/react-router": "workspace:*", "@squide/webpack-module-federation": "workspace:*", "@tanstack/react-query": "rc", - "axios": "1.5.1", "react": "18.2.0", "react-dom": "18.2.0", "react-router-dom": "6.16.0" diff --git a/sample/another-remote-module/src/dev/index.tsx b/sample/another-remote-module/src/dev/index.tsx index 07718d871..ea0f88846 100644 --- a/sample/another-remote-module/src/dev/index.tsx +++ b/sample/another-remote-module/src/dev/index.tsx @@ -5,7 +5,7 @@ import { createRoot } from "react-dom/client"; import { register as registerModule } from "../register.tsx"; import { App } from "./App.tsx"; import { registerDev } from "./register.tsx"; -import { onLogin, onLogout, sessionAccessor } from "./session.ts"; +import { sessionAccessor, sessionManager } from "./session.ts"; // Create the shell runtime. // Services, loggers and sessionAccessor could be reuse through a shared packages or faked when in isolation. @@ -16,7 +16,7 @@ const runtime = new Runtime({ // Registering the remote module as a static module because the "register" function // is local when developing in isolation. -registerLocalModules([registerShell(onLogin, onLogout), registerDev, registerModule], runtime); +registerLocalModules([registerShell(sessionManager), registerDev, registerModule], runtime); const root = createRoot(document.getElementById("root")!); diff --git a/sample/another-remote-module/src/dev/session.ts b/sample/another-remote-module/src/dev/session.ts index 628209caf..6f20a918b 100644 --- a/sample/another-remote-module/src/dev/session.ts +++ b/sample/another-remote-module/src/dev/session.ts @@ -7,17 +7,3 @@ export const sessionManager = new LocalStorageSessionManager(); export const sessionAccessor: SessionAccessorFunction = () => { return sessionManager.getSession(); }; - -export async function onLogin(username: string) { - const session: Session = { - user: { - name: username - } - }; - - sessionManager.setSession(session); -} - -export async function onLogout() { - sessionManager.clearSession(); -} diff --git a/sample/another-remote-module/webpack.build.js b/sample/another-remote-module/webpack.build.js index a70ef7594..7f80523e9 100644 --- a/sample/another-remote-module/webpack.build.js +++ b/sample/another-remote-module/webpack.build.js @@ -13,9 +13,6 @@ export default defineBuildRemoteModuleConfig(swcConfig, "remote2", publicPath, { }, "@sample/shared": { singleton: true - }, - "axios": { - singleton: true } }, environmentVariables: { diff --git a/sample/another-remote-module/webpack.dev.js b/sample/another-remote-module/webpack.dev.js index 0652870c1..bc83b72cc 100644 --- a/sample/another-remote-module/webpack.dev.js +++ b/sample/another-remote-module/webpack.dev.js @@ -15,9 +15,6 @@ if (!process.env.LOCAL) { }, "@sample/shared": { singleton: true - }, - "axios": { - singleton: true } }, environmentVariables: { diff --git a/sample/host/mocks/browser.ts b/sample/host/mocks/browser.ts index f3ec9ee76..7801e361d 100644 --- a/sample/host/mocks/browser.ts +++ b/sample/host/mocks/browser.ts @@ -1,8 +1,7 @@ import { setupWorker, type RestHandler, type StartOptions } from "msw"; -import { hostRequestHandlers } from "./handlers.ts"; export function startMsw(moduleRequestHandlers: RestHandler[], options?: StartOptions) { - const worker = setupWorker(...hostRequestHandlers, ...moduleRequestHandlers); + const worker = setupWorker(...moduleRequestHandlers); worker.start(options); } diff --git a/sample/host/mocks/handlers.ts b/sample/host/mocks/handlers.ts deleted file mode 100644 index 74969ff0f..000000000 --- a/sample/host/mocks/handlers.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { rest, type RestHandler } from "msw"; - -interface Session { - username: string; -} - -let currentSession: Session | undefined; - -function simulateDelay(delay: number) { - return new Promise(resolve => { - setTimeout(() => { - resolve(undefined); - }, delay); - }); -} - -export const hostRequestHandlers: RestHandler[] = [ - rest.post("/login", async (req, res, ctx) => { - const { username, password } = await req.json(); - - if (username !== "temp" || password !== "temp") { - return res( - ctx.status(401) - ); - } - - await simulateDelay(2000); - - currentSession = { - username: username - }; - - return res( - ctx.status(200) - ); - }), - - rest.post("/logout", async (req, res, ctx) => { - currentSession = undefined; - - return res( - ctx.status(200) - ); - }), - - rest.get("/session", async (req, res, ctx) => { - // if (!currentSession) { - // return res( - // ctx.status(401) - // ); - // } - - await simulateDelay(500); - - // return res( - // ctx.status(200), - // ctx.json(currentSession) - // ); - - return res( - ctx.status(200), - ctx.json({ - username: "John Doe" - }) - ); - }), - - rest.get("/feature-flags", (req, res, ctx) => { - return res( - ctx.status(200), - ctx.json([ - { feature: "send-message", isActive: true }, - { feature: "list-ricky-and-morty-characters", isActive: true } - ]) - ); - }) -]; diff --git a/sample/host/src/App.tsx b/sample/host/src/App.tsx index 437379226..67565bd7b 100644 --- a/sample/host/src/App.tsx +++ b/sample/host/src/App.tsx @@ -1,7 +1,7 @@ import { BackgroundColorContext, type Session } from "@sample/shared"; import { useAppRouter } from "@sample/shell"; import { useIsMswStarted } from "@squide/msw"; -import { useRuntime, type Runtime } from "@squide/react-router"; +import { useLogger, useRuntime, type Runtime } from "@squide/react-router"; import { useAreModulesReady } from "@squide/webpack-module-federation"; import axios from "axios"; import { useEffect, useState } from "react"; @@ -12,6 +12,7 @@ export function App() { const [isReady, setIsReady] = useState(false); const runtime = useRuntime() as Runtime; + const logger = useLogger(); // Re-render the app once all the remotes are registered. // Otherwise, the remotes routes won't be added to the router. @@ -21,6 +22,8 @@ export function App() { // Otherwise, the API calls will return a 404 status. const isMswStarted = useIsMswStarted(process.env.USE_MSW as unknown as boolean); + const router = useAppRouter(); + useEffect(() => { if (areModulesReady && isMswStarted) { // getActiveRouteVisibility @@ -28,7 +31,7 @@ export function App() { // const location = useLocation(); const location = window.location; - console.log("**** location: ", location); + // console.log("**** location: ", location); const matchingRoutes = matchRoutes(runtime.routes, location) ?? []; @@ -40,17 +43,26 @@ export function App() { const rootRoute = matchingRoutes.findLast(x => x.route.type === "root"); if (rootRoute!.route.visibility === "authenticated") { - axios.get("/session").then(({ data }) => { - const session: Session = { - user: { - name: data.username - } - }; - - sessionManager.setSession(session); - - setIsReady(true); - }); + logger.debug(`[shell] Fetching session data as "${location}" is a protected route.`); + + axios.get("/session") + .then(({ data }) => { + const session: Session = { + user: { + name: data.username + } + }; + + logger.debug("[shell] Loaded the user session:", session); + + sessionManager.setSession(session); + + setIsReady(true); + }) + .catch(() => { + // The authentication boundary will redirect to the login page. + setIsReady(true); + }); } else { setIsReady(true); } @@ -58,9 +70,7 @@ export function App() { throw new Error(`[shell] There's no matching route for the location: "${location}". Did you add routes to React Router without using the runtime.registerRoute() function?`); } } - }, [areModulesReady, isMswStarted, runtime.routes]); - - const router = useAppRouter(); + }, [areModulesReady, isMswStarted, runtime.routes, router, logger]); if (!isReady) { return
Loading...
; diff --git a/sample/host/src/bootstrap.tsx b/sample/host/src/bootstrap.tsx index b017e76c8..6f45b9016 100644 --- a/sample/host/src/bootstrap.tsx +++ b/sample/host/src/bootstrap.tsx @@ -8,7 +8,7 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import { App } from "./App.tsx"; import { registerHost } from "./register.tsx"; -import { onLogin, onLogout, sessionAccessor } from "./session.ts"; +import { sessionAccessor, sessionManager } from "./session.ts"; const Remotes: RemoteDefinition[] = [ { @@ -35,7 +35,7 @@ const context: AppContext = { name: "Sample app" }; -registerLocalModules([registerShell(onLogin, onLogout), registerHost, registerLocalModule], runtime, { context }); +registerLocalModules([registerShell(sessionManager), registerHost, registerLocalModule], runtime, { context }); registerRemoteModules(Remotes, runtime, { context }).then(() => { if (process.env.USE_MSW) { diff --git a/sample/host/src/session.ts b/sample/host/src/session.ts index e7c09ab6a..ae08e9374 100644 --- a/sample/host/src/session.ts +++ b/sample/host/src/session.ts @@ -1,9 +1,7 @@ -import type { Session } from "@sample/shared"; -import { InvalidCredentialsError } from "@sample/shell"; +import type { Session, SessionManager } from "@sample/shared"; import type { SessionAccessorFunction } from "@squide/react-router"; -import axios from "axios"; -export class SessionManager { +export class InMemorySessionManager implements SessionManager { #session?: Session; setSession(session: Session) { @@ -11,13 +9,13 @@ export class SessionManager { } getSession() { - // return this.#session; + return this.#session; - return { - user: { - name: "John Doe" - } - }; + // return { + // user: { + // name: "John Doe" + // } + // }; } clearSession() { @@ -25,29 +23,8 @@ export class SessionManager { } } -export const sessionManager = new SessionManager(); +export const sessionManager = new InMemorySessionManager(); export const sessionAccessor: SessionAccessorFunction = () => { return sessionManager.getSession(); }; - -export async function onLogin(username: string, password: string) { - try { - await axios.post("/login", { - username, - password - }); - } catch (error: unknown) { - if (axios.isAxiosError(error)) { - if (error.response?.status === 401) { - throw new InvalidCredentialsError(); - } - } - - throw new Error("An unknown error happened while trying to login a user"); - } -} - -export async function onLogout() { - sessionManager.clearSession(); -} diff --git a/sample/local-module/src/dev/index.tsx b/sample/local-module/src/dev/index.tsx index 900298917..11ef74b6c 100644 --- a/sample/local-module/src/dev/index.tsx +++ b/sample/local-module/src/dev/index.tsx @@ -5,7 +5,7 @@ import { createRoot } from "react-dom/client"; import { registerLocalModule } from "../register.tsx"; import { App } from "./App.tsx"; import { registerDev } from "./register.tsx"; -import { onLogin, onLogout, sessionAccessor } from "./session.ts"; +import { sessionAccessor, sessionManager } from "./session.ts"; // Create the shell runtime. // Services, loggers and sessionAccessor could be reuse through a shared packages or faked when in isolation. @@ -14,7 +14,7 @@ const runtime = new Runtime({ sessionAccessor }); -registerLocalModules([registerShell(onLogin, onLogout), registerDev, registerLocalModule], runtime); +registerLocalModules([registerShell(sessionManager), registerDev, registerLocalModule], runtime); const root = createRoot(document.getElementById("root")!); diff --git a/sample/local-module/src/dev/session.ts b/sample/local-module/src/dev/session.ts index 628209caf..6f20a918b 100644 --- a/sample/local-module/src/dev/session.ts +++ b/sample/local-module/src/dev/session.ts @@ -7,17 +7,3 @@ export const sessionManager = new LocalStorageSessionManager(); export const sessionAccessor: SessionAccessorFunction = () => { return sessionManager.getSession(); }; - -export async function onLogin(username: string) { - const session: Session = { - user: { - name: username - } - }; - - sessionManager.setSession(session); -} - -export async function onLogout() { - sessionManager.clearSession(); -} diff --git a/sample/local-module/src/register.tsx b/sample/local-module/src/register.tsx index 839f934b9..5d188defa 100644 --- a/sample/local-module/src/register.tsx +++ b/sample/local-module/src/register.tsx @@ -30,7 +30,7 @@ export const registerLocalModule: ModuleRegisterFunction = } }); - /////// + // Register federated tabs. runtime.registerRoute({ index: true, diff --git a/sample/remote-module/package.json b/sample/remote-module/package.json index c5bb14cb1..8f2c7a98c 100644 --- a/sample/remote-module/package.json +++ b/sample/remote-module/package.json @@ -44,7 +44,6 @@ "@squide/react-router": "workspace:*", "@squide/webpack-module-federation": "workspace:*", "@tanstack/react-query": "rc", - "axios": "1.5.1", "react": "18.2.0", "react-dom": "18.2.0", "react-router-dom": "6.16.0" diff --git a/sample/remote-module/src/dev/index.tsx b/sample/remote-module/src/dev/index.tsx index 8d6a7a5fb..6980bda5d 100644 --- a/sample/remote-module/src/dev/index.tsx +++ b/sample/remote-module/src/dev/index.tsx @@ -6,7 +6,7 @@ import { createRoot } from "react-dom/client"; import { register as registerModule } from "../register.tsx"; import { App } from "./App.tsx"; import { registerDev } from "./register.tsx"; -import { onLogin, onLogout, sessionAccessor } from "./session.ts"; +import { sessionAccessor, sessionManager } from "./session.ts"; const mswPlugin = new MswPlugin(); @@ -20,7 +20,7 @@ const runtime = new Runtime({ // Registering the remote module as a static module because the "register" function // is local when developing in isolation. -registerLocalModules([registerShell(onLogin, onLogout), registerDev, registerModule], runtime); +registerLocalModules([registerShell(sessionManager), registerDev, registerModule], runtime); // Register MSW after the local modules has been registered since the request handlers // will be registered by the modules. diff --git a/sample/remote-module/src/dev/session.ts b/sample/remote-module/src/dev/session.ts index 628209caf..6f20a918b 100644 --- a/sample/remote-module/src/dev/session.ts +++ b/sample/remote-module/src/dev/session.ts @@ -7,17 +7,3 @@ export const sessionManager = new LocalStorageSessionManager(); export const sessionAccessor: SessionAccessorFunction = () => { return sessionManager.getSession(); }; - -export async function onLogin(username: string) { - const session: Session = { - user: { - name: username - } - }; - - sessionManager.setSession(session); -} - -export async function onLogout() { - sessionManager.clearSession(); -} diff --git a/sample/remote-module/src/register.tsx b/sample/remote-module/src/register.tsx index f820bfed2..b326275a9 100644 --- a/sample/remote-module/src/register.tsx +++ b/sample/remote-module/src/register.tsx @@ -3,7 +3,7 @@ import { getMswPlugin } from "@squide/msw"; import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { requestHandlers } from "../mocks/handlers.ts"; -export const register: ModuleRegisterFunction = runtime => { +function registerRoutes(runtime: Runtime) { runtime.registerRoute({ path: "/remote", lazy: async () => import("./Remote.tsx") @@ -23,7 +23,6 @@ export const register: ModuleRegisterFunction = runtime => { }); runtime.registerRoute({ - hoist: true, path: "/hoisted", lazy: () => import("./CustomLayout.tsx"), children: [ @@ -32,6 +31,8 @@ export const register: ModuleRegisterFunction = runtime => { lazy: () => import("./Hoisted.tsx") } ] + }, { + hoist: true }); runtime.registerRoute({ @@ -123,9 +124,15 @@ export const register: ModuleRegisterFunction = runtime => { }, { menuId: "/federated-tabs" }); +} - // Register request handlers for MSW. - +function registerMsw(runtime: Runtime) { const mswPlugin = getMswPlugin(runtime); + mswPlugin.registerRequestHandlers(requestHandlers); +} + +export const register: ModuleRegisterFunction = runtime => { + registerRoutes(runtime); + registerMsw(runtime); }; diff --git a/sample/remote-module/webpack.build.js b/sample/remote-module/webpack.build.js index bf8e8bfe1..84e40ed0a 100644 --- a/sample/remote-module/webpack.build.js +++ b/sample/remote-module/webpack.build.js @@ -13,9 +13,6 @@ export default defineBuildRemoteModuleConfig(swcConfig, "remote1", publicPath, { }, "@sample/shared": { singleton: true - }, - "axios": { - singleton: true } }, environmentVariables: { diff --git a/sample/remote-module/webpack.dev.js b/sample/remote-module/webpack.dev.js index c26c97a81..440851bca 100644 --- a/sample/remote-module/webpack.dev.js +++ b/sample/remote-module/webpack.dev.js @@ -15,9 +15,6 @@ if (!process.env.LOCAL) { }, "@sample/shared": { singleton: true - }, - "axios": { - singleton: true } }, environmentVariables: { diff --git a/sample/shared/src/session.ts b/sample/shared/src/session.ts index c3a3b6e50..8c3f4633c 100644 --- a/sample/shared/src/session.ts +++ b/sample/shared/src/session.ts @@ -3,3 +3,9 @@ export interface Session { name: string; }; } + +export interface SessionManager { + setSession: (session: Session) => void; + getSession: () => Session | undefined; + clearSession: () => void; +} diff --git a/sample/shell/mocks/authenticationHandlers.ts b/sample/shell/mocks/authenticationHandlers.ts new file mode 100644 index 000000000..c53547b8f --- /dev/null +++ b/sample/shell/mocks/authenticationHandlers.ts @@ -0,0 +1,58 @@ +import { rest, type RestHandler } from "msw"; + +const SessionKey = "msw-session"; + +function simulateDelay(delay: number) { + return new Promise(resolve => { + setTimeout(() => { + resolve(undefined); + }, delay); + }); +} + +export const authenticationHandlers: RestHandler[] = [ + rest.post("/login", async (req, res, ctx) => { + const { username, password } = await req.json(); + + if (username !== "temp" || password !== "temp") { + return res( + ctx.status(401) + ); + } + + await simulateDelay(2000); + + window.localStorage.setItem(SessionKey, JSON.stringify({ + username + })); + + return res( + ctx.status(200) + ); + }), + + rest.post("/logout", async (req, res, ctx) => { + window.localStorage.removeItem(SessionKey); + + return res( + ctx.status(200) + ); + }), + + rest.get("/session", async (req, res, ctx) => { + const session = window.localStorage.getItem(SessionKey); + + if (!session) { + return res( + ctx.status(401) + ); + } + + await simulateDelay(500); + + return res( + ctx.status(200), + ctx.json(JSON.parse(session)) + ); + }) +]; diff --git a/sample/shell/package.json b/sample/shell/package.json index 6f88cb2a0..3c20edd44 100644 --- a/sample/shell/package.json +++ b/sample/shell/package.json @@ -22,6 +22,9 @@ "peerDependencies": { "@sample/shared": "*", "@squide/react-router": "*", + "@squide/msw": "*", + "axios": "*", + "msw": "*", "react": "*", "react-dom": "*", "react-router-dom": "*" @@ -30,11 +33,14 @@ "@remix-run/router": "1.9.0", "@sample/shared": "workspace:*", "@squide/react-router": "workspace:*", + "@squide/msw": "workspace:*", "@types/react": "18.2.22", "@types/react-dom": "18.2.7", "@workleap/eslint-plugin": "2.1.1", "@workleap/tsup-configs": "3.0.1", "@workleap/typescript-configs": "3.0.2", + "axios": "1.5.1", + "msw": "1.3.2", "nodemon": "3.0.1", "react": "18.2.0", "react-dom": "18.2.0", diff --git a/sample/shell/src/AuthenticatedLayout.tsx b/sample/shell/src/AuthenticatedLayout.tsx index b5c25a3b4..befa2816e 100644 --- a/sample/shell/src/AuthenticatedLayout.tsx +++ b/sample/shell/src/AuthenticatedLayout.tsx @@ -1,7 +1,8 @@ -import { useApplicationEventBusListener, type Session } from "@sample/shared"; -import { isNavigationLink, useNavigationItems, useRenderedNavigationItems, useSession, type NavigationLinkRenderProps, type NavigationSectionRenderProps, type RenderItemFunction, type RenderSectionFunction } from "@squide/react-router"; +import { useApplicationEventBusListener, type Session, type SessionManager } from "@sample/shared"; +import { isNavigationLink, useLogger, useNavigationItems, useRenderedNavigationItems, useSession, type NavigationLinkRenderProps, type NavigationSectionRenderProps, type RenderItemFunction, type RenderSectionFunction } from "@squide/react-router"; +import axios from "axios"; import { Suspense, useCallback, type ReactNode } from "react"; -import { Link, Outlet } from "react-router-dom"; +import { Link, Outlet, useNavigate } from "react-router-dom"; type RenderLinkItemFunction = (item: NavigationLinkRenderProps, index: number, level: number) => ReactNode; @@ -40,15 +41,36 @@ const renderSection: RenderSectionFunction = (elements, index, level) => { ); }; -export function AuthenticatedLayout() { +export interface AuthenticatedLayoutProps { + sessionManager: SessionManager; +} + +export function AuthenticatedLayout({ sessionManager }: AuthenticatedLayoutProps) { + const logger = useLogger(); const session = useSession() as Session; + const navigate = useNavigate(); + const handleModulesMessage = useCallback((data: unknown) => { console.log("[sample] Message received from a module: ", data); }, []); useApplicationEventBusListener("write-to-host", handleModulesMessage); + const onDisconnect = useCallback(async () => { + axios.post("/logout") + .then(() => { + sessionManager.clearSession(); + + logger.debug("[shell] The user session has been cleared."); + + // navigate("/logout"); + }) + .catch(() => { + throw new Error("An unknown error happened while disconnecting the user."); + }); + }, [logger, navigate, sessionManager]); + const navigationItems = useNavigationItems(); const renderedNavigationItems = useRenderedNavigationItems(navigationItems, renderItem, renderSection); @@ -63,7 +85,7 @@ export function AuthenticatedLayout() { (User: {session.user.name})
- Disconnect +
Loading...}> diff --git a/sample/shell/src/AuthenticationBoundary.tsx b/sample/shell/src/AuthenticationBoundary.tsx index 70181e056..36f89373e 100644 --- a/sample/shell/src/AuthenticationBoundary.tsx +++ b/sample/shell/src/AuthenticationBoundary.tsx @@ -1,6 +1,14 @@ -import { useIsAuthenticated } from "@squide/react-router"; +import { useIsAuthenticated, useLogger } from "@squide/react-router"; import { Navigate, Outlet } from "react-router-dom"; export function AuthenticationBoundary() { - return useIsAuthenticated() ? : ; + const logger = useLogger(); + + if (useIsAuthenticated()) { + return ; + } + + logger.debug("[shell] The user is not authenticated, redirecting to the login page."); + + return ; } diff --git a/sample/shell/src/Login.tsx b/sample/shell/src/Login.tsx index 73f482776..e2d16f728 100644 --- a/sample/shell/src/Login.tsx +++ b/sample/shell/src/Login.tsx @@ -1,50 +1,39 @@ import { useIsAuthenticated } from "@squide/react-router"; +import axios from "axios"; import { useCallback, useState, type ChangeEvent, type MouseEvent } from "react"; -import { Navigate, useNavigate } from "react-router-dom"; +import { Navigate } from "react-router-dom"; -export type OnLoginHandler = (username: string, password: string) => Promise; - -export class InvalidCredentialsError extends Error { - constructor(message: string = "") { - super(message); - this.name = "InvalidCredentialsError"; - } -} - -export interface LoginProps { - onLogin?: OnLoginHandler; -} - -export function Login({ onLogin }: LoginProps) { +export function Login() { const [username, setUserName] = useState(""); const [password, setPassword] = useState(""); const [errorMessage, setErrorMessage] = useState(); const [isBusy, setIsBusy] = useState(false); - const navigate = useNavigate(); - - const handleClick = useCallback(async (event: MouseEvent) => { + const handleClick = useCallback((event: MouseEvent) => { event.preventDefault(); - try { - setIsBusy(true); - setErrorMessage(undefined); + setIsBusy(true); + setErrorMessage(undefined); - if (onLogin) { - await onLogin(username, password); - } + axios.post("/login", { username, password }) + .then(() => { + setIsBusy(false); - navigate("/"); - } catch (error: unknown) { - setIsBusy(false); + // Reloading the application so the App.tsx code is runned again. + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + window.location = "/"; + }) + .catch((error: unknown) => { + setIsBusy(false); - if (error instanceof InvalidCredentialsError) { - setErrorMessage("Invalid credentials, please try again."); - } else { - setErrorMessage("An unknown error occured while trying to log you in, please try again."); - } - } - }, [username, password, onLogin, navigate]); + if (axios.isAxiosError(error) && error.response?.status === 401) { + setErrorMessage("Invalid credentials, please try again."); + } else { + throw error; + } + }); + }, [username, password]); const handleUserNameChange = useCallback((event: ChangeEvent) => { setUserName(event.target.value); diff --git a/sample/shell/src/Logout.tsx b/sample/shell/src/Logout.tsx index 18c01c9cf..7af5e47cd 100644 --- a/sample/shell/src/Logout.tsx +++ b/sample/shell/src/Logout.tsx @@ -1,16 +1,6 @@ import { Link } from "react-router-dom"; -export type onLogoutHandler = () => Promise; - -export interface LogoutProps { - onLogout?: onLogoutHandler; -} - -export function Logout({ onLogout }: LogoutProps) { - if (onLogout) { - onLogout(); - } - +export function Logout() { return (

Logged out

diff --git a/sample/shell/src/register.tsx b/sample/shell/src/register.tsx index a26def052..a10e4c5e3 100644 --- a/sample/shell/src/register.tsx +++ b/sample/shell/src/register.tsx @@ -1,94 +1,119 @@ +import type { SessionManager } from "@sample/shared"; +import { getMswPlugin } from "@squide/msw"; import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { ManagedRoutes } from "@squide/react-router"; -import type { OnLoginHandler } from "./Login.tsx"; -import type { onLogoutHandler } from "./Logout.tsx"; +import { authenticationHandlers } from "../mocks/authenticationHandlers.ts"; import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; import { RootLayout } from "./RootLayout.tsx"; -export function registerShell(onLogin: OnLoginHandler, onLogout: onLogoutHandler) { - const register: ModuleRegisterFunction = runtime => { - runtime.registerRoute({ - // Pathless route to declare a root layout and a root error boundary. - hoist: true, - element: , - children: [ - { - // Public pages like the login and logout pages will be rendered under this pathless route. - name: "root-error-boundary", - errorElement: , - children: [ - { - // Pathless route to declare an authenticated layout. - lazy: async () => { - const { AuthenticatedLayout } = await import("./AuthenticatedLayout.tsx"); - - return { - element: - }; - }, - children: [ - { - // Pathless route to declare an error boundary inside the layout instead of outside. - // It's quite useful to prevent losing the layout when an unmanaged error occurs. - lazy: async () => { - const { ModuleErrorBoundary } = await import("./ModuleErrorBoundary.tsx"); +function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { + runtime.registerRoute({ + // Pathless route to declare a root layout and a root error boundary. + element: , + children: [ + { + // Public pages like the login and logout pages will be rendered under this pathless route. + name: "root-error-boundary", + errorElement: , + children: [ + { + // Pathless route to declare an authenticated boundary. + lazy: async () => { + const { AuthenticationBoundary } = await import("./AuthenticationBoundary.tsx"); - return { - errorElement: - }; - }, - children: [ - ManagedRoutes - ] - } - ] - } - ] - } - ] - }); + return { + element: + }; + }, + children: [ + { + // Pathless route to declare an authenticated layout. + lazy: async () => { + const { AuthenticatedLayout } = await import("./AuthenticatedLayout.tsx"); - runtime.registerRoute({ - visibility: "public", - path: "/login", - lazy: async () => { - const { Login } = await import("./Login.tsx"); + return { + element: + }; + }, + children: [ + { + // Pathless route to declare an error boundary inside the layout instead of outside. + // It's quite useful to prevent losing the layout when an unmanaged error occurs. + lazy: async () => { + const { ModuleErrorBoundary } = await import("./ModuleErrorBoundary.tsx"); - return { - element: - }; + return { + errorElement: + }; + }, + children: [ + ManagedRoutes + ] + } + ] + } + ] + } + ] } - }, { - parentName: "root-error-boundary" - }); + ] + }, { + hoist: true + }); - runtime.registerRoute({ - visibility: "public", - path: "/logout", - lazy: async () => { - const { Logout } = await import("./Logout.tsx"); + runtime.registerRoute({ + visibility: "public", + path: "/login", + lazy: async () => { + const { Login } = await import("./Login.tsx"); - return { - element: - }; - } - }, { - parentName: "root-error-boundary" - }); + return { + element: + }; + } + }, { + parentName: "root-error-boundary" + }); - runtime.registerRoute({ - visibility: "public", - path: "*", - lazy: async () => { - const { NoMatch } = await import("./NoMatch.tsx"); + runtime.registerRoute({ + visibility: "public", + path: "/logout", + lazy: async () => { + const { Logout } = await import("./Logout.tsx"); - return { - element: - }; - } - }, { - parentName: "root-error-boundary" - }); + return { + element: + }; + } + }, { + parentName: "root-error-boundary" + }); + + runtime.registerRoute({ + visibility: "public", + path: "*", + lazy: async () => { + const { NoMatch } = await import("./NoMatch.tsx"); + + return { + element: + }; + } + }, { + parentName: "root-error-boundary" + }); +} + +function registerMsw(runtime: Runtime) { + const mswPlugin = getMswPlugin(runtime); + + mswPlugin.registerRequestHandlers(authenticationHandlers); +} + +export function registerShell(sessionManager: SessionManager) { + const register: ModuleRegisterFunction = runtime => { + registerRoutes(runtime, sessionManager); + registerMsw(runtime); }; return register; From 04aeafbf17a5587c50eb3d6fb1e0e13311441e83 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Tue, 10 Oct 2023 11:54:33 -0400 Subject: [PATCH 07/51] Fixing disconnect button --- pnpm-lock.yaml | 115 ++++++++++++----------- sample/host/webpack.build.js | 4 - sample/host/webpack.dev.js | 4 - sample/shell/src/AuthenticatedLayout.tsx | 10 +- 4 files changed, 67 insertions(+), 66 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cb02006bd..c908b74c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -309,7 +309,7 @@ importers: version: link:../../packages/webpack-module-federation '@tanstack/react-query': specifier: rc - version: 5.0.0-rc.5(react-dom@18.2.0)(react@18.2.0) + version: 5.0.0-rc.7(react-dom@18.2.0)(react@18.2.0) react: specifier: 18.2.0 version: 18.2.0 @@ -328,7 +328,7 @@ importers: version: 0.5.2 '@tanstack/react-query-devtools': specifier: rc - version: 5.0.0-rc.5(@tanstack/react-query@5.0.0-rc.5)(react-dom@18.2.0)(react@18.2.0) + version: 5.0.0-rc.7(@tanstack/react-query@5.0.0-rc.7)(react-dom@18.2.0)(react@18.2.0) '@types/react': specifier: 18.2.22 version: 18.2.22 @@ -576,7 +576,7 @@ importers: version: link:../../packages/webpack-module-federation '@tanstack/react-query': specifier: rc - version: 5.0.0-rc.5(react-dom@18.2.0)(react@18.2.0) + version: 5.0.0-rc.7(react-dom@18.2.0)(react@18.2.0) react: specifier: 18.2.0 version: 18.2.0 @@ -595,7 +595,7 @@ importers: version: 0.5.2 '@tanstack/react-query-devtools': specifier: rc - version: 5.0.0-rc.5(@tanstack/react-query@5.0.0-rc.5)(react-dom@18.2.0)(react@18.2.0) + version: 5.0.0-rc.7(@tanstack/react-query@5.0.0-rc.7)(react-dom@18.2.0)(react@18.2.0) '@types/react': specifier: 18.2.22 version: 18.2.22 @@ -2810,15 +2810,15 @@ packages: dependencies: '@grpc/grpc-js': 1.9.5 '@opentelemetry/api': 1.6.0 - '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) + '@opentelemetry/core': 1.17.1(@opentelemetry/api@1.6.0) '@opentelemetry/exporter-metrics-otlp-grpc': 0.41.2(@opentelemetry/api@1.6.0) '@opentelemetry/exporter-metrics-otlp-proto': 0.39.1(@opentelemetry/api@1.6.0) '@opentelemetry/exporter-trace-otlp-grpc': 0.41.2(@opentelemetry/api@1.6.0) '@opentelemetry/exporter-trace-otlp-proto': 0.41.2(@opentelemetry/api@1.6.0) - '@opentelemetry/resources': 1.17.0(@opentelemetry/api@1.6.0) - '@opentelemetry/sdk-metrics': 1.17.0(@opentelemetry/api@1.6.0) + '@opentelemetry/resources': 1.17.1(@opentelemetry/api@1.6.0) + '@opentelemetry/sdk-metrics': 1.17.1(@opentelemetry/api@1.6.0) '@opentelemetry/sdk-node': 0.39.1(@opentelemetry/api@1.6.0)(supports-color@9.4.0) - '@opentelemetry/sdk-trace-base': 1.17.0(@opentelemetry/api@1.6.0) + '@opentelemetry/sdk-trace-base': 1.17.1(@opentelemetry/api@1.6.0) axios: 1.5.1(debug@4.3.4) transitivePeerDependencies: - debug @@ -3253,7 +3253,7 @@ packages: '@netlify/config': 20.9.0 '@netlify/edge-bundler': 8.20.0 '@netlify/framework-info': 9.8.10 - '@netlify/functions-utils': 5.2.30(supports-color@9.4.0) + '@netlify/functions-utils': 5.2.31(supports-color@9.4.0) '@netlify/git-utils': 5.1.1 '@netlify/plugins-list': 6.71.0 '@netlify/run-utils': 5.1.1 @@ -3427,11 +3427,11 @@ packages: semver: 7.5.4 dev: true - /@netlify/functions-utils@5.2.30(supports-color@9.4.0): - resolution: {integrity: sha512-p2TyQxdCJjj6gXyhwTAEtkGpAsquCzjQ8Cm7LmS5Gt/Z6BqsmugxXictFHTh/BJvX3UJLxtGlyiExOw9SeWlWQ==} + /@netlify/functions-utils@5.2.31(supports-color@9.4.0): + resolution: {integrity: sha512-EPYizFLstBKw7+p7KNEJu2AcnM5ofHki1CZ5rEkvILuwvIyYnViwQGHWihmbJI8COlHdXj9F9cxUXn0vkSEKOw==} engines: {node: ^14.16.0 || >=16.0.0} dependencies: - '@netlify/zip-it-and-ship-it': 9.19.0(supports-color@9.4.0) + '@netlify/zip-it-and-ship-it': 9.23.0(supports-color@9.4.0) cpy: 9.0.1 path-exists: 5.0.0 transitivePeerDependencies: @@ -3604,6 +3604,14 @@ packages: urlpattern-polyfill: 8.0.2 dev: true + /@netlify/serverless-functions-api@1.8.0: + resolution: {integrity: sha512-+dsowkoEA+LF4wS9kKafToHNSace7MxD2q3pgBik3N8UjAXBZo7J9t/E7rpkcm5w2ZXklvrDM815bOrzfDE5Jg==} + engines: {node: ^14.18.0 || >=16.0.0} + dependencies: + '@netlify/node-cookies': 0.1.0 + urlpattern-polyfill: 8.0.2 + dev: true + /@netlify/zip-it-and-ship-it@9.18.1(supports-color@9.4.0): resolution: {integrity: sha512-XOIqPHUSe5Tstzf4QUrEaCm9dH0QGpm8M3efKcRQr8IXKM10zNH7SZ43ovyv3GxULpvbndLUtGi4bdARaMxAxw==} engines: {node: ^14.18.0 || >=16.0.0} @@ -3646,14 +3654,14 @@ packages: - supports-color dev: true - /@netlify/zip-it-and-ship-it@9.19.0(supports-color@9.4.0): - resolution: {integrity: sha512-2SAFndmls1+BSsl8pMazGehscAM+pY89kHVshQl5i4UR99HGb8/NPSbnJvG0ECZmv9EONZ3fUrzGQUku1i21Tg==} + /@netlify/zip-it-and-ship-it@9.23.0(supports-color@9.4.0): + resolution: {integrity: sha512-4Khr9U3VKCEpK13zykbP8g2EgK58c7okRAyC0C+7lU5oopwp5K4lkKvGmZCaNyls3hhjXaX/wLv6K+t1NOIGSw==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true dependencies: '@babel/parser': 7.23.0 '@netlify/binary-info': 1.0.0 - '@netlify/serverless-functions-api': 1.7.3 + '@netlify/serverless-functions-api': 1.8.0 '@vercel/nft': 0.23.1(supports-color@9.4.0) archiver: 6.0.1 common-path-prefix: 3.0.0 @@ -3663,7 +3671,6 @@ packages: execa: 6.1.0 filter-obj: 5.1.0 find-up: 6.3.0 - get-tsconfig: 4.7.2 glob: 8.1.0 is-builtin-module: 3.2.1 is-path-inside: 4.0.0 @@ -3914,14 +3921,14 @@ packages: '@opentelemetry/semantic-conventions': 1.15.2 dev: true - /@opentelemetry/core@1.17.0(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-tfnl3h+UefCgx1aeN2xtrmr6BmdWGKXypk0pflQR0urFS40aE88trnkOMc2HTJZbMrqEEl4HsaBeFhwLVXsrJg==} + /@opentelemetry/core@1.17.1(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-I6LrZvl1FF97FQXPR0iieWQmKnGxYtMbWA1GrAXnLUR+B1Hn2m8KqQNEIlZAucyv00GBgpWkpllmULmZfG8P3g==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.7.0' dependencies: '@opentelemetry/api': 1.6.0 - '@opentelemetry/semantic-conventions': 1.17.0 + '@opentelemetry/semantic-conventions': 1.17.1 dev: true /@opentelemetry/exporter-jaeger@1.13.0(@opentelemetry/api@1.6.0): @@ -4240,15 +4247,15 @@ packages: '@opentelemetry/semantic-conventions': 1.15.2 dev: true - /@opentelemetry/resources@1.17.0(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-+u0ciVnj8lhuL/qGRBPeVYvk7fL+H/vOddfvmOeJaA1KC+5/3UED1c9KoZQlRsNT5Kw1FaK8LkY2NVLYfOVZQw==} + /@opentelemetry/resources@1.17.1(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-M2e5emqg5I7qRKqlzKx0ROkcPyF8PbcSaWEdsm72od9txP7Z/Pl8PDYOyu80xWvbHAWk5mDxOF6v3vNdifzclA==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.7.0' dependencies: '@opentelemetry/api': 1.6.0 - '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) - '@opentelemetry/semantic-conventions': 1.17.0 + '@opentelemetry/core': 1.17.1(@opentelemetry/api@1.6.0) + '@opentelemetry/semantic-conventions': 1.17.1 dev: true /@opentelemetry/sdk-logs@0.39.1(@opentelemetry/api-logs@0.39.1)(@opentelemetry/api@1.6.0): @@ -4301,15 +4308,15 @@ packages: lodash.merge: 4.6.2 dev: true - /@opentelemetry/sdk-metrics@1.17.0(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-HlWM27yGmYuwCoVRe3yg2PqKnIsq0kEF0HQgvkeDWz2NYkq9fFaSspR6kvjxUTbghAlZrabiqbgyKoYpYaXS3w==} + /@opentelemetry/sdk-metrics@1.17.1(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-eHdpsMCKhKhwznxvEfls8Wv3y4ZBWkkXlD3m7vtHIiWBqsMHspWSfie1s07mM45i/bBCf6YBMgz17FUxIXwmZA==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.7.0' dependencies: '@opentelemetry/api': 1.6.0 - '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) - '@opentelemetry/resources': 1.17.0(@opentelemetry/api@1.6.0) + '@opentelemetry/core': 1.17.1(@opentelemetry/api@1.6.0) + '@opentelemetry/resources': 1.17.1(@opentelemetry/api@1.6.0) lodash.merge: 4.6.2 dev: true @@ -4360,16 +4367,16 @@ packages: '@opentelemetry/semantic-conventions': 1.15.2 dev: true - /@opentelemetry/sdk-trace-base@1.17.0(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-2T5HA1/1iE36Q9eg6D4zYlC4Y4GcycI1J6NsHPKZY9oWfAxWsoYnRlkPfUqyY5XVtocCo/xHpnJvGNHwzT70oQ==} + /@opentelemetry/sdk-trace-base@1.17.1(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-pfSJJSjZj5jkCJUQZicSpzN8Iz9UKMryPWikZRGObPnJo6cUSoKkjZh6BM3j+D47G4olMBN+YZKYqkFM1L6zNA==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.7.0' dependencies: '@opentelemetry/api': 1.6.0 - '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) - '@opentelemetry/resources': 1.17.0(@opentelemetry/api@1.6.0) - '@opentelemetry/semantic-conventions': 1.17.0 + '@opentelemetry/core': 1.17.1(@opentelemetry/api@1.6.0) + '@opentelemetry/resources': 1.17.1(@opentelemetry/api@1.6.0) + '@opentelemetry/semantic-conventions': 1.17.1 dev: true /@opentelemetry/sdk-trace-node@1.13.0(@opentelemetry/api@1.6.0): @@ -4397,8 +4404,8 @@ packages: engines: {node: '>=14'} dev: true - /@opentelemetry/semantic-conventions@1.17.0: - resolution: {integrity: sha512-+fguCd2d8d2qruk0H0DsCEy2CTK3t0Tugg7MhZ/UQMvmewbZLNnJ6heSYyzIZWG5IPfAXzoj4f4F/qpM7l4VBA==} + /@opentelemetry/semantic-conventions@1.17.1: + resolution: {integrity: sha512-xbR2U+2YjauIuo42qmE8XyJK6dYeRMLJuOlUP5SO4auET4VtOHOzgkRVOq+Ik18N+Xf3YPcqJs9dZMiDddz1eQ==} engines: {node: '>=14'} dev: true @@ -4924,28 +4931,28 @@ packages: defer-to-connect: 2.0.1 dev: true - /@tanstack/query-core@5.0.0-rc.5: - resolution: {integrity: sha512-Rht0Krzo7iYXmYTaa4YTS0OAFY4ZGFhvQ4p1t5IobUkuP9zGsFBb8EUh55GKZeRNuHb9WvLQWXCVgfY40PQYjA==} + /@tanstack/query-core@5.0.0-rc.6: + resolution: {integrity: sha512-aBGionFwNoPsxnOZPVDIWOStRFWh0/jJgLgC238XyjoML/gN+pwcGNvhp995Pt+uOZPhw5XemzoKCVX9Vpq9KA==} /@tanstack/query-devtools@5.0.0-rc.5: resolution: {integrity: sha512-1MBzK9LwKPYxzxm+4oScgxN0Cqn3af4ULsNBK5GTAG7WtxkJjUHAK9Qd6PoXlrzymSGH6xtYdFwdY1zgQ84Vpw==} dev: true - /@tanstack/react-query-devtools@5.0.0-rc.5(@tanstack/react-query@5.0.0-rc.5)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-V8VZgelfIhRDD69sF+T7PFIAKmR/T2kjXP2KdVmBt52Jh1XExAu89/MJlPrB0LuBqgHTV7ZKRhMGjEVG7+CbHA==} + /@tanstack/react-query-devtools@5.0.0-rc.7(@tanstack/react-query@5.0.0-rc.7)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-ZnqHgLH5WicurJcs32+ThwppnSztv9oQp+qGWl/5nfAQh/yt2d+bH0VcqbAx7wKynjA8Vg/LXBSdiVhHsy30gA==} peerDependencies: - '@tanstack/react-query': ^5.0.0-rc.5 + '@tanstack/react-query': ^5.0.0-rc.7 react: ^18.0.0 react-dom: ^18.0.0 dependencies: '@tanstack/query-devtools': 5.0.0-rc.5 - '@tanstack/react-query': 5.0.0-rc.5(react-dom@18.2.0)(react@18.2.0) + '@tanstack/react-query': 5.0.0-rc.7(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true - /@tanstack/react-query@5.0.0-rc.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-Iwg5d88RRC1mU1U8nQnm3C7KsIYDzhbNB4EtYniuVvcLeZ3fqrFT7hzuvbR+jLgbERupn9+YpDlNXj2PmcULYA==} + /@tanstack/react-query@5.0.0-rc.7(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-LajFlMMj4CYGgO1KgSVaF1voznOqXUzhDNc2weRlS3Ua9vncTcl3JMh4HO7fReeOGogn6X/1JGILXh7CyrXwOQ==} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 @@ -4956,7 +4963,7 @@ packages: react-native: optional: true dependencies: - '@tanstack/query-core': 5.0.0-rc.5 + '@tanstack/query-core': 5.0.0-rc.6 client-only: 0.0.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -6938,8 +6945,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001546 - electron-to-chromium: 1.4.547 + caniuse-lite: 1.0.30001547 + electron-to-chromium: 1.4.548 node-releases: 2.0.13 update-browserslist-db: 1.0.13(browserslist@4.21.10) @@ -6948,8 +6955,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001546 - electron-to-chromium: 1.4.547 + caniuse-lite: 1.0.30001547 + electron-to-chromium: 1.4.548 node-releases: 2.0.13 update-browserslist-db: 1.0.13(browserslist@4.22.1) dev: true @@ -7131,8 +7138,8 @@ packages: engines: {node: '>=14.16'} dev: true - /caniuse-lite@1.0.30001546: - resolution: {integrity: sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw==} + /caniuse-lite@1.0.30001547: + resolution: {integrity: sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==} /ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -8426,8 +8433,8 @@ packages: /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - /electron-to-chromium@1.4.547: - resolution: {integrity: sha512-QC99aDjzNir+D4c1jE7QWt3RybAxBWYXBdCoaUaAumkD7nDwPLgyt03qNgpVscn18UIGJIZZbFlzNaUSwhPLiA==} + /electron-to-chromium@1.4.548: + resolution: {integrity: sha512-R77KD6mXv37DOyKLN/eW1rGS61N6yHOfapNSX9w+y9DdPG83l9Gkuv7qkCFZ4Ta4JPhrjgQfYbv4Y3TnM1Hi2Q==} /elegant-spinner@1.0.1: resolution: {integrity: sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ==} @@ -9412,7 +9419,7 @@ packages: rfdc: 1.3.0 secure-json-parse: 2.7.0 semver: 7.5.4 - tiny-lru: 11.2.2 + tiny-lru: 11.2.3 transitivePeerDependencies: - supports-color dev: true @@ -16213,8 +16220,8 @@ packages: engines: {node: '>=4'} dev: true - /tiny-lru@11.2.2: - resolution: {integrity: sha512-1An82KBJOUKCfRhsk4tHe4rNblnhAUzisesWBGf/0pztaVoNYM7eJxouN+mq6WkV/qNG21AFbrsgQfvBk0X9ng==} + /tiny-lru@11.2.3: + resolution: {integrity: sha512-mF9jPTrvN7UHk0bekOk3RlFdFwfyS4CJYVsGc7nInL3pVgUCYj5r9X6GpZBFQgLr0TKJo8Dp+F3oRvYzxU9xiA==} engines: {node: '>=12'} dev: true diff --git a/sample/host/webpack.build.js b/sample/host/webpack.build.js index 19e5c23a0..b2ab59699 100644 --- a/sample/host/webpack.build.js +++ b/sample/host/webpack.build.js @@ -15,10 +15,6 @@ export default defineBuildHostConfig(swcConfig, "host", publicPath, { "@sample/shared": { singleton: true, eager: true - }, - "axios": { - singleton: true, - eager: true } }, environmentVariables: { diff --git a/sample/host/webpack.dev.js b/sample/host/webpack.dev.js index d63616d29..b83721ab0 100644 --- a/sample/host/webpack.dev.js +++ b/sample/host/webpack.dev.js @@ -12,10 +12,6 @@ export default defineDevHostConfig(swcConfig, "host", 8080, { "@sample/shared": { singleton: true, eager: true - }, - "axios": { - singleton: true, - eager: true } }, environmentVariables: { diff --git a/sample/shell/src/AuthenticatedLayout.tsx b/sample/shell/src/AuthenticatedLayout.tsx index befa2816e..ea17a95a9 100644 --- a/sample/shell/src/AuthenticatedLayout.tsx +++ b/sample/shell/src/AuthenticatedLayout.tsx @@ -1,7 +1,7 @@ import { useApplicationEventBusListener, type Session, type SessionManager } from "@sample/shared"; import { isNavigationLink, useLogger, useNavigationItems, useRenderedNavigationItems, useSession, type NavigationLinkRenderProps, type NavigationSectionRenderProps, type RenderItemFunction, type RenderSectionFunction } from "@squide/react-router"; import axios from "axios"; -import { Suspense, useCallback, type ReactNode } from "react"; +import { Suspense, useCallback, type MouseEvent, type ReactNode } from "react"; import { Link, Outlet, useNavigate } from "react-router-dom"; type RenderLinkItemFunction = (item: NavigationLinkRenderProps, index: number, level: number) => ReactNode; @@ -57,14 +57,16 @@ export function AuthenticatedLayout({ sessionManager }: AuthenticatedLayoutProps useApplicationEventBusListener("write-to-host", handleModulesMessage); - const onDisconnect = useCallback(async () => { + const onDisconnect = useCallback(async (event: MouseEvent) => { + event.preventDefault(); + axios.post("/logout") .then(() => { sessionManager.clearSession(); logger.debug("[shell] The user session has been cleared."); - // navigate("/logout"); + navigate("/logout"); }) .catch(() => { throw new Error("An unknown error happened while disconnecting the user."); @@ -82,7 +84,7 @@ export function AuthenticatedLayout({ sessionManager }: AuthenticatedLayoutProps {renderedNavigationItems}
- (User: {session.user.name}) + (User: {session?.user?.name})
From 3b73e845c04cd4133f6e6484b552511433dc2fe2 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Tue, 10 Oct 2023 17:03:48 -0400 Subject: [PATCH 08/51] Route visibility can now also be applied to nested routes --- docs/reference/runtime/runtime-class.md | 63 +++++-- packages/react-router/src/routeRegistry.ts | 178 ++++++++---------- packages/react-router/src/runtime.ts | 16 +- packages/react-router/tests/runtime.test.tsx | 183 +++++++++++++++++-- sample/host/src/App.tsx | 30 +-- sample/shell/src/AuthenticatedLayout.tsx | 2 +- sample/shell/src/register.tsx | 2 + 7 files changed, 325 insertions(+), 149 deletions(-) diff --git a/docs/reference/runtime/runtime-class.md b/docs/reference/runtime/runtime-class.md index 1d4a586de..3c65e4807 100644 --- a/docs/reference/runtime/runtime-class.md +++ b/docs/reference/runtime/runtime-class.md @@ -61,19 +61,14 @@ const runtime = new Runtime({ runtime.registerRoute(route, options?: {}) ``` +- `route`: accept any properties of a React Router [Route](https://reactrouter.com/en/main/components/route) component with the addition of: + - `name`: An optional name for the route. + - `visibility`: An optional visibility indicator for the route. Values are `public` or `authenticated`. - `options`: An optional object literal of options: + - `hoist`: An optional boolean value to register the route at the root of the router. The default value is `false`. - `parentPath`: An optional path of a parent route to register this new route under. - `parentName`: An optional name of a parent route to register this new route under. -A Squide route can either be a `RootRoute` or a `Route`. - -- `RootRoute`: accept any properties of a React Router [Route](https://reactrouter.com/en/main/components/route) component with the addition of: - - `hoist`: An optional boolean value to register the route at the root of the router. The default value is `false`. - - `visibility`: An optional visibility indicator for the route. Values are `public` or `authenticated` and the default value is `authenticated`. - - `name`: An optional name for the route. -- `Route`: accept any properties of a React Router [Route](https://reactrouter.com/en/main/components/route) component with the addition of: - - `name`: An optional name for the route. - ```tsx import { Page } from "./Page.tsx" @@ -88,7 +83,7 @@ runtime.registerRoute({ Unlike a regular page, a hoisted page is added at the root of the router, outside of the boundaries of the host application's root layout. This means that a hoisted page has full control over its rendering. -```tsx !#6 +```tsx !#7 import { Page } from "./Page.tsx"; runtime.registerRoute({ @@ -103,18 +98,42 @@ runtime.registerRoute({ ### Register a public route -When registering a route, a hint can be provided to indicate if the route is intended to be displayed as a `public` or `authenticated` route. This is especially useful when dealing with code that conditionally fetch data for authenticated routes. +When registering a route, a hint can be provided, indicating if the route is intended to be displayed as a `public` or `authenticated` route. This is especially useful when dealing with code that conditionally fetch data for authenticated routes (e.g. a session). -```tsx !#6 +```tsx !#4 import { Page } from "./Page.tsx"; runtime.registerRoute({ + visibility: "public" path: "/page-1", - element: , + element: +}); +``` + +A nested route can also have a visibility hint: + +```tsx !#10 +import { Layout } from "./Layout.tsx"; +import { Page } from "./Page.tsx"; + +runtime.registerRoute({ visibility: "public" + path: "/layout", + element: , + children: [ + { + visibility: "public", + path: "/page-1", + element: , + } + ] }); ``` +!!!info +When no visibility hint is provided, a route is considered as an `authenticated` route. +!!! + ### Register a named route The `registerRoute` function accepts a `parentName` property, allowing a route to be [nested under an existing parent route](#register-nested-routes-under-an-existing-route). When searching for the parent route matching the `parentName` property, the `parentName` will be matched against the `name` property of every route. @@ -122,7 +141,7 @@ The `registerRoute` function accepts a `parentName` property, allowing a route t > A `name` property should usually only be defined for routes that doesn't have a path like an error boundary or an authentication boundary. ```tsx !#4 -import { RootErrorBoundary } from "./Page.tsx"; +import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; runtime.registerRoute({ name: "error-boundary", @@ -130,6 +149,22 @@ runtime.registerRoute({ }); ``` +A nested route can also be named: + +```tsx !#8 +import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; +import { RootLayout } from "./RootLayout.tsx"; + +runtime.registerRoute({ + name: "error-boundary", + element: , + children: [ + name: "root-layout", + element: + ] +}); +``` + ### Register nested routes under an existing route React router [nested routes](https://reactrouter.com/en/main/start/tutorial#nested-routes) enable applications to render nested layouts at various points within the router tree. This is quite helpful for federated applications as it enables composable and decoupled UI. diff --git a/packages/react-router/src/routeRegistry.ts b/packages/react-router/src/routeRegistry.ts index 7cc119140..5597573d9 100644 --- a/packages/react-router/src/routeRegistry.ts +++ b/packages/react-router/src/routeRegistry.ts @@ -3,24 +3,19 @@ import type { IndexRouteObject, NonIndexRouteObject } from "react-router-dom"; export type RouteVisibility = "public" | "authenticated"; -export type RouteType = "root"; - export interface IndexRoute extends IndexRouteObject { name?: string; + visibility?: RouteVisibility; } export interface NonIndexRoute extends Omit { name?: string; + visibility?: RouteVisibility; children?: Route[]; } export type Route = IndexRoute | NonIndexRoute; -export type RootRoute = Route & { - visibility?: RouteVisibility; - type?: RouteType; -}; - function normalizePath(routePath?: string) { if (routePath && routePath !== "/" && routePath.endsWith("/")) { return routePath.substring(0, routePath.length - 1); @@ -49,11 +44,11 @@ export interface AddRouteReturnType { } export class RouteRegistry { - #routes: RootRoute[]; + #routes: Route[]; // Using an index to speed up the look up of parent routes. - // - readonly #routesIndex: Map = new Map(); + // + readonly #routesIndex: Map = new Map(); // A collection of pending routes to registered once their layout is registered. // @@ -63,26 +58,12 @@ export class RouteRegistry { this.#routes = []; } - add(route: RootRoute, { parentPath, parentName }: RegisterRouteOptions = {}) { - if (parentPath) { - // The normalized path cannot be undefined because it's been provided by the consumer - // (e.g. it cannot be a pathless route). - return this.#addNestedRoutes([route], normalizePath(parentPath)!); - } - - if (parentName) { - return this.#addNestedRoutes([route], parentName); - } - - return this.#addRootRoutes([route]); - } - #addIndex(route: Route) { const key = createIndexKey(route); if (key) { if (this.#routesIndex.has(key)) { - throw new Error(`[squide] A route index has already been registered for the key: "${key}". Did you register 2 routes with the same "path" or "name" property?`); + throw new Error(`[squide] A route index has already been registered for the key: "${key}". Did you register two routes with the same "path" or "name" property?`); } this.#routesIndex.set(key, route); @@ -91,55 +72,84 @@ export class RouteRegistry { return key; } - #recursivelyAddIndexes(route: Route) { - const newIndexes: string[] = []; - const key = this.#addIndex(route); + #recursivelyAddRoutes(routes: Route[]) { + const newRoutes: Route[] = []; + const completedPendingRegistrations: Route[] = []; - if (key) { - newIndexes.push(key); - } + routes.forEach((x: Route) => { + // Creates a copy of the route object and add the default properties. + const route = { + ...x, + visibility: x.visibility ?? "authenticated" + }; - if (route.children) { - route.children.forEach(x => { - const indexes = this.#recursivelyAddIndexes(x); + if (route.children) { + // Recursively go through the children. + const result = this.#recursivelyAddRoutes(route.children); - newIndexes.push(...indexes); - }); - } + route.children = result.newRoutes; - return newIndexes; - } + completedPendingRegistrations.push(...result.completedPendingRegistrations); + } - #addRootRoutes(routes: RootRoute[]): AddRouteReturnType { - // Creates a copy of the route objects and add a "type" property to each route indicating - // that it's a root route. - const _routes: RootRoute[] = routes.map(x => ({ - ...x, - visibility: x.visibility ?? "authenticated", - type: "root" - })); + // Add index entries to speed up the registration of future nested routes. + const indexKey = this.#addIndex(route); - const newIndexes: string[] = []; + // IMPORTANT: do not deal with the pending registrations before recursively going through the children. + // Otherwise pending routes will be handled twice (one time as a pending registration and one time as child + // of the route). + if (indexKey) { + const pendingRegistrations = this.#tryRegisterPendingRoutes(indexKey); - // Add index entries to speed up the registration of future nested routes. - // This is done recursively to also register indexes for the nested routes if there are any. - _routes.forEach(x => { - const indexes = this.#recursivelyAddIndexes(x); + completedPendingRegistrations.unshift(...pendingRegistrations); + } - newIndexes.push(...indexes); + newRoutes.push(route); }); - // Create a new array so the routes array is immutable. - this.#routes = [...this.#routes, ..._routes]; + return { + newRoutes, + completedPendingRegistrations + }; + } - const completedPendingRegistrations: Route[] = []; + #tryRegisterPendingRoutes(parentId: string) { + const pendingRegistrations = this.#pendingRegistrations.get(parentId); + + if (pendingRegistrations) { + // Try to register the pending routes. + const { registrationStatus } = this.#addNestedRoutes(pendingRegistrations, parentId); - // Use the new indexes to retrieve the route pending registrations and complete their registration. - newIndexes.forEach(x => { - const pendingRegistrations = this.#tryRegisterPendingRoutes(x); + if (registrationStatus === "registered") { + // Remove the pending registrations. + this.#pendingRegistrations.delete(parentId); - completedPendingRegistrations.push(...pendingRegistrations); - }); + return pendingRegistrations; + } + } + + return []; + } + + add(route: Route, { parentPath, parentName }: RegisterRouteOptions = {}) { + if (parentPath) { + // The normalized path cannot be undefined because it's been provided by the consumer + // (e.g. it cannot be a pathless route). + return this.#addNestedRoutes([route], normalizePath(parentPath)!); + } + + if (parentName) { + return this.#addNestedRoutes([route], parentName); + } + + return this.#addRootRoutes([route]); + } + + #addRootRoutes(routes: Route[]): AddRouteReturnType { + const { newRoutes, completedPendingRegistrations } = this.#recursivelyAddRoutes(routes); + + // Create a new array so the routes array is immutable. + this.#routes = [...this.#routes, ...newRoutes]; return { registrationStatus: "registered", @@ -148,18 +158,15 @@ export class RouteRegistry { } #addNestedRoutes(routes: Route[], parentId: string): AddRouteReturnType { - // Creates a copy of the route objects. - const _routes: Route[] = routes.map(x => ({ ...x })); - const layoutRoute = this.#routesIndex.get(parentId); if (!layoutRoute) { const pendingRegistration = this.#pendingRegistrations.get(parentId); if (pendingRegistration) { - pendingRegistration.push(..._routes); + pendingRegistration.push(...routes); } else { - this.#pendingRegistrations.set(parentId, [..._routes]); + this.#pendingRegistrations.set(parentId, [...routes]); } return { @@ -168,57 +175,24 @@ export class RouteRegistry { }; } + const { newRoutes, completedPendingRegistrations } = this.#recursivelyAddRoutes(routes); + // Register new nested routes as children of their layout route. layoutRoute.children = [ ...(layoutRoute.children ?? []), - ..._routes + ...newRoutes ]; - const newIndexes: string[] = []; - - // Add index entries to speed up the registration of future nested routes. - // This is done recursively to also register indexes for the nested routes if there are any. - _routes.forEach(x => { - const indexes = this.#recursivelyAddIndexes(x); - - newIndexes.push(...indexes); - }); - // Create a new array since the routes array is immutable and a nested // object has been updated. this.#routes = [...this.#routes]; - const completedPendingRegistrations: Route[] = []; - - // Use the new indexes to retrieve the route pending registrations and complete their registration. - newIndexes.forEach(x => { - const pendingRegistrations = this.#tryRegisterPendingRoutes(x); - - completedPendingRegistrations.push(...pendingRegistrations); - }); - return { registrationStatus: "registered", completedPendingRegistrations }; } - #tryRegisterPendingRoutes(parentId: string) { - const pendingRegistrations = this.#pendingRegistrations.get(parentId); - - if (pendingRegistrations) { - // Try to register the pending routes. - this.#addNestedRoutes(pendingRegistrations, parentId); - - // Remove the pending registrations. - this.#pendingRegistrations.delete(parentId); - - return pendingRegistrations; - } - - return []; - } - get routes() { return this.#routes; } diff --git a/packages/react-router/src/runtime.ts b/packages/react-router/src/runtime.ts index 5ae19a037..cc0877859 100644 --- a/packages/react-router/src/runtime.ts +++ b/packages/react-router/src/runtime.ts @@ -1,6 +1,6 @@ import { AbstractRuntime, RootMenuId, type RegisterNavigationItemOptions, type RegisterRouteOptions } from "@squide/core"; import { NavigationItemRegistry, type RootNavigationItem } from "./navigationItemRegistry.ts"; -import { RouteRegistry, type RootRoute, type Route } from "./routeRegistry.ts"; +import { RouteRegistry, type Route } from "./routeRegistry.ts"; const ManagedRoutesOutletName = "__squide-managed-routes-outlet__"; @@ -12,11 +12,11 @@ export function isManagedRoutesOutletRoute(route: Route) { return route.name === ManagedRoutesOutletName; } -export class Runtime extends AbstractRuntime { +export class Runtime extends AbstractRuntime { readonly #routeRegistry = new RouteRegistry(); readonly #navigationItemRegistry = new NavigationItemRegistry(); - #validateRootRoutes(route: RootRoute, { hoist, parentPath, parentName }: RegisterRouteOptions = {}) { + #validateRouteRegistrationOptions(route: Route, { hoist, parentPath, parentName }: RegisterRouteOptions = {}) { if (hoist && parentPath) { throw new Error(`[squide] A route cannot have the "hoist" property when a "publicPath" option is provided. Route id: "${route.path ?? route.name ?? "(no identifier)"}".`); } @@ -26,8 +26,8 @@ export class Runtime extends AbstractRuntime 0) { this._logger.debug( - `[squide] The pending registration of the following route${result.completedPendingRegistrations.length !== 1 ? "s" : ""} has been %ccompleted%c.`, "color: white; background-color: #26bfa5;", "%s", + `[squide] The pending registration of the following route${result.completedPendingRegistrations.length > 0 ? "s" : ""} has been %ccompleted%c.`, "color: white; background-color: #26bfa5;", "%s", "Newly registered routes:", result.completedPendingRegistrations, "All registered routes:", this.#routeRegistry.routes ); @@ -99,7 +99,7 @@ export class Runtime extends AbstractRuntime 0 ? "s" : ""} were expected to be registered but ${pendingRegistrations.size > 0 ? "are" : "is"} missing:\r\n\r\n`; let index = 0; // It's easier to use for ... of with a Map object. @@ -116,7 +116,7 @@ export class Runtime extends AbstractRuntime 0 ? "s" : ""} has been registered, make sure that the following conditions are met:\r\n`; message += "- The missing parent routes \"path\" or \"name\" property perfectly match the provided \"parentPath\" or \"parentName\" (make sure that there's no leading or trailing \"/\" that differs).\r\n"; message += "- The missing parent routes has been registered with the \"registerRoute()\" function. A route cannot be registered under a parent route that has not be registered with the \"registerRoute()\" function.\r\n"; message += "For more information about nested routes, refers to https://gsoft-inc.github.io/wl-squide/reference/runtime/runtime-class/#register-routes-under-a-specific-nested-layout-route."; diff --git a/packages/react-router/tests/runtime.test.tsx b/packages/react-router/tests/runtime.test.tsx index 30d5476e5..30abb6ff9 100644 --- a/packages/react-router/tests/runtime.test.tsx +++ b/packages/react-router/tests/runtime.test.tsx @@ -1,4 +1,4 @@ -import type { RootRoute, Route } from "../src/routeRegistry.ts"; +import type { Route } from "../src/routeRegistry.ts"; import { ManagedRoutes, Runtime, isManagedRoutesOutletRoute } from "../src/runtime.ts"; describe("registerRoute", () => { @@ -7,7 +7,7 @@ describe("registerRoute", () => { runtime.registerRoute(ManagedRoutes); } - function getManagedRoutes(routes: Route[]): (Route | RootRoute)[] | undefined { + function getManagedRoutes(routes: Route[]): Route[] | undefined { for (const route of routes) { if (isManagedRoutesOutletRoute(route)) { return route.children as Route[]; @@ -164,7 +164,7 @@ describe("registerRoute", () => { expect(routes[0].children![0].children![0].index).toBeTruthy(); }); - test("can register a route with a \"public\" visibility", () => { + test("can register a root route with a \"public\" visibility", () => { const runtime = new Runtime(); registerManagedRoutesOutlet(runtime); @@ -179,10 +179,10 @@ describe("registerRoute", () => { expect(routes.length).toBe(1); expect(routes[0].path).toBe("/public"); - expect((routes[0] as RootRoute).visibility).toBe("public"); + expect(routes[0].visibility).toBe("public"); }); - test("can register a route with a \"authenticated\" visibility", () => { + test("can register a root route with a \"authenticated\" visibility", () => { const runtime = new Runtime(); registerManagedRoutesOutlet(runtime); @@ -197,7 +197,92 @@ describe("registerRoute", () => { expect(routes.length).toBe(1); expect(routes[0].path).toBe("/authenticated"); - expect((routes[0] as RootRoute).visibility).toBe("authenticated"); + expect(routes[0].visibility).toBe("authenticated"); + }); + + test("when a root route has no visibility property, it is considered as an \"authenticated\" route", () => { + const runtime = new Runtime(); + + registerManagedRoutesOutlet(runtime); + + runtime.registerRoute({ + path: "/foo", + element:
Hello!
+ }); + + const routes = getManagedRoutes(runtime.routes)!; + + expect(routes.length).toBe(1); + expect(routes[0].path).toBe("/foo"); + expect(routes[0].visibility).toBe("authenticated"); + }); + + test("can register a nested route with a \"public\" visibility", () => { + const runtime = new Runtime(); + + registerManagedRoutesOutlet(runtime); + + runtime.registerRoute({ + path: "/layout", + element:
Hello!
, + children: [ + { + visibility: "public", + path: "/layout/nested", + element:
Hello!
+ } + ] + }); + + const routes = getManagedRoutes(runtime.routes)!; + + expect(routes[0].children![0].path).toBe("/layout/nested"); + expect(routes[0].children![0].visibility).toBe("public"); + }); + + test("can register a nested route with a \"authenticated\" visibility", () => { + const runtime = new Runtime(); + + registerManagedRoutesOutlet(runtime); + + runtime.registerRoute({ + path: "/layout", + element:
Hello!
, + children: [ + { + visibility: "authenticated", + path: "/layout/nested", + element:
Hello!
+ } + ] + }); + + const routes = getManagedRoutes(runtime.routes)!; + + expect(routes[0].children![0].path).toBe("/layout/nested"); + expect(routes[0].children![0].visibility).toBe("authenticated"); + }); + + test("when a nested route has no visibility property, it is considered as an \"authenticated\" route", () => { + const runtime = new Runtime(); + + registerManagedRoutesOutlet(runtime); + + runtime.registerRoute({ + path: "/layout", + element:
Hello!
, + children: [ + { + path: "/layout/nested", + element:
Hello!
+ } + ] + }); + + const routes = getManagedRoutes(runtime.routes)!; + + expect(routes[0].children![0].path).toBe("/layout/nested"); + expect(routes[0].children![0].visibility).toBe("authenticated"); }); test("can register a root route with a name", () => { @@ -345,7 +430,7 @@ describe("registerRoute", () => { expect(runtime.routes[0].children![0].children![0].index).toBeTruthy(); }); - test("can register a route with a \"public\" visibility", () => { + test("can register a root route with a \"public\" visibility", () => { const runtime = new Runtime(); runtime.registerRoute({ @@ -356,12 +441,11 @@ describe("registerRoute", () => { hoist: true }); - expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].path).toBe("/public"); - expect((runtime.routes[0] as RootRoute).visibility).toBe("public"); + expect(runtime.routes[0].visibility).toBe("public"); }); - test("can register a route with a \"authenticated\" visibility", () => { + test("can register a root route with a \"authenticated\" visibility", () => { const runtime = new Runtime(); runtime.registerRoute({ @@ -372,9 +456,84 @@ describe("registerRoute", () => { hoist: true }); - expect(runtime.routes.length).toBe(1); expect(runtime.routes[0].path).toBe("/authenticated"); - expect((runtime.routes[0] as RootRoute).visibility).toBe("authenticated"); + expect(runtime.routes[0].visibility).toBe("authenticated"); + }); + + test("when a root route has no visibility property, it is considered as an \"authenticated\" route", () => { + const runtime = new Runtime(); + + runtime.registerRoute({ + path: "/foo", + element:
Hello!
+ }, { + hoist: true + }); + + expect(runtime.routes[0].path).toBe("/foo"); + expect(runtime.routes[0].visibility).toBe("authenticated"); + }); + + test("can register a nested route with a \"public\" visibility", () => { + const runtime = new Runtime(); + + runtime.registerRoute({ + path: "/layout", + element:
Hello!
, + children: [ + { + visibility: "public", + path: "/layout/nested", + element:
Hello!
+ } + ] + }, { + hoist: true + }); + + expect(runtime.routes[0].children![0].path).toBe("/layout/nested"); + expect(runtime.routes[0].children![0].visibility).toBe("public"); + }); + + test("can register a nested route with a \"authenticated\" visibility", () => { + const runtime = new Runtime(); + + runtime.registerRoute({ + path: "/layout", + element:
Hello!
, + children: [ + { + visibility: "authenticated", + path: "/layout/nested", + element:
Hello!
+ } + ] + }, { + hoist: true + }); + + expect(runtime.routes[0].children![0].path).toBe("/layout/nested"); + expect(runtime.routes[0].children![0].visibility).toBe("authenticated"); + }); + + test("when a nested route has no visibility property, it is considered as an \"authenticated\" route", () => { + const runtime = new Runtime(); + + runtime.registerRoute({ + path: "/layout", + element:
Hello!
, + children: [ + { + path: "/layout/nested", + element:
Hello!
+ } + ] + }, { + hoist: true + }); + + expect(runtime.routes[0].children![0].path).toBe("/layout/nested"); + expect(runtime.routes[0].children![0].visibility).toBe("authenticated"); }); test("can register a root route with a name", () => { diff --git a/sample/host/src/App.tsx b/sample/host/src/App.tsx index 67565bd7b..d36abff3c 100644 --- a/sample/host/src/App.tsx +++ b/sample/host/src/App.tsx @@ -26,23 +26,23 @@ export function App() { useEffect(() => { if (areModulesReady && isMswStarted) { - // getActiveRouteVisibility + // 2 hooks: + // getActiveRouteVisibility -> "public" | "protected" | unknown + // isActiveRouteProtected -> true if protected, false otherwise | throw an Error when unknown - // const location = useLocation(); const location = window.location; - - // console.log("**** location: ", location); - const matchingRoutes = matchRoutes(runtime.routes, location) ?? []; - console.log("**** matchingRoutes:", matchingRoutes); + logger.debug(`[shell] Found ${matchingRoutes.length} matching route${matchingRoutes.length > 0 ? "s" : ""}:`, matchingRoutes); if (matchingRoutes.length > 0) { - // When a route is nested, it also returns all the parts that constistuate the whole route (for example the layouts). - // We only want to know the visiblity of the deepest root route. - const rootRoute = matchingRoutes.findLast(x => x.route.type === "root"); + // When a route is nested, it also returns all the parts that constistuate the whole route (for example the layouts and the boundaries). + // We only want to know the visiblity of the actual route that has been requested, which is always the last entry. + const activeRoute = matchingRoutes[matchingRoutes.length - 1]!.route; + + logger.debug(`[shell] The active route is "${activeRoute.visibility}":`, activeRoute); - if (rootRoute!.route.visibility === "authenticated") { + if (activeRoute!.visibility === "authenticated") { logger.debug(`[shell] Fetching session data as "${location}" is a protected route.`); axios.get("/session") @@ -59,9 +59,15 @@ export function App() { setIsReady(true); }) - .catch(() => { - // The authentication boundary will redirect to the login page. + .catch((error: unknown) => { setIsReady(true); + + if (axios.isAxiosError(error) && error.response?.status === 401) { + // The authentication boundary will redirect to the login page. + return; + } + + throw error; }); } else { setIsReady(true); diff --git a/sample/shell/src/AuthenticatedLayout.tsx b/sample/shell/src/AuthenticatedLayout.tsx index ea17a95a9..5998aa89b 100644 --- a/sample/shell/src/AuthenticatedLayout.tsx +++ b/sample/shell/src/AuthenticatedLayout.tsx @@ -74,7 +74,6 @@ export function AuthenticatedLayout({ sessionManager }: AuthenticatedLayoutProps }, [logger, navigate, sessionManager]); const navigationItems = useNavigationItems(); - const renderedNavigationItems = useRenderedNavigationItems(navigationItems, renderItem, renderSection); return ( @@ -84,6 +83,7 @@ export function AuthenticatedLayout({ sessionManager }: AuthenticatedLayoutProps {renderedNavigationItems}
+ {/* Must check for a null session because when the disconnect button is clicked, it will clear the session and rerender this layout. */} (User: {session?.user?.name})
diff --git a/sample/shell/src/register.tsx b/sample/shell/src/register.tsx index a10e4c5e3..095476544 100644 --- a/sample/shell/src/register.tsx +++ b/sample/shell/src/register.tsx @@ -9,10 +9,12 @@ import { RootLayout } from "./RootLayout.tsx"; function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { runtime.registerRoute({ // Pathless route to declare a root layout and a root error boundary. + visibility: "public", element: , children: [ { // Public pages like the login and logout pages will be rendered under this pathless route. + visibility: "public", name: "root-error-boundary", errorElement: , children: [ From 97574ed2d0995e4d17bbcc7f41211977c2ebd721 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Wed, 11 Oct 2023 00:07:32 -0400 Subject: [PATCH 09/51] Refactored the host app App.tsx file --- docs/getting-started/create-remote-module.md | 2 +- docs/getting-started/learn-the-api.md | 42 -------- docs/reference/default.md | 6 -- .../registration/registerLocalModules.md | 2 +- .../registration/registerRemoteModules.md | 2 +- docs/reference/runtime/runtime-class.md | 18 +--- docs/reference/runtime/useService.md | 31 ------ docs/reference/runtime/useServices.md | 30 ------ docs/reference/services/index.yaml | 2 - docs/reference/services/service.md | 94 ---------------- packages/msw/src/useIsMswReady.ts | 4 +- packages/react-router/src/index.ts | 3 + packages/react-router/src/outlets.ts | 11 ++ packages/react-router/src/routeRegistry.ts | 44 ++++++-- packages/react-router/src/runtime.ts | 36 +------ packages/react-router/src/useActiveRoute.ts | 26 +++++ packages/react-router/tests/runtime.test.tsx | 3 +- pnpm-lock.yaml | 3 + sample/another-remote-module/src/dev/App.tsx | 13 +-- sample/host/src/App.tsx | 88 +-------------- sample/local-module/src/dev/App.tsx | 13 +-- sample/remote-module/src/dev/App.tsx | 11 +- sample/shell/package.json | 2 + sample/shell/src/AuthenticatedLayout.tsx | 2 + sample/shell/src/Login.tsx | 9 +- sample/shell/src/register.tsx | 1 + sample/shell/src/useAppRouter.tsx | 101 ++++++++++++++++-- 27 files changed, 212 insertions(+), 387 deletions(-) delete mode 100644 docs/reference/runtime/useService.md delete mode 100644 docs/reference/runtime/useServices.md delete mode 100644 docs/reference/services/index.yaml delete mode 100644 docs/reference/services/service.md create mode 100644 packages/react-router/src/outlets.ts create mode 100644 packages/react-router/src/useActiveRoute.ts diff --git a/docs/getting-started/create-remote-module.md b/docs/getting-started/create-remote-module.md index 6d9aceee7..9224a083e 100644 --- a/docs/getting-started/create-remote-module.md +++ b/docs/getting-started/create-remote-module.md @@ -62,7 +62,7 @@ Then, ensure that you are developing your module using [ESM syntax](https://deve Next, register the remote module routes and navigation items with the [registerRoute](/reference/runtime/runtime-class.md#register-routes) and [registerNavigationItem](/reference/runtime/runtime-class.md#register-navigation-items) functions: -```tsx !#6-11,13-18 remote-module/src/register.tsx +```tsx !#6-9,11-14 remote-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import type { AppContext } from "@sample/shared"; import { Page } from "./Page.tsx"; diff --git a/docs/getting-started/learn-the-api.md b/docs/getting-started/learn-the-api.md index 8e9ac4867..c810b4aa9 100644 --- a/docs/getting-started/learn-the-api.md +++ b/docs/getting-started/learn-the-api.md @@ -131,48 +131,6 @@ const isAuthenticated = useIsAuthenticated(); The session is also available from the [Runtime](/reference/runtime/runtime-class.md) instance. -## Services - -While Squide provides a range of built-in functionalities, by no mean these alone can support the needs of every mature application. Therefore, the shell [Runtime](/reference/runtime/runtime-class.md) allows the addition of custom services. - -First, define a service by implementing the [Service](../reference/services/service.md) interface: - -```ts !#3 shared/src/telemetryService.ts -import { Service } from "@squide/react-router"; - -export class TelemetryService extends Service { - constructor() { - super(TelemetryService.name) - } - - getUser(userId: string) { - ... - } -} -``` - -Then, make the service available to every part of the application by passing a service instance to the `Runtime` instance: - -```ts !#5 host/src/boostrap.tsx -import { Runtime } from "@squide/react-router"; -import { TelemetryService } from "@sample/shared"; - -const runtime = new Runtime({ - services: [new TelemetryService()] -}); -``` - -Finally, access the service instance from anywhere with the [useService](/reference/runtime/useService.md) hook: - -```ts -import { useService } from "@squide/react-router"; -import { TelemetryService } from "@sample/shared"; - -const service = useService(TelemetryService.name) as TelemetryService; -``` - -The services are also available from the [Runtime](/reference/runtime/runtime-class.md#retrieve-a-service) instance. - ## Plugins To keep Squide lightweight, not all functionalities should be integrated as a core functionality. However, to accommodate a broad range of technologies, a plugin system has been implemented to fill the gap. diff --git a/docs/reference/default.md b/docs/reference/default.md index 259028001..22927ceba 100644 --- a/docs/reference/default.md +++ b/docs/reference/default.md @@ -20,8 +20,6 @@ expanded: true - [useRoutes](runtime/useRoutes.md) - [useNavigationItems](runtime/useNavigationItems.md) - [useLogger](runtime/useLogger.md) -- [useService](runtime/useService.md) -- [useServices](runtime/useServices.md) - [useSession](runtime/useSession.md) ### Registration @@ -51,10 +49,6 @@ expanded: true - [useIsAuthenticated](session/useIsAuthenticated.md) -### Services - -- [Service](services/service.md) - ### Plugins - [Plugin](plugins/plugin.md) diff --git a/docs/reference/registration/registerLocalModules.md b/docs/reference/registration/registerLocalModules.md index 96985470f..d2e0fa148 100644 --- a/docs/reference/registration/registerLocalModules.md +++ b/docs/reference/registration/registerLocalModules.md @@ -42,7 +42,7 @@ const context: AppContext = { registerLocalModules([register], runtime, { context }); ``` -```tsx !#5-19 local-module/src/register.tsx +```tsx !#5-15 local-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import type { AppContext } from "@sample/shared"; import { About } from "./About.tsx"; diff --git a/docs/reference/registration/registerRemoteModules.md b/docs/reference/registration/registerRemoteModules.md index b60525991..97460fd05 100644 --- a/docs/reference/registration/registerRemoteModules.md +++ b/docs/reference/registration/registerRemoteModules.md @@ -52,7 +52,7 @@ const Remotes: RemoteDefinition = [ registerRemoteModules(Remotes, runtime, { context }); ``` -```tsx !#5-19 remote-module/src/register.tsx +```tsx !#5-15 remote-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import type { AppContext } from "@sample/shared"; import { About } from "./About.tsx"; diff --git a/docs/reference/runtime/runtime-class.md b/docs/reference/runtime/runtime-class.md index 3c65e4807..afc8f15ba 100644 --- a/docs/reference/runtime/runtime-class.md +++ b/docs/reference/runtime/runtime-class.md @@ -11,7 +11,7 @@ A runtime instance give modules access to functionalities such as routing, navig ## Reference ```ts -const runtime = new Runtime(options?: { loggers?: [], services?: [], plugins?: [], sessionAccessor?: () => {} }) +const runtime = new Runtime(options?: { loggers?: [], plugins?: [], sessionAccessor?: () => {} }) ``` ### Parameters @@ -19,7 +19,6 @@ const runtime = new Runtime(options?: { loggers?: [], services?: [], plugins?: [ - `options`: An optional object literal of options: - `mode`: An optional mode to optimize Squide for `production`. Values are `"development"` (default) and `"production"`. - `loggers`: An optional array of `Logger` instances. - - `services`: An optional array of custom service instances. - `plugins`: An optional array of custom plugin instances. - `sessionAccessor`: An optional function returning the current session. @@ -31,13 +30,12 @@ const runtime = new Runtime(options?: { loggers?: [], services?: [], plugins?: [ import { ConsoleLogger, Runtime } from "@squide/react-router"; import { LocalStorageSessionManager } from "@squide/fakes"; import { MswPlugin } from "@squide/msw"; -import { TelemetryService, type AppSession } from "@sample/shared"; +import { type AppSession } from "@sample/shared"; const sessionManager = new LocalStorageSessionManager(); const runtime = new Runtime({ loggers: [new ConsoleLogger()], - services: [new TelemetryService()], plugins: [new MswPlugin()], sessionAccessor: () => { return sessionManager.getSession(); @@ -63,7 +61,7 @@ runtime.registerRoute(route, options?: {}) - `route`: accept any properties of a React Router [Route](https://reactrouter.com/en/main/components/route) component with the addition of: - `name`: An optional name for the route. - - `visibility`: An optional visibility indicator for the route. Values are `public` or `authenticated`. + - `visibility`: An optional visibility indicator for the route. Accepted values are `"public"` or `"authenticated"`. - `options`: An optional object literal of options: - `hoist`: An optional boolean value to register the route at the root of the router. The default value is `false`. - `parentPath`: An optional path of a parent route to register this new route under. @@ -215,6 +213,7 @@ const routes = runtime.routes; runtime.registerNavigationItem(item, options?: {}) ``` +- `item`: `NavigationSection | NavigationLink`. - `options`: An optional object literal of options: - `menuId`: An optional menu id to associate the item with. @@ -394,15 +393,6 @@ runtime.eventBus.addListener("write-to-host", () => {}); runtime.eventBus.dispatch("write-to-host", "Hello host!"); ``` -### Retrieve a service - -```ts -// If the service isn't registered, an exception will be thrown. -const service = runtime.getService(TelemetryService.name) as TelemetryService; -``` - -[!ref Learn more about services](../services/service.md) - ### Retrieve a plugin ```ts diff --git a/docs/reference/runtime/useService.md b/docs/reference/runtime/useService.md deleted file mode 100644 index 4dcb08861..000000000 --- a/docs/reference/runtime/useService.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -toc: - depth: 2-3 ---- - -# useService - -Retrieve a custom service instance from the `Runtime` instance provided by `RuntimeContext`. - -## Reference - -```ts -const service = useService(name) -``` - -### Parameters - -- `name`: A custom service name. - -### Returns - -A service instance or throw an error if the specified service name doesn't match any registered instance. - -## Usage - -```ts -import { useService } from "@squide/react-router"; -import { TelemetryService } from "@sample/shared"; - -const telemetryService = useService(TelemetryService.name) as TelemetryService; -``` diff --git a/docs/reference/runtime/useServices.md b/docs/reference/runtime/useServices.md deleted file mode 100644 index debf308b1..000000000 --- a/docs/reference/runtime/useServices.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -toc: - depth: 2-3 ---- - -# useServices - -Retrieve an array of custom service instances from the `Runtime` instance provided by `RuntimeContext`. - -## Reference - -```ts -const services = useServices() -``` - -### Parameters - -None - -### Returns - -An array of custom service instances. - -## Usage - -```ts -import { useServices } from "@squide/react-router"; - -const services = useServices(); -``` diff --git a/docs/reference/services/index.yaml b/docs/reference/services/index.yaml deleted file mode 100644 index 9f632f243..000000000 --- a/docs/reference/services/index.yaml +++ /dev/null @@ -1,2 +0,0 @@ -order: 30 -label: "Services" diff --git a/docs/reference/services/service.md b/docs/reference/services/service.md deleted file mode 100644 index f0368f0a8..000000000 --- a/docs/reference/services/service.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -toc: - depth: 2-3 ---- - -# Service - -An abstract base class to define a service. - -## Usage - -### Define a custom service - -```ts !#3 shared/src/telemetryService.ts -import { Service } from "@squide/core"; - -export class TelemetryService extends Service { - constructor() { - super(TelemetryService.name); - } -} -``` - -### Register a service - -```ts !#4 host/src/boostrap.tsx -import { TelemetryService } from "@sample/shared"; - -const runtime = new Runtime({ - services: [new TelemetryService()] -}); -``` - -### Retrieve a service from a runtime instance - -```ts -import { TelemetryService } from "@sample/shared"; - -const telemetryService = runtime.getService(TelemetryService.name); -``` - -### Retrieve a service with the `useService` hook - -```ts -import { useService } from "@squide/react-router"; -import { TelemetryService } from "@sample/shared"; - -const telemetryService = runtime.getService(TelemetryService.name); -``` - -### Retrieve a service with a custom function or a hook - -We recommend pairing a custom service definition with a custom function or hook to retrieve the service from a runtime instance. - -```ts !#9-11,13-17 shared/src/telemetryService.ts -import { Service, useRuntime, type Runtime } from "@squide/react-router"; - -export class TelemetryService extends Service { - constructor() { - super(TelemetryService.name); - } -} - -export function getTelemetryService(runtime: Runtime) { - return runtime.getService(TelemetryService.name) as TelemetryService; -} - -export function useTelemetryService() { - const runtime = useRuntime(); - - return runtime.getService(TelemetryService.name) as TelemetryService; -} -``` - -```ts -import { getTelemetryService } from "@sample/shared"; - -const telemetryService = getTelemetryService(runtime); -``` - -```ts -import { useTelemetryService } from "@sample/shared"; - -const telemetryService = useTelemetryService(); -``` - -Retrieving a service with a custom function or hook doesn't require the consumer to remember the service name, and has the upside of inferring the typings. - - - - - - - diff --git a/packages/msw/src/useIsMswReady.ts b/packages/msw/src/useIsMswReady.ts index be0c88a1f..0afe3bad7 100644 --- a/packages/msw/src/useIsMswReady.ts +++ b/packages/msw/src/useIsMswReady.ts @@ -11,14 +11,14 @@ export function useIsMswStarted(enabled: boolean, { interval = 10 }: UseIsMswSta const runtime = useRuntime(); const logger = useLogger(); - const mswPlugin = getMswPlugin(runtime); - // Using a state hook to force a rerender once MSW is started. const [value, setIsStarted] = useState(!enabled); // Perform a reload once MSW is started. useEffect(() => { if (enabled) { + const mswPlugin = getMswPlugin(runtime); + const intervalId = setInterval(() => { if (mswPlugin.isStarted) { logger.debug("[squide] MSW is ready."); diff --git a/packages/react-router/src/index.ts b/packages/react-router/src/index.ts index 6421be76c..1f74e33de 100644 --- a/packages/react-router/src/index.ts +++ b/packages/react-router/src/index.ts @@ -1,7 +1,10 @@ export * from "@squide/core"; + export * from "./navigationItemRegistry.ts"; +export * from "./outlets.ts"; export * from "./routeRegistry.ts"; export * from "./runtime.ts"; +export * from "./useActiveRoute.ts"; export * from "./useNavigationItems.ts"; export * from "./useRenderedNavigationItems.tsx"; export * from "./useRoutes.ts"; diff --git a/packages/react-router/src/outlets.ts b/packages/react-router/src/outlets.ts new file mode 100644 index 000000000..49cb8941d --- /dev/null +++ b/packages/react-router/src/outlets.ts @@ -0,0 +1,11 @@ +import type { Route } from "./routeRegistry.ts"; + +export const ManagedRoutesOutletName = "__squide-managed-routes-outlet__"; + +export const ManagedRoutes: Route = { + name: ManagedRoutesOutletName +}; + +export function isManagedRoutesOutletRoute(route: Route) { + return route.name === ManagedRoutesOutletName; +} diff --git a/packages/react-router/src/routeRegistry.ts b/packages/react-router/src/routeRegistry.ts index 5597573d9..89ed60051 100644 --- a/packages/react-router/src/routeRegistry.ts +++ b/packages/react-router/src/routeRegistry.ts @@ -1,5 +1,6 @@ import type { RegisterRouteOptions } from "@squide/core"; import type { IndexRouteObject, NonIndexRouteObject } from "react-router-dom"; +import { ManagedRoutesOutletName, isManagedRoutesOutletRoute } from "./outlets.ts"; export type RouteVisibility = "public" | "authenticated"; @@ -16,6 +17,8 @@ export interface NonIndexRoute extends Omit { export type Route = IndexRoute | NonIndexRoute; +export type RouteRegistrationStatus = "pending" | "registered"; + function normalizePath(routePath?: string) { if (routePath && routePath !== "/" && routePath.endsWith("/")) { return routePath.substring(0, routePath.length - 1); @@ -36,12 +39,6 @@ export function createIndexKey(route: Route) { return undefined; } -export type RouteRegistrationStatus = "pending" | "registered"; - -export interface AddRouteReturnType { - registrationStatus: RouteRegistrationStatus; - completedPendingRegistrations: Route[]; -} export class RouteRegistry { #routes: Route[]; @@ -95,7 +92,7 @@ export class RouteRegistry { // Add index entries to speed up the registration of future nested routes. const indexKey = this.#addIndex(route); - // IMPORTANT: do not deal with the pending registrations before recursively going through the children. + // IMPORTANT: do not handle the pending registrations before recursively going through the children. // Otherwise pending routes will be handled twice (one time as a pending registration and one time as child // of the route). if (indexKey) { @@ -131,7 +128,34 @@ export class RouteRegistry { return []; } - add(route: Route, { parentPath, parentName }: RegisterRouteOptions = {}) { + #validateRouteRegistrationOptions(route: Route, { hoist, parentPath, parentName }: RegisterRouteOptions = {}) { + if (hoist && parentPath) { + throw new Error(`[squide] A route cannot have the "hoist" property when a "publicPath" option is provided. Route id: "${route.path ?? route.name ?? "(no identifier)"}".`); + } + + if (hoist && parentName) { + throw new Error(`[squide] A route cannot have the "hoist" property when a "parentName" option is provided. Route id: "${route.path ?? route.name ?? "(no identifier)"}".`); + } + } + + add(route: Route, options: RegisterRouteOptions = {}) { + let parentName = options.parentName; + + // By default, a route that is not hoisted nor nested under a known + // parent will be rendered under the ManagedRoutes outlet. + if (!options.hoist && !parentName && !isManagedRoutesOutletRoute(route)) { + parentName = ManagedRoutesOutletName; + } + + this.#validateRouteRegistrationOptions(route, options); + + return this.#addRoute(route, { + ...options, + parentName + }); + } + + #addRoute(route: Route, { parentPath, parentName }: RegisterRouteOptions) { if (parentPath) { // The normalized path cannot be undefined because it's been provided by the consumer // (e.g. it cannot be a pathless route). @@ -145,7 +169,7 @@ export class RouteRegistry { return this.#addRootRoutes([route]); } - #addRootRoutes(routes: Route[]): AddRouteReturnType { + #addRootRoutes(routes: Route[]) { const { newRoutes, completedPendingRegistrations } = this.#recursivelyAddRoutes(routes); // Create a new array so the routes array is immutable. @@ -157,7 +181,7 @@ export class RouteRegistry { }; } - #addNestedRoutes(routes: Route[], parentId: string): AddRouteReturnType { + #addNestedRoutes(routes: Route[], parentId: string) { const layoutRoute = this.#routesIndex.get(parentId); if (!layoutRoute) { diff --git a/packages/react-router/src/runtime.ts b/packages/react-router/src/runtime.ts index cc0877859..1891a60cb 100644 --- a/packages/react-router/src/runtime.ts +++ b/packages/react-router/src/runtime.ts @@ -1,46 +1,14 @@ import { AbstractRuntime, RootMenuId, type RegisterNavigationItemOptions, type RegisterRouteOptions } from "@squide/core"; import { NavigationItemRegistry, type RootNavigationItem } from "./navigationItemRegistry.ts"; +import { ManagedRoutes } from "./outlets.ts"; import { RouteRegistry, type Route } from "./routeRegistry.ts"; -const ManagedRoutesOutletName = "__squide-managed-routes-outlet__"; - -export const ManagedRoutes: Route = { - name: ManagedRoutesOutletName -}; - -export function isManagedRoutesOutletRoute(route: Route) { - return route.name === ManagedRoutesOutletName; -} - export class Runtime extends AbstractRuntime { readonly #routeRegistry = new RouteRegistry(); readonly #navigationItemRegistry = new NavigationItemRegistry(); - #validateRouteRegistrationOptions(route: Route, { hoist, parentPath, parentName }: RegisterRouteOptions = {}) { - if (hoist && parentPath) { - throw new Error(`[squide] A route cannot have the "hoist" property when a "publicPath" option is provided. Route id: "${route.path ?? route.name ?? "(no identifier)"}".`); - } - - if (hoist && parentName) { - throw new Error(`[squide] A route cannot have the "hoist" property when a "parentName" option is provided. Route id: "${route.path ?? route.name ?? "(no identifier)"}".`); - } - } - registerRoute(route: Route, options: RegisterRouteOptions = {}) { - this.#validateRouteRegistrationOptions(route, options); - - let parentName = options.parentName; - - // By default, a route that is not hoisted nor nested under a known - // parent will be rendered under the ManagedRoutes outlet. - if (!options.hoist && !parentName && !isManagedRoutesOutletRoute(route)) { - parentName = ManagedRoutesOutletName; - } - - const result = this.#routeRegistry.add(route, { - ...options, - parentName - }); + const result = this.#routeRegistry.add(route, options); if (result.registrationStatus === "registered") { const parentId = options.parentPath ?? options.parentName; diff --git a/packages/react-router/src/useActiveRoute.ts b/packages/react-router/src/useActiveRoute.ts new file mode 100644 index 000000000..c4356718a --- /dev/null +++ b/packages/react-router/src/useActiveRoute.ts @@ -0,0 +1,26 @@ +import { matchRoutes } from "react-router-dom"; +import { useRoutes } from "./useRoutes.ts"; + +export function useActiveRoute(locationArg: Partial) { + const routes = useRoutes(); + + const matchingRoutes = matchRoutes(routes, locationArg) ?? []; + + if (matchingRoutes.length > 0) { + // When a route is nested, it also returns all the parts that constituate the whole route (for example the layouts and the boundaries). + // We only want to know the visiblity of the actual route that has been requested, which is always the last entry. + return matchingRoutes[matchingRoutes.length - 1]!.route; + } + + return undefined; +} + +export function useIsActiveRouteProtected(locationArg: Partial) { + const activeRoute = useActiveRoute(locationArg); + + if (!activeRoute) { + throw new Error(`[squide] There's no matching route for the location: "${locationArg.pathname}". Did you add routes to React Router without using the runtime.registerRoute() function?`); + } + + return activeRoute.visibility === "authenticated"; +} diff --git a/packages/react-router/tests/runtime.test.tsx b/packages/react-router/tests/runtime.test.tsx index 30abb6ff9..c152c3776 100644 --- a/packages/react-router/tests/runtime.test.tsx +++ b/packages/react-router/tests/runtime.test.tsx @@ -1,5 +1,6 @@ +import { ManagedRoutes, isManagedRoutesOutletRoute } from "../src/outlets.ts"; import type { Route } from "../src/routeRegistry.ts"; -import { ManagedRoutes, Runtime, isManagedRoutesOutletRoute } from "../src/runtime.ts"; +import { Runtime } from "../src/runtime.ts"; describe("registerRoute", () => { describe("managed routes", () => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c908b74c5..57b13b3a3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -704,6 +704,9 @@ importers: '@squide/react-router': specifier: workspace:* version: link:../../packages/react-router + '@squide/webpack-module-federation': + specifier: workspace:* + version: link:../../packages/webpack-module-federation '@types/react': specifier: 18.2.22 version: 18.2.22 diff --git a/sample/another-remote-module/src/dev/App.tsx b/sample/another-remote-module/src/dev/App.tsx index 4ec07d97d..48291f3b0 100644 --- a/sample/another-remote-module/src/dev/App.tsx +++ b/sample/another-remote-module/src/dev/App.tsx @@ -1,13 +1,8 @@ import { useAppRouter } from "@sample/shell"; -import { RouterProvider } from "react-router-dom"; +import { sessionManager } from "./session.ts"; export function App() { - const router = useAppRouter(); - - return ( - - ); + return useAppRouter(sessionManager, { + waitForMsw: process.env.USE_MSW as unknown as boolean + }); } diff --git a/sample/host/src/App.tsx b/sample/host/src/App.tsx index d36abff3c..8affd330c 100644 --- a/sample/host/src/App.tsx +++ b/sample/host/src/App.tsx @@ -1,93 +1,15 @@ -import { BackgroundColorContext, type Session } from "@sample/shared"; +import { BackgroundColorContext } from "@sample/shared"; import { useAppRouter } from "@sample/shell"; -import { useIsMswStarted } from "@squide/msw"; -import { useLogger, useRuntime, type Runtime } from "@squide/react-router"; -import { useAreModulesReady } from "@squide/webpack-module-federation"; -import axios from "axios"; -import { useEffect, useState } from "react"; -import { RouterProvider, matchRoutes } from "react-router-dom"; import { sessionManager } from "./session.ts"; export function App() { - const [isReady, setIsReady] = useState(false); - - const runtime = useRuntime() as Runtime; - const logger = useLogger(); - - // Re-render the app once all the remotes are registered. - // Otherwise, the remotes routes won't be added to the router. - const areModulesReady = useAreModulesReady(); - - // Re-render the app once MSW is started. - // Otherwise, the API calls will return a 404 status. - const isMswStarted = useIsMswStarted(process.env.USE_MSW as unknown as boolean); - - const router = useAppRouter(); - - useEffect(() => { - if (areModulesReady && isMswStarted) { - // 2 hooks: - // getActiveRouteVisibility -> "public" | "protected" | unknown - // isActiveRouteProtected -> true if protected, false otherwise | throw an Error when unknown - - const location = window.location; - const matchingRoutes = matchRoutes(runtime.routes, location) ?? []; - - logger.debug(`[shell] Found ${matchingRoutes.length} matching route${matchingRoutes.length > 0 ? "s" : ""}:`, matchingRoutes); - - if (matchingRoutes.length > 0) { - // When a route is nested, it also returns all the parts that constistuate the whole route (for example the layouts and the boundaries). - // We only want to know the visiblity of the actual route that has been requested, which is always the last entry. - const activeRoute = matchingRoutes[matchingRoutes.length - 1]!.route; - - logger.debug(`[shell] The active route is "${activeRoute.visibility}":`, activeRoute); - - if (activeRoute!.visibility === "authenticated") { - logger.debug(`[shell] Fetching session data as "${location}" is a protected route.`); - - axios.get("/session") - .then(({ data }) => { - const session: Session = { - user: { - name: data.username - } - }; - - logger.debug("[shell] Loaded the user session:", session); - - sessionManager.setSession(session); - - setIsReady(true); - }) - .catch((error: unknown) => { - setIsReady(true); - - if (axios.isAxiosError(error) && error.response?.status === 401) { - // The authentication boundary will redirect to the login page. - return; - } - - throw error; - }); - } else { - setIsReady(true); - } - } else { - throw new Error(`[shell] There's no matching route for the location: "${location}". Did you add routes to React Router without using the runtime.registerRoute() function?`); - } - } - }, [areModulesReady, isMswStarted, runtime.routes, router, logger]); - - if (!isReady) { - return
Loading...
; - } + const appRouter = useAppRouter(sessionManager, { + waitForMsw: process.env.USE_MSW as unknown as boolean + }); return ( - + {appRouter} ); } diff --git a/sample/local-module/src/dev/App.tsx b/sample/local-module/src/dev/App.tsx index 4ec07d97d..48291f3b0 100644 --- a/sample/local-module/src/dev/App.tsx +++ b/sample/local-module/src/dev/App.tsx @@ -1,13 +1,8 @@ import { useAppRouter } from "@sample/shell"; -import { RouterProvider } from "react-router-dom"; +import { sessionManager } from "./session.ts"; export function App() { - const router = useAppRouter(); - - return ( - - ); + return useAppRouter(sessionManager, { + waitForMsw: process.env.USE_MSW as unknown as boolean + }); } diff --git a/sample/remote-module/src/dev/App.tsx b/sample/remote-module/src/dev/App.tsx index decc98576..5f8386d66 100644 --- a/sample/remote-module/src/dev/App.tsx +++ b/sample/remote-module/src/dev/App.tsx @@ -1,16 +1,15 @@ import { BackgroundColorContext } from "@sample/shared"; import { useAppRouter } from "@sample/shell"; -import { RouterProvider } from "react-router-dom"; +import { sessionManager } from "./session.ts"; export function App() { - const router = useAppRouter(); + const appRouter = useAppRouter(sessionManager, { + waitForMsw: process.env.USE_MSW as unknown as boolean + }); return ( - + {appRouter} ); } diff --git a/sample/shell/package.json b/sample/shell/package.json index 3c20edd44..a38561975 100644 --- a/sample/shell/package.json +++ b/sample/shell/package.json @@ -23,6 +23,7 @@ "@sample/shared": "*", "@squide/react-router": "*", "@squide/msw": "*", + "@squide/webpack-module-federation": "*", "axios": "*", "msw": "*", "react": "*", @@ -34,6 +35,7 @@ "@sample/shared": "workspace:*", "@squide/react-router": "workspace:*", "@squide/msw": "workspace:*", + "@squide/webpack-module-federation": "workspace:*", "@types/react": "18.2.22", "@types/react-dom": "18.2.7", "@workleap/eslint-plugin": "2.1.1", diff --git a/sample/shell/src/AuthenticatedLayout.tsx b/sample/shell/src/AuthenticatedLayout.tsx index 5998aa89b..b4ef96e14 100644 --- a/sample/shell/src/AuthenticatedLayout.tsx +++ b/sample/shell/src/AuthenticatedLayout.tsx @@ -76,6 +76,8 @@ export function AuthenticatedLayout({ sessionManager }: AuthenticatedLayoutProps const navigationItems = useNavigationItems(); const renderedNavigationItems = useRenderedNavigationItems(navigationItems, renderItem, renderSection); + console.log("$$$$$$$$ Hello from authenticated layout"); + return ( <>
diff --git a/sample/shell/src/Login.tsx b/sample/shell/src/Login.tsx index e2d16f728..4703761b1 100644 --- a/sample/shell/src/Login.tsx +++ b/sample/shell/src/Login.tsx @@ -1,7 +1,7 @@ import { useIsAuthenticated } from "@squide/react-router"; import axios from "axios"; import { useCallback, useState, type ChangeEvent, type MouseEvent } from "react"; -import { Navigate } from "react-router-dom"; +import { Navigate, useNavigate } from "react-router-dom"; export function Login() { const [username, setUserName] = useState(""); @@ -19,7 +19,12 @@ export function Login() { .then(() => { setIsBusy(false); - // Reloading the application so the App.tsx code is runned again. + // Reloading the whole application so the "App.tsx" component is re-rendered. Ideally, "useNavigate" would be + // used so "App.tsx" component would re-renderer everytime the location change but it doesn't + // seem feasible (at least not easily) as public and private routes go through the "App.tsx" component. + // Anyhow, since all the Workleap apps will authenticate through a third party authentication provider, it + // doesn't seems like a big deal as the application will be reloaded anyway after the user logged in on the third party. + // application will be reloaded anyway after the login on the third party. // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore window.location = "/"; diff --git a/sample/shell/src/register.tsx b/sample/shell/src/register.tsx index 095476544..a690013fc 100644 --- a/sample/shell/src/register.tsx +++ b/sample/shell/src/register.tsx @@ -55,6 +55,7 @@ function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { ] } ] + } ] } diff --git a/sample/shell/src/useAppRouter.tsx b/sample/shell/src/useAppRouter.tsx index c1f568e8d..ca85c7882 100644 --- a/sample/shell/src/useAppRouter.tsx +++ b/sample/shell/src/useAppRouter.tsx @@ -1,15 +1,98 @@ -import { useRoutes } from "@squide/react-router"; -import { useMemo } from "react"; -import { createBrowserRouter } from "react-router-dom"; -// Importing the Router type to prevent: error TS2742: The inferred type of 'useAppRouter' cannot be named without a reference -import type { Router } from "@remix-run/router"; +import type { Session, SessionManager } from "@sample/shared"; +import { useIsMswStarted } from "@squide/msw"; +import { useIsActiveRouteProtected, useLogger, useRoutes } from "@squide/react-router"; +import { useAreModulesReady } from "@squide/webpack-module-federation"; +import axios from "axios"; +import { useEffect, useMemo, useState } from "react"; +import { Outlet, RouterProvider, createBrowserRouter } from "react-router-dom"; -export function useAppRouter(): Router { +interface BootstrappingRouteProps { + sessionManager: SessionManager; + waitForMsw?: boolean; +} + +function BootstrappingRoute({ sessionManager, waitForMsw = false }: BootstrappingRouteProps) { + const [isReady, setIsReady] = useState(false); + + const logger = useLogger(); + + // Re-render the app once all the remotes are registered, otherwise the remotes routes won't be added to the router. + const areModulesReady = useAreModulesReady(); + + // Re-render the app once MSW is started, otherwise, the API calls for module routes will return a 404 status. + const isMswStarted = useIsMswStarted(waitForMsw); + + // Ideally "useLocation" would be used so the component re-renderer everytime the location change but it doesn't + // seem feasible (at least not easily) as public and private routes go through this component. + // Anyhow, since all the Workleap apps will authenticate through a third party authentication provider, it + // doesn't seems like a big deal as the application will be reloaded anyway after the user logged in on the third party. + const isActiveRouteProtected = useIsActiveRouteProtected(window.location); + + useEffect(() => { + if (areModulesReady && isMswStarted) { + if (isActiveRouteProtected) { + logger.debug(`[shell] Fetching session data as "${window.location}" is a protected route.`); + + axios.get("/session") + .then(({ data }) => { + const session: Session = { + user: { + name: data.username + } + }; + + logger.debug("[shell] Loaded the user session:", session); + + sessionManager.setSession(session); + + setIsReady(true); + }) + .catch((error: unknown) => { + setIsReady(true); + + if (axios.isAxiosError(error) && error.response?.status === 401) { + // The authentication boundary will redirect to the login page. + return; + } + + throw error; + }); + } else { + logger.debug(`[shell] Passing through as "${window.location}" is a public route.`); + + setIsReady(true); + } + } + }, [areModulesReady, isMswStarted, isActiveRouteProtected, logger, sessionManager]); + + if (!isReady) { + return
Loading...
; + } + + return ; +} + +export interface UseAppRouterOptions { + waitForMsw?: boolean; +} + +export function useAppRouter(sessionManager: SessionManager, { waitForMsw }: UseAppRouterOptions = {}) { const routes = useRoutes(); const router = useMemo(() => { - return createBrowserRouter(routes); - }, [routes]); + return createBrowserRouter([ + { + // Pathless route to initialize the application. + element: , + children: routes + } + ]); + }, [routes, sessionManager, waitForMsw]); - return router; + return ( + + ); } From 6c6e40b77c4d7ad2d192ff6e5c923e201a1ba2d8 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Wed, 11 Oct 2023 00:19:52 -0400 Subject: [PATCH 10/51] Improved console logs --- packages/core/src/runtime/abstractRuntime.ts | 2 +- packages/msw/src/useIsMswReady.ts | 2 +- packages/react-router/src/runtime.ts | 6 +++--- sample/shell/src/AuthenticatedLayout.tsx | 2 -- sample/shell/src/useAppRouter.tsx | 2 +- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/core/src/runtime/abstractRuntime.ts b/packages/core/src/runtime/abstractRuntime.ts index bc6c0ed89..de597d306 100644 --- a/packages/core/src/runtime/abstractRuntime.ts +++ b/packages/core/src/runtime/abstractRuntime.ts @@ -103,6 +103,6 @@ export abstract class AbstractRuntime { if (mswPlugin.isStarted) { - logger.debug("[squide] MSW is ready."); + logger.debug("[squide] %cMSW is ready%c.", "color: white; background-color: green;", ""); // Must clear interval before calling "_completeRegistration" in case there's an error. clearInterval(intervalId); diff --git a/packages/react-router/src/runtime.ts b/packages/react-router/src/runtime.ts index 1891a60cb..5185594fe 100644 --- a/packages/react-router/src/runtime.ts +++ b/packages/react-router/src/runtime.ts @@ -22,7 +22,7 @@ export class Runtime extends AbstractRuntime { if (result.completedPendingRegistrations.length > 0) { this._logger.debug( - `[squide] The pending registration of the following route${result.completedPendingRegistrations.length > 0 ? "s" : ""} has been %ccompleted%c.`, "color: white; background-color: #26bfa5;", "%s", + `[squide] The pending registration of the following route${result.completedPendingRegistrations.length > 0 ? "s" : ""} has been %ccompleted%c.`, "color: white; background-color: green;", "%s", "Newly registered routes:", result.completedPendingRegistrations, "All registered routes:", this.#routeRegistry.routes ); @@ -31,7 +31,7 @@ export class Runtime extends AbstractRuntime { const parentId = options.parentPath ?? options.parentName; this._logger.debug( - `[squide] The following route registration is %cpending%c until "${parentId}" is registered.`, "color: white; background-color: #007acc;", "%s", + `[squide] The following route registration is %cpending%c until "${parentId}" is registered.`, "color: black; background-color: yellow;", "%s", "Pending registration:", route, "All registered routes:", this.#routeRegistry.routes ); @@ -48,7 +48,7 @@ export class Runtime extends AbstractRuntime { const items = this.#navigationItemRegistry.getItems(menuId)!; this._logger.debug( - `[squide] The following navigation item has been registered to the "${menuId}" menu for a total of ${items.length} item${items.length !== 1 ? "s" : ""}.`, + `[squide] The following navigation item has been %cregistered%c to the "${menuId}" menu for a total of ${items.length} item${items.length !== 1 ? "s" : ""}.`, "color: white; background-color: green;", "%s", "Newly registered item:", navigationItem, "All registered items:", this.#navigationItemRegistry.getItems(menuId) ); diff --git a/sample/shell/src/AuthenticatedLayout.tsx b/sample/shell/src/AuthenticatedLayout.tsx index b4ef96e14..5998aa89b 100644 --- a/sample/shell/src/AuthenticatedLayout.tsx +++ b/sample/shell/src/AuthenticatedLayout.tsx @@ -76,8 +76,6 @@ export function AuthenticatedLayout({ sessionManager }: AuthenticatedLayoutProps const navigationItems = useNavigationItems(); const renderedNavigationItems = useRenderedNavigationItems(navigationItems, renderItem, renderSection); - console.log("$$$$$$$$ Hello from authenticated layout"); - return ( <>
diff --git a/sample/shell/src/useAppRouter.tsx b/sample/shell/src/useAppRouter.tsx index ca85c7882..a0f151646 100644 --- a/sample/shell/src/useAppRouter.tsx +++ b/sample/shell/src/useAppRouter.tsx @@ -41,7 +41,7 @@ function BootstrappingRoute({ sessionManager, waitForMsw = false }: Bootstrappin } }; - logger.debug("[shell] Loaded the user session:", session); + logger.debug("[shell] %cSession is ready%c:", "color: white; background-color: green;", "", session); sessionManager.setSession(session); From e76f4a325b1d771ead517ec414bd076dd05630c2 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Wed, 11 Oct 2023 01:27:24 -0400 Subject: [PATCH 11/51] Minor fixes --- sample/shell/src/Login.tsx | 6 ++---- sample/shell/src/useAppRouter.tsx | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/sample/shell/src/Login.tsx b/sample/shell/src/Login.tsx index 4703761b1..220d94912 100644 --- a/sample/shell/src/Login.tsx +++ b/sample/shell/src/Login.tsx @@ -1,7 +1,7 @@ import { useIsAuthenticated } from "@squide/react-router"; import axios from "axios"; import { useCallback, useState, type ChangeEvent, type MouseEvent } from "react"; -import { Navigate, useNavigate } from "react-router-dom"; +import { Navigate } from "react-router-dom"; export function Login() { const [username, setUserName] = useState(""); @@ -25,9 +25,7 @@ export function Login() { // Anyhow, since all the Workleap apps will authenticate through a third party authentication provider, it // doesn't seems like a big deal as the application will be reloaded anyway after the user logged in on the third party. // application will be reloaded anyway after the login on the third party. - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - window.location = "/"; + window.location.href = "/"; }) .catch((error: unknown) => { setIsBusy(false); diff --git a/sample/shell/src/useAppRouter.tsx b/sample/shell/src/useAppRouter.tsx index a0f151646..489bc5390 100644 --- a/sample/shell/src/useAppRouter.tsx +++ b/sample/shell/src/useAppRouter.tsx @@ -6,12 +6,12 @@ import axios from "axios"; import { useEffect, useMemo, useState } from "react"; import { Outlet, RouterProvider, createBrowserRouter } from "react-router-dom"; -interface BootstrappingRouteProps { +interface RootRouteProps { sessionManager: SessionManager; waitForMsw?: boolean; } -function BootstrappingRoute({ sessionManager, waitForMsw = false }: BootstrappingRouteProps) { +function RootRoute({ sessionManager, waitForMsw = false }: RootRouteProps) { const [isReady, setIsReady] = useState(false); const logger = useLogger(); @@ -83,7 +83,7 @@ export function useAppRouter(sessionManager: SessionManager, { waitForMsw }: Use return createBrowserRouter([ { // Pathless route to initialize the application. - element: , + element: , children: routes } ]); From 069f4cdf1065786efc19d167ac1bf1420b0d0410 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Wed, 11 Oct 2023 01:50:19 -0400 Subject: [PATCH 12/51] Renamed useActiveRoute for useMatchingRoute --- packages/react-router/src/index.ts | 2 +- .../src/{useActiveRoute.ts => useMatchingRoute.ts} | 6 +++--- sample/shell/src/useAppRouter.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename packages/react-router/src/{useActiveRoute.ts => useMatchingRoute.ts} (81%) diff --git a/packages/react-router/src/index.ts b/packages/react-router/src/index.ts index 1f74e33de..7b7fbb301 100644 --- a/packages/react-router/src/index.ts +++ b/packages/react-router/src/index.ts @@ -4,7 +4,7 @@ export * from "./navigationItemRegistry.ts"; export * from "./outlets.ts"; export * from "./routeRegistry.ts"; export * from "./runtime.ts"; -export * from "./useActiveRoute.ts"; +export * from "./useMatchingRoute.ts"; export * from "./useNavigationItems.ts"; export * from "./useRenderedNavigationItems.tsx"; export * from "./useRoutes.ts"; diff --git a/packages/react-router/src/useActiveRoute.ts b/packages/react-router/src/useMatchingRoute.ts similarity index 81% rename from packages/react-router/src/useActiveRoute.ts rename to packages/react-router/src/useMatchingRoute.ts index c4356718a..984b8d273 100644 --- a/packages/react-router/src/useActiveRoute.ts +++ b/packages/react-router/src/useMatchingRoute.ts @@ -1,7 +1,7 @@ import { matchRoutes } from "react-router-dom"; import { useRoutes } from "./useRoutes.ts"; -export function useActiveRoute(locationArg: Partial) { +export function useMatchingRoute(locationArg: Partial) { const routes = useRoutes(); const matchingRoutes = matchRoutes(routes, locationArg) ?? []; @@ -15,8 +15,8 @@ export function useActiveRoute(locationArg: Partial) { return undefined; } -export function useIsActiveRouteProtected(locationArg: Partial) { - const activeRoute = useActiveRoute(locationArg); +export function useIsMatchingRouteProtected(locationArg: Partial) { + const activeRoute = useMatchingRoute(locationArg); if (!activeRoute) { throw new Error(`[squide] There's no matching route for the location: "${locationArg.pathname}". Did you add routes to React Router without using the runtime.registerRoute() function?`); diff --git a/sample/shell/src/useAppRouter.tsx b/sample/shell/src/useAppRouter.tsx index 489bc5390..ebc26ce44 100644 --- a/sample/shell/src/useAppRouter.tsx +++ b/sample/shell/src/useAppRouter.tsx @@ -1,6 +1,6 @@ import type { Session, SessionManager } from "@sample/shared"; import { useIsMswStarted } from "@squide/msw"; -import { useIsActiveRouteProtected, useLogger, useRoutes } from "@squide/react-router"; +import { useIsMatchingRouteProtected, useLogger, useRoutes } from "@squide/react-router"; import { useAreModulesReady } from "@squide/webpack-module-federation"; import axios from "axios"; import { useEffect, useMemo, useState } from "react"; @@ -26,7 +26,7 @@ function RootRoute({ sessionManager, waitForMsw = false }: RootRouteProps) { // seem feasible (at least not easily) as public and private routes go through this component. // Anyhow, since all the Workleap apps will authenticate through a third party authentication provider, it // doesn't seems like a big deal as the application will be reloaded anyway after the user logged in on the third party. - const isActiveRouteProtected = useIsActiveRouteProtected(window.location); + const isActiveRouteProtected = useIsMatchingRouteProtected(window.location); useEffect(() => { if (areModulesReady && isMswStarted) { From c191e8cd1a14fc2f195081d467d3b3c3877aa56c Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Wed, 11 Oct 2023 10:56:16 -0400 Subject: [PATCH 13/51] Remote routes were not working anymore --- docs/reference/runtime/runtime-class.md | 6 +-- packages/react-router/src/routeRegistry.ts | 4 +- packages/react-router/src/useMatchingRoute.ts | 2 +- packages/react-router/tests/runtime.test.tsx | 48 +++++++++---------- sample/another-remote-module/src/dev/App.tsx | 4 +- sample/host/src/App.tsx | 4 +- sample/local-module/src/dev/App.tsx | 4 +- sample/remote-module/src/dev/App.tsx | 4 +- sample/shell/src/register.tsx | 1 - sample/shell/src/useAppRouter.tsx | 34 +++---------- 10 files changed, 41 insertions(+), 70 deletions(-) diff --git a/docs/reference/runtime/runtime-class.md b/docs/reference/runtime/runtime-class.md index afc8f15ba..365900f81 100644 --- a/docs/reference/runtime/runtime-class.md +++ b/docs/reference/runtime/runtime-class.md @@ -61,7 +61,7 @@ runtime.registerRoute(route, options?: {}) - `route`: accept any properties of a React Router [Route](https://reactrouter.com/en/main/components/route) component with the addition of: - `name`: An optional name for the route. - - `visibility`: An optional visibility indicator for the route. Accepted values are `"public"` or `"authenticated"`. + - `visibility`: An optional visibility indicator for the route. Accepted values are `"public"` or `"protected"`. - `options`: An optional object literal of options: - `hoist`: An optional boolean value to register the route at the root of the router. The default value is `false`. - `parentPath`: An optional path of a parent route to register this new route under. @@ -96,7 +96,7 @@ runtime.registerRoute({ ### Register a public route -When registering a route, a hint can be provided, indicating if the route is intended to be displayed as a `public` or `authenticated` route. This is especially useful when dealing with code that conditionally fetch data for authenticated routes (e.g. a session). +When registering a route, a hint can be provided, indicating if the route is intended to be displayed as a `public` or `protected` route. This is especially useful when dealing with code that conditionally fetch data for protected routes (e.g. a session). ```tsx !#4 import { Page } from "./Page.tsx"; @@ -129,7 +129,7 @@ runtime.registerRoute({ ``` !!!info -When no visibility hint is provided, a route is considered as an `authenticated` route. +When no visibility hint is provided, a route is considered as a `protected` route. !!! ### Register a named route diff --git a/packages/react-router/src/routeRegistry.ts b/packages/react-router/src/routeRegistry.ts index 89ed60051..1414769d1 100644 --- a/packages/react-router/src/routeRegistry.ts +++ b/packages/react-router/src/routeRegistry.ts @@ -2,7 +2,7 @@ import type { RegisterRouteOptions } from "@squide/core"; import type { IndexRouteObject, NonIndexRouteObject } from "react-router-dom"; import { ManagedRoutesOutletName, isManagedRoutesOutletRoute } from "./outlets.ts"; -export type RouteVisibility = "public" | "authenticated"; +export type RouteVisibility = "public" | "protected"; export interface IndexRoute extends IndexRouteObject { name?: string; @@ -77,7 +77,7 @@ export class RouteRegistry { // Creates a copy of the route object and add the default properties. const route = { ...x, - visibility: x.visibility ?? "authenticated" + visibility: x.visibility ?? "protected" }; if (route.children) { diff --git a/packages/react-router/src/useMatchingRoute.ts b/packages/react-router/src/useMatchingRoute.ts index 984b8d273..a85a395d0 100644 --- a/packages/react-router/src/useMatchingRoute.ts +++ b/packages/react-router/src/useMatchingRoute.ts @@ -22,5 +22,5 @@ export function useIsMatchingRouteProtected(locationArg: Partial) { throw new Error(`[squide] There's no matching route for the location: "${locationArg.pathname}". Did you add routes to React Router without using the runtime.registerRoute() function?`); } - return activeRoute.visibility === "authenticated"; + return activeRoute.visibility === "protected"; } diff --git a/packages/react-router/tests/runtime.test.tsx b/packages/react-router/tests/runtime.test.tsx index c152c3776..443baab0a 100644 --- a/packages/react-router/tests/runtime.test.tsx +++ b/packages/react-router/tests/runtime.test.tsx @@ -183,25 +183,25 @@ describe("registerRoute", () => { expect(routes[0].visibility).toBe("public"); }); - test("can register a root route with a \"authenticated\" visibility", () => { + test("can register a root route with a \"protected\" visibility", () => { const runtime = new Runtime(); registerManagedRoutesOutlet(runtime); runtime.registerRoute({ - path: "/authenticated", + path: "/protected", element:
Hello!
, - visibility: "authenticated" + visibility: "protected" }); const routes = getManagedRoutes(runtime.routes)!; expect(routes.length).toBe(1); - expect(routes[0].path).toBe("/authenticated"); - expect(routes[0].visibility).toBe("authenticated"); + expect(routes[0].path).toBe("/protected"); + expect(routes[0].visibility).toBe("protected"); }); - test("when a root route has no visibility property, it is considered as an \"authenticated\" route", () => { + test("when a root route has no visibility property, it is considered as an \"protected\" route", () => { const runtime = new Runtime(); registerManagedRoutesOutlet(runtime); @@ -215,7 +215,7 @@ describe("registerRoute", () => { expect(routes.length).toBe(1); expect(routes[0].path).toBe("/foo"); - expect(routes[0].visibility).toBe("authenticated"); + expect(routes[0].visibility).toBe("protected"); }); test("can register a nested route with a \"public\" visibility", () => { @@ -241,7 +241,7 @@ describe("registerRoute", () => { expect(routes[0].children![0].visibility).toBe("public"); }); - test("can register a nested route with a \"authenticated\" visibility", () => { + test("can register a nested route with a \"protected\" visibility", () => { const runtime = new Runtime(); registerManagedRoutesOutlet(runtime); @@ -251,7 +251,7 @@ describe("registerRoute", () => { element:
Hello!
, children: [ { - visibility: "authenticated", + visibility: "protected", path: "/layout/nested", element:
Hello!
} @@ -261,10 +261,10 @@ describe("registerRoute", () => { const routes = getManagedRoutes(runtime.routes)!; expect(routes[0].children![0].path).toBe("/layout/nested"); - expect(routes[0].children![0].visibility).toBe("authenticated"); + expect(routes[0].children![0].visibility).toBe("protected"); }); - test("when a nested route has no visibility property, it is considered as an \"authenticated\" route", () => { + test("when a nested route has no visibility property, it is considered as a \"protected\" route", () => { const runtime = new Runtime(); registerManagedRoutesOutlet(runtime); @@ -283,7 +283,7 @@ describe("registerRoute", () => { const routes = getManagedRoutes(runtime.routes)!; expect(routes[0].children![0].path).toBe("/layout/nested"); - expect(routes[0].children![0].visibility).toBe("authenticated"); + expect(routes[0].children![0].visibility).toBe("protected"); }); test("can register a root route with a name", () => { @@ -446,22 +446,22 @@ describe("registerRoute", () => { expect(runtime.routes[0].visibility).toBe("public"); }); - test("can register a root route with a \"authenticated\" visibility", () => { + test("can register a root route with a \"protected\" visibility", () => { const runtime = new Runtime(); runtime.registerRoute({ - path: "/authenticated", + path: "/protected", element:
Hello!
, - visibility: "authenticated" + visibility: "protected" }, { hoist: true }); - expect(runtime.routes[0].path).toBe("/authenticated"); - expect(runtime.routes[0].visibility).toBe("authenticated"); + expect(runtime.routes[0].path).toBe("/protected"); + expect(runtime.routes[0].visibility).toBe("protected"); }); - test("when a root route has no visibility property, it is considered as an \"authenticated\" route", () => { + test("when a root route has no visibility property, it is considered as an \"protected\" route", () => { const runtime = new Runtime(); runtime.registerRoute({ @@ -472,7 +472,7 @@ describe("registerRoute", () => { }); expect(runtime.routes[0].path).toBe("/foo"); - expect(runtime.routes[0].visibility).toBe("authenticated"); + expect(runtime.routes[0].visibility).toBe("protected"); }); test("can register a nested route with a \"public\" visibility", () => { @@ -496,7 +496,7 @@ describe("registerRoute", () => { expect(runtime.routes[0].children![0].visibility).toBe("public"); }); - test("can register a nested route with a \"authenticated\" visibility", () => { + test("can register a nested route with a \"protected\" visibility", () => { const runtime = new Runtime(); runtime.registerRoute({ @@ -504,7 +504,7 @@ describe("registerRoute", () => { element:
Hello!
, children: [ { - visibility: "authenticated", + visibility: "protected", path: "/layout/nested", element:
Hello!
} @@ -514,10 +514,10 @@ describe("registerRoute", () => { }); expect(runtime.routes[0].children![0].path).toBe("/layout/nested"); - expect(runtime.routes[0].children![0].visibility).toBe("authenticated"); + expect(runtime.routes[0].children![0].visibility).toBe("protected"); }); - test("when a nested route has no visibility property, it is considered as an \"authenticated\" route", () => { + test("when a nested route has no visibility property, it is considered as an \"protected\" route", () => { const runtime = new Runtime(); runtime.registerRoute({ @@ -534,7 +534,7 @@ describe("registerRoute", () => { }); expect(runtime.routes[0].children![0].path).toBe("/layout/nested"); - expect(runtime.routes[0].children![0].visibility).toBe("authenticated"); + expect(runtime.routes[0].children![0].visibility).toBe("protected"); }); test("can register a root route with a name", () => { diff --git a/sample/another-remote-module/src/dev/App.tsx b/sample/another-remote-module/src/dev/App.tsx index 48291f3b0..41e44d807 100644 --- a/sample/another-remote-module/src/dev/App.tsx +++ b/sample/another-remote-module/src/dev/App.tsx @@ -2,7 +2,5 @@ import { useAppRouter } from "@sample/shell"; import { sessionManager } from "./session.ts"; export function App() { - return useAppRouter(sessionManager, { - waitForMsw: process.env.USE_MSW as unknown as boolean - }); + return useAppRouter(process.env.USE_MSW as unknown as boolean, sessionManager); } diff --git a/sample/host/src/App.tsx b/sample/host/src/App.tsx index 8affd330c..24ca34efc 100644 --- a/sample/host/src/App.tsx +++ b/sample/host/src/App.tsx @@ -3,9 +3,7 @@ import { useAppRouter } from "@sample/shell"; import { sessionManager } from "./session.ts"; export function App() { - const appRouter = useAppRouter(sessionManager, { - waitForMsw: process.env.USE_MSW as unknown as boolean - }); + const appRouter = useAppRouter(process.env.USE_MSW as unknown as boolean, sessionManager); return ( diff --git a/sample/local-module/src/dev/App.tsx b/sample/local-module/src/dev/App.tsx index 48291f3b0..41e44d807 100644 --- a/sample/local-module/src/dev/App.tsx +++ b/sample/local-module/src/dev/App.tsx @@ -2,7 +2,5 @@ import { useAppRouter } from "@sample/shell"; import { sessionManager } from "./session.ts"; export function App() { - return useAppRouter(sessionManager, { - waitForMsw: process.env.USE_MSW as unknown as boolean - }); + return useAppRouter(process.env.USE_MSW as unknown as boolean, sessionManager); } diff --git a/sample/remote-module/src/dev/App.tsx b/sample/remote-module/src/dev/App.tsx index 5f8386d66..fabbb2ec8 100644 --- a/sample/remote-module/src/dev/App.tsx +++ b/sample/remote-module/src/dev/App.tsx @@ -3,9 +3,7 @@ import { useAppRouter } from "@sample/shell"; import { sessionManager } from "./session.ts"; export function App() { - const appRouter = useAppRouter(sessionManager, { - waitForMsw: process.env.USE_MSW as unknown as boolean - }); + const appRouter = useAppRouter(process.env.USE_MSW as unknown as boolean, sessionManager); return ( diff --git a/sample/shell/src/register.tsx b/sample/shell/src/register.tsx index a690013fc..095476544 100644 --- a/sample/shell/src/register.tsx +++ b/sample/shell/src/register.tsx @@ -55,7 +55,6 @@ function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { ] } ] - } ] } diff --git a/sample/shell/src/useAppRouter.tsx b/sample/shell/src/useAppRouter.tsx index ebc26ce44..5deab9621 100644 --- a/sample/shell/src/useAppRouter.tsx +++ b/sample/shell/src/useAppRouter.tsx @@ -4,17 +4,13 @@ import { useIsMatchingRouteProtected, useLogger, useRoutes } from "@squide/react import { useAreModulesReady } from "@squide/webpack-module-federation"; import axios from "axios"; import { useEffect, useMemo, useState } from "react"; -import { Outlet, RouterProvider, createBrowserRouter } from "react-router-dom"; +import { RouterProvider, createBrowserRouter } from "react-router-dom"; -interface RootRouteProps { - sessionManager: SessionManager; - waitForMsw?: boolean; -} - -function RootRoute({ sessionManager, waitForMsw = false }: RootRouteProps) { +export function useAppRouter(waitForMsw: boolean, sessionManager: SessionManager) { const [isReady, setIsReady] = useState(false); const logger = useLogger(); + const routes = useRoutes(); // Re-render the app once all the remotes are registered, otherwise the remotes routes won't be added to the router. const areModulesReady = useAreModulesReady(); @@ -65,30 +61,14 @@ function RootRoute({ sessionManager, waitForMsw = false }: RootRouteProps) { } }, [areModulesReady, isMswStarted, isActiveRouteProtected, logger, sessionManager]); + const router = useMemo(() => { + return createBrowserRouter(routes); + }, [routes]); + if (!isReady) { return
Loading...
; } - return ; -} - -export interface UseAppRouterOptions { - waitForMsw?: boolean; -} - -export function useAppRouter(sessionManager: SessionManager, { waitForMsw }: UseAppRouterOptions = {}) { - const routes = useRoutes(); - - const router = useMemo(() => { - return createBrowserRouter([ - { - // Pathless route to initialize the application. - element: , - children: routes - } - ]); - }, [routes, sessionManager, waitForMsw]); - return ( Date: Wed, 11 Oct 2023 20:10:49 -0400 Subject: [PATCH 14/51] Replaced router loader by react query --- .../tests/defineConfig.test.ts | 38 +++++++++++++++++++ pnpm-lock.yaml | 9 +++++ sample/another-remote-module/package.json | 1 + sample/another-remote-module/src/register.tsx | 6 ++- sample/local-module/package.json | 1 + sample/local-module/src/register.tsx | 10 +++-- sample/remote-module/mocks/handlers.ts | 6 ++- sample/remote-module/package.json | 1 + sample/remote-module/src/Fetch.tsx | 13 +++++-- sample/remote-module/src/register.tsx | 37 ++++++++++++++---- sample/shell/src/ModuleErrorBoundary.tsx | 10 ++++- sample/shell/src/RootErrorBoundary.tsx | 10 ++++- sample/shell/src/register.tsx | 10 +++++ 13 files changed, 134 insertions(+), 18 deletions(-) diff --git a/packages/webpack-module-federation/tests/defineConfig.test.ts b/packages/webpack-module-federation/tests/defineConfig.test.ts index b2d7b1e5e..dab9c5e85 100644 --- a/packages/webpack-module-federation/tests/defineConfig.test.ts +++ b/packages/webpack-module-federation/tests/defineConfig.test.ts @@ -119,6 +119,18 @@ describe("defineDevHostConfig", () => { expect(result).toBeDefined(); }); + + test("when configuration transformers are provided, the transformers are applied to the configuration", () => { + const result = defineDevHostConfig(SwcConfig, "host", 8080, { + transformers: [(config: WebpackConfig) => { + config.entry = "updated by the dummy transformer"; + + return config; + }] + }); + + expect(result.entry).toBe("updated by the dummy transformer"); + }); }); @@ -215,6 +227,18 @@ describe("defineBuildHostConfig", () => { expect(result).toBeDefined(); }); + + test("when configuration transformers are provided, the transformers are applied to the configuration", () => { + const result = defineBuildHostConfig(SwcConfig, "host", "http://localhost:8080/", { + transformers: [(config: WebpackConfig) => { + config.entry = "updated by the dummy transformer"; + + return config; + }] + }); + + expect(result.entry).toBe("updated by the dummy transformer"); + }); }); @@ -439,6 +463,20 @@ describe("defineBuildRemoteModuleConfig", () => { expect(result).toBeDefined(); }); + + test("when configuration transformers are provided, the transformers are applied to the configuration", () => { + const dummyTransformer = (config: WebpackConfig) => { + config.entry = "updated by the dummy transformer"; + + return config; + }; + + const result = defineBuildRemoteModuleConfig(SwcConfig, "remote1", "http://localhost:8081/", { + transformers: [dummyTransformer] + }); + + expect(result.entry).toBe("updated by the dummy transformer"); + }); }); describe("defineHostModuleFederationPluginOptions", () => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 57b13b3a3..da6aacb69 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -353,6 +353,9 @@ importers: '@workleap/webpack-configs': specifier: 1.0.8 version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) + axios: + specifier: 1.5.1 + version: 1.5.1(debug@4.3.4) browserslist: specifier: 4.21.10 version: 4.21.10 @@ -529,6 +532,9 @@ importers: '@workleap/webpack-configs': specifier: 1.0.8 version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(esbuild@0.18.20)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) + axios: + specifier: 1.5.1 + version: 1.5.1(debug@4.3.4) browserslist: specifier: 4.21.10 version: 4.21.10 @@ -620,6 +626,9 @@ importers: '@workleap/webpack-configs': specifier: 1.0.8 version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) + axios: + specifier: 1.5.1 + version: 1.5.1(debug@4.3.4) browserslist: specifier: 4.21.10 version: 4.21.10 diff --git a/sample/another-remote-module/package.json b/sample/another-remote-module/package.json index 3aed9cc89..3ecd9b5e9 100644 --- a/sample/another-remote-module/package.json +++ b/sample/another-remote-module/package.json @@ -25,6 +25,7 @@ "@workleap/swc-configs": "2.1.2", "@workleap/typescript-configs": "3.0.2", "@workleap/webpack-configs": "1.0.8", + "axios": "1.5.1", "browserslist": "4.21.10", "cross-env": "7.0.3", "http-server": "14.1.1", diff --git a/sample/another-remote-module/src/register.tsx b/sample/another-remote-module/src/register.tsx index ae21a47a5..88b2fd58b 100644 --- a/sample/another-remote-module/src/register.tsx +++ b/sample/another-remote-module/src/register.tsx @@ -1,6 +1,6 @@ import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; -export const register: ModuleRegisterFunction = runtime => { +function registerRoutes(runtime: Runtime) { runtime.registerRoute({ path: "/federated-tabs", lazy: () => import("@sample/shared/FederatedTabsLayout.tsx") @@ -10,4 +10,8 @@ export const register: ModuleRegisterFunction = runtime => { to: "/federated-tabs", label: "Tabs" }); +} + +export const register: ModuleRegisterFunction = runtime => { + registerRoutes(runtime); }; diff --git a/sample/local-module/package.json b/sample/local-module/package.json index 26d89d1f1..434ed44b1 100644 --- a/sample/local-module/package.json +++ b/sample/local-module/package.json @@ -37,6 +37,7 @@ "@workleap/tsup-configs": "3.0.1", "@workleap/typescript-configs": "3.0.2", "@workleap/webpack-configs": "1.0.8", + "axios": "1.5.1", "browserslist": "4.21.10", "cross-env": "7.0.3", "nodemon": "3.0.1", diff --git a/sample/local-module/src/register.tsx b/sample/local-module/src/register.tsx index 5d188defa..58c02fd2e 100644 --- a/sample/local-module/src/register.tsx +++ b/sample/local-module/src/register.tsx @@ -1,9 +1,7 @@ import type { AppContext } from "@sample/shared"; import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; -export const registerLocalModule: ModuleRegisterFunction = (runtime, context) => { - console.log("Local module context: ", context); - +function registerRoutes(runtime: Runtime) { runtime.registerRoute({ path: "/about", lazy: () => import("./About.tsx") @@ -45,4 +43,10 @@ export const registerLocalModule: ModuleRegisterFunction = }, { menuId: "/federated-tabs" }); +} + +export const registerLocalModule: ModuleRegisterFunction = (runtime, context) => { + console.log("Local module context: ", context); + + registerRoutes(runtime); }; diff --git a/sample/remote-module/mocks/handlers.ts b/sample/remote-module/mocks/handlers.ts index 37430410c..c24c83720 100644 --- a/sample/remote-module/mocks/handlers.ts +++ b/sample/remote-module/mocks/handlers.ts @@ -1,7 +1,11 @@ import { rest, type RestHandler } from "msw"; export const requestHandlers: RestHandler[] = [ - rest.get("https://rickandmortyapi.com/api/character/1,2,3,4,5", (req, res, ctx) => { + rest.get("https://rickandmortyapi.com/api/character/1,2,3,4,5", async (req, res, ctx) => { + // return res( + // ctx.status(401) + // ); + return res( ctx.status(200), ctx.json([{ diff --git a/sample/remote-module/package.json b/sample/remote-module/package.json index 8f2c7a98c..eca2659d4 100644 --- a/sample/remote-module/package.json +++ b/sample/remote-module/package.json @@ -26,6 +26,7 @@ "@workleap/swc-configs": "2.1.2", "@workleap/typescript-configs": "3.0.2", "@workleap/webpack-configs": "1.0.8", + "axios": "1.5.1", "browserslist": "4.21.10", "cross-env": "7.0.3", "http-server": "14.1.1", diff --git a/sample/remote-module/src/Fetch.tsx b/sample/remote-module/src/Fetch.tsx index fca552185..09216849b 100644 --- a/sample/remote-module/src/Fetch.tsx +++ b/sample/remote-module/src/Fetch.tsx @@ -1,4 +1,5 @@ -import { useLoaderData } from "react-router-dom"; +import { useSuspenseQuery } from "@tanstack/react-query"; +import axios from "axios"; interface Character { id: number; @@ -7,14 +8,20 @@ interface Character { } export function Fetch() { - const characters = useLoaderData() as Character[]; + const { data: characters } = useSuspenseQuery({ queryKey: ["https://rickandmortyapi.com/api/character/", "1", "2", "3", "4", "5"], queryFn: () => { + return axios + .get("https://rickandmortyapi.com/api/character/1,2,3,4,5") + .then(({ data }) => { + return data; + }); + } }); return (

Fetch

An example fetching data with React Router loaders.

- {characters.map(x => { + {characters.map((x: Character) => { return (
Id: {x.id} diff --git a/sample/remote-module/src/register.tsx b/sample/remote-module/src/register.tsx index b326275a9..fa3d386f6 100644 --- a/sample/remote-module/src/register.tsx +++ b/sample/remote-module/src/register.tsx @@ -1,8 +1,31 @@ import { BackgroundColorContext } from "@sample/shared"; import { getMswPlugin } from "@squide/msw"; import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import type { ReactNode } from "react"; import { requestHandlers } from "../mocks/handlers.ts"; +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + retry: failureCount => { + return failureCount <= 2; + } + } + } +}); + +function Providers({ children }: { children: ReactNode }) { + return ( + + {children} + + + ); +} + function registerRoutes(runtime: Runtime) { runtime.registerRoute({ path: "/remote", @@ -11,14 +34,12 @@ function registerRoutes(runtime: Runtime) { runtime.registerRoute({ path: "/fetch", - lazy: () => import("./Fetch.tsx"), - loader: async function loader() { - return fetch("https://rickandmortyapi.com/api/character/1,2,3,4,5", { - method: "GET", - headers: { - "Accept": "application/json" - } - }); + lazy: async () => { + const { Fetch } = await import("./Fetch.tsx"); + + return { + element: + }; } }); diff --git a/sample/shell/src/ModuleErrorBoundary.tsx b/sample/shell/src/ModuleErrorBoundary.tsx index eed247667..3076f4fa3 100644 --- a/sample/shell/src/ModuleErrorBoundary.tsx +++ b/sample/shell/src/ModuleErrorBoundary.tsx @@ -1,4 +1,5 @@ import { useLogger } from "@squide/react-router"; +import { useCallback } from "react"; import { isRouteErrorResponse, useLocation, useRouteError } from "react-router-dom"; function getErrorMessage(error: unknown) { @@ -16,13 +17,20 @@ export function ModuleErrorBoundary() { const location = useLocation(); const logger = useLogger(); - logger.error(`[sample] An unmanaged error occurred while rendering the route with path ${location.pathname}`, error); + const handleReloadButtonClick = useCallback(() => { + window.location.reload(); + }, []); + + logger.error(`[shell] An unmanaged error occurred while rendering the route with path ${location.pathname}`, error); return (

Unmanaged error

An unmanaged error occurred inside a module. Still, other parts of the application are fully functional!

👉 {getErrorMessage(error)} +
+ +
); } diff --git a/sample/shell/src/RootErrorBoundary.tsx b/sample/shell/src/RootErrorBoundary.tsx index 7cdbe5fed..ebaecb73c 100644 --- a/sample/shell/src/RootErrorBoundary.tsx +++ b/sample/shell/src/RootErrorBoundary.tsx @@ -1,4 +1,5 @@ import { useLogger } from "@squide/react-router"; +import { useCallback } from "react"; import { isRouteErrorResponse, useLocation, useRouteError } from "react-router-dom"; function getErrorMessage(error: unknown) { @@ -16,13 +17,20 @@ export function RootErrorBoundary() { const location = useLocation(); const logger = useLogger(); - logger.error(`[sample] An unmanaged error occurred while rendering the route with path ${location.pathname}`, error); + const handleReloadButtonClick = useCallback(() => { + window.location.reload(); + }, []); + + logger.error(`[shell] An unmanaged error occurred while rendering the route with path ${location.pathname}`, error); return (

Unmanaged error

An unmanaged error occurred and the application is broken, try refreshing your browser.

👉 {getErrorMessage(error)} +
+ +
); } diff --git a/sample/shell/src/register.tsx b/sample/shell/src/register.tsx index 095476544..d6ee55d22 100644 --- a/sample/shell/src/register.tsx +++ b/sample/shell/src/register.tsx @@ -6,6 +6,14 @@ import { authenticationHandlers } from "../mocks/authenticationHandlers.ts"; import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; import { RootLayout } from "./RootLayout.tsx"; +// async function lazyPage(componentPath: string, props: object = {}) { +// const { Component } = await import(`${componentPath}`); + +// return { +// element: +// }; +// } + function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { runtime.registerRoute({ // Pathless route to declare a root layout and a root error boundary. @@ -20,6 +28,7 @@ function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { children: [ { // Pathless route to declare an authenticated boundary. + // lazy: () => lazyElement("./AuthenticationBoundary.tsx"), lazy: async () => { const { AuthenticationBoundary } = await import("./AuthenticationBoundary.tsx"); @@ -30,6 +39,7 @@ function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { children: [ { // Pathless route to declare an authenticated layout. + // lazy: () => lazyPage("./AuthenticatedLayout.tsx", { sessionManager }), lazy: async () => { const { AuthenticatedLayout } = await import("./AuthenticatedLayout.tsx"); From 660db78f44bb52c9c462a7dce69679eb95694989 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Wed, 11 Oct 2023 22:22:16 -0400 Subject: [PATCH 15/51] Added a subscription service --- package.json | 8 +- packages/core/package.json | 7 +- packages/fakes/package.json | 2 +- packages/msw/package.json | 10 +- packages/react-router/package.json | 12 +- .../webpack-module-federation/package.json | 14 +- .../src/defineConfig.ts | 5 +- pnpm-lock.yaml | 1400 +++++++++-------- sample/another-remote-module/package.json | 20 +- sample/another-remote-module/webpack.build.js | 3 - sample/another-remote-module/webpack.dev.js | 6 +- sample/host/package.json | 20 +- sample/host/webpack.build.js | 4 - sample/host/webpack.dev.js | 5 +- sample/local-module/package.json | 18 +- sample/local-module/webpack.config.js | 3 +- sample/remote-module/mocks/handlers.ts | 7 +- sample/remote-module/package.json | 18 +- sample/remote-module/webpack.build.js | 3 - sample/remote-module/webpack.dev.js | 4 +- sample/shared/package.json | 7 +- sample/shared/src/index.ts | 1 + sample/shared/src/session.ts | 1 + sample/shared/src/subscription.ts | 30 + sample/shell/mocks/authenticationHandlers.ts | 14 +- sample/shell/mocks/session.ts | 8 + sample/shell/mocks/subscriptionHandlers.ts | 19 + sample/shell/package.json | 12 +- sample/shell/src/AuthenticatedLayout.tsx | 6 +- sample/shell/src/ModuleErrorBoundary.tsx | 5 +- sample/shell/src/RootErrorBoundary.tsx | 5 +- sample/shell/src/register.tsx | 16 +- sample/shell/src/useAppRouter.tsx | 40 +- 33 files changed, 979 insertions(+), 754 deletions(-) create mode 100644 sample/shared/src/subscription.ts create mode 100644 sample/shell/mocks/session.ts create mode 100644 sample/shell/mocks/subscriptionHandlers.ts diff --git a/package.json b/package.json index 25a5b13f2..9ba70db0b 100644 --- a/package.json +++ b/package.json @@ -37,13 +37,13 @@ "devDependencies": { "@changesets/changelog-github": "0.4.8", "@changesets/cli": "2.26.2", - "@typescript-eslint/parser": "6.7.2", - "@workleap/eslint-plugin": "2.1.1", + "@typescript-eslint/parser": "6.7.5", + "@workleap/eslint-plugin": "3.0.0", "@workleap/typescript-configs": "3.0.2", "cross-env": "7.0.3", - "eslint": "8.49.0", + "eslint": "8.51.0", "jest": "29.7.0", - "netlify-cli": "16.4.1", + "netlify-cli": "16.6.1", "retypeapp": "3.5.0", "ts-node": "10.9.1", "typescript": "5.2.2" diff --git a/packages/core/package.json b/packages/core/package.json index 39f00f7c8..420d32714 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -35,9 +35,9 @@ "react-dom": "*" }, "devDependencies": { - "@types/react": "18.2.22", - "@types/react-dom": "18.2.7", - "@workleap/eslint-plugin": "2.1.1", + "@types/react": "18.2.28", + "@types/react-dom": "18.2.13", + "@workleap/eslint-plugin": "3.0.0", "@workleap/tsup-configs": "3.0.1", "@workleap/typescript-configs": "3.0.2", "react": "18.2.0", @@ -48,7 +48,6 @@ "dependencies": { "eventemitter3": "5.0.1" }, - "sideEffects": false, "engines": { "node": ">=18.0.0" diff --git a/packages/fakes/package.json b/packages/fakes/package.json index 06286baaa..a52d160b3 100644 --- a/packages/fakes/package.json +++ b/packages/fakes/package.json @@ -32,7 +32,7 @@ }, "devDependencies": { "@types/jest": "29.5.5", - "@workleap/eslint-plugin": "2.1.1", + "@workleap/eslint-plugin": "3.0.0", "@workleap/tsup-configs": "3.0.1", "@workleap/typescript-configs": "3.0.2", "jest": "29.7.0", diff --git a/packages/msw/package.json b/packages/msw/package.json index 622f6ea80..e5047ab90 100644 --- a/packages/msw/package.json +++ b/packages/msw/package.json @@ -31,15 +31,15 @@ "build": "tsup --config ./tsup.build.ts" }, "peerDependencies": { + "msw": "*", "react": "*", - "react-dom": "*", - "msw": "*" + "react-dom": "*" }, "devDependencies": { "@types/jest": "29.5.5", - "@types/react": "18.2.22", - "@types/react-dom": "18.2.7", - "@workleap/eslint-plugin": "2.1.1", + "@types/react": "18.2.28", + "@types/react-dom": "18.2.13", + "@workleap/eslint-plugin": "3.0.0", "@workleap/tsup-configs": "3.0.1", "@workleap/typescript-configs": "3.0.2", "jest": "29.7.0", diff --git a/packages/react-router/package.json b/packages/react-router/package.json index 07ff80175..fc7aa4cd2 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -36,15 +36,15 @@ "react-router-dom": "*" }, "devDependencies": { - "@swc/core": "1.3.86", - "@swc/helpers": "0.5.2", + "@swc/core": "1.3.92", + "@swc/helpers": "0.5.3", "@swc/jest": "0.2.29", "@testing-library/react": "14.0.0", "@types/jest": "29.5.5", - "@types/react": "18.2.22", - "@types/react-dom": "18.2.7", - "@types/react-test-renderer": "18.0.2", - "@workleap/eslint-plugin": "2.1.1", + "@types/react": "18.2.28", + "@types/react-dom": "18.2.13", + "@types/react-test-renderer": "18.0.3", + "@workleap/eslint-plugin": "3.0.0", "@workleap/swc-configs": "2.1.2", "@workleap/tsup-configs": "3.0.1", "@workleap/typescript-configs": "3.0.2", diff --git a/packages/webpack-module-federation/package.json b/packages/webpack-module-federation/package.json index fd9d1a82d..41bf584a5 100644 --- a/packages/webpack-module-federation/package.json +++ b/packages/webpack-module-federation/package.json @@ -43,18 +43,18 @@ "webpack": ">=5.0.0" }, "devDependencies": { - "@swc/core": "1.3.86", - "@swc/helpers": "0.5.2", + "@swc/core": "1.3.92", + "@swc/helpers": "0.5.3", "@swc/jest": "0.2.29", "@types/jest": "29.5.5", - "@types/node": "20.6.3", - "@types/react": "18.2.22", - "@types/react-dom": "18.2.7", - "@workleap/eslint-plugin": "2.1.1", + "@types/node": "20.8.4", + "@types/react": "18.2.28", + "@types/react-dom": "18.2.13", + "@workleap/eslint-plugin": "3.0.0", "@workleap/swc-configs": "2.1.2", "@workleap/tsup-configs": "3.0.1", "@workleap/typescript-configs": "3.0.2", - "@workleap/webpack-configs": "1.0.8", + "@workleap/webpack-configs": "1.1.0", "jest": "29.7.0", "react": "18.2.0", "react-dom": "18.2.0", diff --git a/packages/webpack-module-federation/src/defineConfig.ts b/packages/webpack-module-federation/src/defineConfig.ts index ab2e14ecd..7b8c9fa2c 100644 --- a/packages/webpack-module-federation/src/defineConfig.ts +++ b/packages/webpack-module-federation/src/defineConfig.ts @@ -204,7 +204,7 @@ const devRemoteModuleTransformer: WebpackConfigTransformer = (config: WebpackCon return config; }; -export interface DefineDevRemoteModuleConfigOptions extends Omit { +export interface DefineDevRemoteModuleConfigOptions extends Omit { router?: Router; sharedDependencies?: ModuleFederationPluginOptions["shared"]; moduleFederationPluginOptions?: ModuleFederationPluginOptions; @@ -230,6 +230,9 @@ export function defineDevRemoteModuleConfig(swcConfig: SwcConfig, applicationNam cache, fastRefresh: false, htmlWebpackPlugin, + // Disable the error overlay by default for remotes as otherwise every remote module's error overlay will be + // stack on top of the host application's error overlay. + overlay: false, plugins: [ ...plugins, new webpack.container.ModuleFederationPlugin(moduleFederationPluginOptions) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index da6aacb69..11e492350 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,11 +15,11 @@ importers: specifier: 2.26.2 version: 2.26.2 '@typescript-eslint/parser': - specifier: 6.7.2 - version: 6.7.2(eslint@8.49.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(eslint@8.51.0)(typescript@5.2.2) '@workleap/eslint-plugin': - specifier: 2.1.1 - version: 2.1.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) + specifier: 3.0.0 + version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) '@workleap/typescript-configs': specifier: 3.0.2 version: 3.0.2(typescript@5.2.2) @@ -27,20 +27,20 @@ importers: specifier: 7.0.3 version: 7.0.3 eslint: - specifier: 8.49.0 - version: 8.49.0 + specifier: 8.51.0 + version: 8.51.0 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.6.3)(ts-node@10.9.1) + version: 29.7.0(@types/node@20.8.4)(ts-node@10.9.1) netlify-cli: - specifier: 16.4.1 - version: 16.4.1(@types/node@20.6.3) + specifier: 16.6.1 + version: 16.6.1(@types/node@20.8.4) retypeapp: specifier: 3.5.0 version: 3.5.0 ts-node: specifier: 10.9.1 - version: 10.9.1(@types/node@20.6.3)(typescript@5.2.2) + version: 10.9.1(@types/node@20.8.4)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 @@ -52,14 +52,14 @@ importers: version: 5.0.1 devDependencies: '@types/react': - specifier: 18.2.22 - version: 18.2.22 + specifier: 18.2.28 + version: 18.2.28 '@types/react-dom': - specifier: 18.2.7 - version: 18.2.7 + specifier: 18.2.13 + version: 18.2.13 '@workleap/eslint-plugin': - specifier: 2.1.1 - version: 2.1.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) + specifier: 3.0.0 + version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) '@workleap/tsup-configs': specifier: 3.0.1 version: 3.0.1(tsup@7.2.0)(typescript@5.2.2) @@ -74,7 +74,7 @@ importers: version: 18.2.0(react@18.2.0) tsup: specifier: 7.2.0 - version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) + version: 7.2.0(@swc/core@1.3.92)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 @@ -89,8 +89,8 @@ importers: specifier: 29.5.5 version: 29.5.5 '@workleap/eslint-plugin': - specifier: 2.1.1 - version: 2.1.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) + specifier: 3.0.0 + version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) '@workleap/tsup-configs': specifier: 3.0.1 version: 3.0.1(tsup@7.2.0)(typescript@5.2.2) @@ -99,10 +99,10 @@ importers: version: 3.0.2(typescript@5.2.2) jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.6.3)(ts-node@10.9.1) + version: 29.7.0(@types/node@20.8.4)(ts-node@10.9.1) tsup: specifier: 7.2.0 - version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) + version: 7.2.0(@swc/core@1.3.92)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 @@ -117,14 +117,14 @@ importers: specifier: 29.5.5 version: 29.5.5 '@types/react': - specifier: 18.2.22 - version: 18.2.22 + specifier: 18.2.28 + version: 18.2.28 '@types/react-dom': - specifier: 18.2.7 - version: 18.2.7 + specifier: 18.2.13 + version: 18.2.13 '@workleap/eslint-plugin': - specifier: 2.1.1 - version: 2.1.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) + specifier: 3.0.0 + version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) '@workleap/tsup-configs': specifier: 3.0.1 version: 3.0.1(tsup@7.2.0)(typescript@5.2.2) @@ -133,7 +133,7 @@ importers: version: 3.0.2(typescript@5.2.2) jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.6.3)(ts-node@10.9.1) + version: 29.7.0(@types/node@20.8.4)(ts-node@10.9.1) msw: specifier: 1.3.2 version: 1.3.2(typescript@5.2.2) @@ -145,7 +145,7 @@ importers: version: 18.2.0(react@18.2.0) tsup: specifier: 7.2.0 - version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) + version: 7.2.0(@swc/core@1.3.92)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 @@ -157,14 +157,14 @@ importers: version: link:../core devDependencies: '@swc/core': - specifier: 1.3.86 - version: 1.3.86(@swc/helpers@0.5.2) + specifier: 1.3.92 + version: 1.3.92(@swc/helpers@0.5.3) '@swc/helpers': - specifier: 0.5.2 - version: 0.5.2 + specifier: 0.5.3 + version: 0.5.3 '@swc/jest': specifier: 0.2.29 - version: 0.2.29(@swc/core@1.3.86) + version: 0.2.29(@swc/core@1.3.92) '@testing-library/react': specifier: 14.0.0 version: 14.0.0(react-dom@18.2.0)(react@18.2.0) @@ -172,20 +172,20 @@ importers: specifier: 29.5.5 version: 29.5.5 '@types/react': - specifier: 18.2.22 - version: 18.2.22 + specifier: 18.2.28 + version: 18.2.28 '@types/react-dom': - specifier: 18.2.7 - version: 18.2.7 + specifier: 18.2.13 + version: 18.2.13 '@types/react-test-renderer': - specifier: 18.0.2 - version: 18.0.2 + specifier: 18.0.3 + version: 18.0.3 '@workleap/eslint-plugin': - specifier: 2.1.1 - version: 2.1.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) + specifier: 3.0.0 + version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) '@workleap/swc-configs': specifier: 2.1.2 - version: 2.1.2(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@swc/jest@0.2.29)(browserslist@4.21.10) + version: 2.1.2(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@swc/jest@0.2.29)(browserslist@4.22.1) '@workleap/tsup-configs': specifier: 3.0.1 version: 3.0.1(tsup@7.2.0)(typescript@5.2.2) @@ -194,7 +194,7 @@ importers: version: 3.0.2(typescript@5.2.2) jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.6.3)(ts-node@10.9.1) + version: 29.7.0(@types/node@20.8.4)(ts-node@10.9.1) jest-environment-jsdom: specifier: 29.7.0 version: 29.7.0 @@ -215,7 +215,7 @@ importers: version: 29.1.1(@babel/core@7.23.0)(esbuild@0.18.20)(jest@29.7.0)(typescript@5.2.2) tsup: specifier: 7.2.0 - version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) + version: 7.2.0(@swc/core@1.3.92)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 @@ -233,32 +233,32 @@ importers: version: 5.5.3(webpack@5.88.2) devDependencies: '@swc/core': - specifier: 1.3.86 - version: 1.3.86(@swc/helpers@0.5.2) + specifier: 1.3.92 + version: 1.3.92(@swc/helpers@0.5.3) '@swc/helpers': - specifier: 0.5.2 - version: 0.5.2 + specifier: 0.5.3 + version: 0.5.3 '@swc/jest': specifier: 0.2.29 - version: 0.2.29(@swc/core@1.3.86) + version: 0.2.29(@swc/core@1.3.92) '@types/jest': specifier: 29.5.5 version: 29.5.5 '@types/node': - specifier: 20.6.3 - version: 20.6.3 + specifier: 20.8.4 + version: 20.8.4 '@types/react': - specifier: 18.2.22 - version: 18.2.22 + specifier: 18.2.28 + version: 18.2.28 '@types/react-dom': - specifier: 18.2.7 - version: 18.2.7 + specifier: 18.2.13 + version: 18.2.13 '@workleap/eslint-plugin': - specifier: 2.1.1 - version: 2.1.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) + specifier: 3.0.0 + version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) '@workleap/swc-configs': specifier: 2.1.2 - version: 2.1.2(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@swc/jest@0.2.29)(browserslist@4.21.10) + version: 2.1.2(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@swc/jest@0.2.29)(browserslist@4.22.1) '@workleap/tsup-configs': specifier: 3.0.1 version: 3.0.1(tsup@7.2.0)(typescript@5.2.2) @@ -266,11 +266,11 @@ importers: specifier: 3.0.2 version: 3.0.2(typescript@5.2.2) '@workleap/webpack-configs': - specifier: 1.0.8 - version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(browserslist@4.21.10)(esbuild@0.18.20)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack@5.88.2) + specifier: 1.1.0 + version: 1.1.0(@swc/core@1.3.92)(@swc/helpers@0.5.3)(browserslist@4.22.1)(esbuild@0.18.20)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack@5.88.2) jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.6.3)(ts-node@10.9.1) + version: 29.7.0(@types/node@20.8.4)(ts-node@10.9.1) react: specifier: 18.2.0 version: 18.2.0 @@ -282,13 +282,13 @@ importers: version: 29.1.1(@babel/core@7.23.0)(esbuild@0.18.20)(jest@29.7.0)(typescript@5.2.2) tsup: specifier: 7.2.0 - version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) + version: 7.2.0(@swc/core@1.3.92)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 webpack: specifier: 5.88.2 - version: 5.88.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4) + version: 5.88.2(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4) sample/another-remote-module: dependencies: @@ -298,18 +298,12 @@ importers: '@sample/shell': specifier: workspace:* version: link:../shell - '@squide/fakes': - specifier: workspace:* - version: link:../../packages/fakes '@squide/react-router': specifier: workspace:* version: link:../../packages/react-router '@squide/webpack-module-federation': specifier: workspace:* version: link:../../packages/webpack-module-federation - '@tanstack/react-query': - specifier: rc - version: 5.0.0-rc.7(react-dom@18.2.0)(react@18.2.0) react: specifier: 18.2.0 version: 18.2.0 @@ -320,45 +314,45 @@ importers: specifier: 6.16.0 version: 6.16.0(react-dom@18.2.0)(react@18.2.0) devDependencies: + '@squide/fakes': + specifier: workspace:* + version: link:../../packages/fakes '@swc/core': - specifier: 1.3.86 - version: 1.3.86(@swc/helpers@0.5.2) + specifier: 1.3.92 + version: 1.3.92(@swc/helpers@0.5.3) '@swc/helpers': - specifier: 0.5.2 - version: 0.5.2 - '@tanstack/react-query-devtools': - specifier: rc - version: 5.0.0-rc.7(@tanstack/react-query@5.0.0-rc.7)(react-dom@18.2.0)(react@18.2.0) + specifier: 0.5.3 + version: 0.5.3 '@types/react': - specifier: 18.2.22 - version: 18.2.22 + specifier: 18.2.28 + version: 18.2.28 '@types/react-dom': - specifier: 18.2.7 - version: 18.2.7 + specifier: 18.2.13 + version: 18.2.13 '@types/webpack': - specifier: 5.28.2 - version: 5.28.2(@swc/core@1.3.86)(webpack-cli@5.1.4) + specifier: 5.28.3 + version: 5.28.3(@swc/core@1.3.92)(webpack-cli@5.1.4) '@workleap/browserslist-config': specifier: 2.0.0 version: 2.0.0 '@workleap/eslint-plugin': - specifier: 2.1.1 - version: 2.1.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) + specifier: 3.0.0 + version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) '@workleap/swc-configs': specifier: 2.1.2 - version: 2.1.2(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@swc/jest@0.2.29)(browserslist@4.21.10) + version: 2.1.2(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@swc/jest@0.2.29)(browserslist@4.22.1) '@workleap/typescript-configs': specifier: 3.0.2 version: 3.0.2(typescript@5.2.2) '@workleap/webpack-configs': - specifier: 1.0.8 - version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) + specifier: 1.1.0 + version: 1.1.0(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@types/webpack@5.28.3)(browserslist@4.22.1)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) axios: specifier: 1.5.1 version: 1.5.1(debug@4.3.4) browserslist: - specifier: 4.21.10 - version: 4.21.10 + specifier: 4.22.1 + version: 4.22.1 cross-env: specifier: 7.0.3 version: 7.0.3 @@ -370,7 +364,7 @@ importers: version: 5.2.2 webpack: specifier: 5.88.2 - version: 5.88.2(@swc/core@1.3.86)(webpack-cli@5.1.4) + version: 5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4) webpack-cli: specifier: 5.1.4 version: 5.1.4(webpack-dev-server@4.15.1)(webpack@5.88.2) @@ -389,9 +383,6 @@ importers: '@sample/shell': specifier: workspace:* version: link:../shell - '@squide/fakes': - specifier: workspace:* - version: link:../../packages/fakes '@squide/msw': specifier: workspace:* version: link:../../packages/msw @@ -414,39 +405,42 @@ importers: specifier: 6.16.0 version: 6.16.0(react-dom@18.2.0)(react@18.2.0) devDependencies: + '@squide/fakes': + specifier: workspace:* + version: link:../../packages/fakes '@swc/core': - specifier: 1.3.86 - version: 1.3.86(@swc/helpers@0.5.2) + specifier: 1.3.92 + version: 1.3.92(@swc/helpers@0.5.3) '@swc/helpers': - specifier: 0.5.2 - version: 0.5.2 + specifier: 0.5.3 + version: 0.5.3 '@types/react': - specifier: 18.2.22 - version: 18.2.22 + specifier: 18.2.28 + version: 18.2.28 '@types/react-dom': - specifier: 18.2.7 - version: 18.2.7 + specifier: 18.2.13 + version: 18.2.13 '@types/webpack': - specifier: 5.28.2 - version: 5.28.2(@swc/core@1.3.86)(webpack-cli@5.1.4) + specifier: 5.28.3 + version: 5.28.3(@swc/core@1.3.92)(webpack-cli@5.1.4) '@workleap/browserslist-config': specifier: 2.0.0 version: 2.0.0 '@workleap/eslint-plugin': - specifier: 2.1.1 - version: 2.1.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) + specifier: 3.0.0 + version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) '@workleap/swc-configs': specifier: 2.1.2 - version: 2.1.2(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@swc/jest@0.2.29)(browserslist@4.21.10) + version: 2.1.2(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@swc/jest@0.2.29)(browserslist@4.22.1) '@workleap/typescript-configs': specifier: 3.0.2 version: 3.0.2(typescript@5.2.2) '@workleap/webpack-configs': - specifier: 1.0.8 - version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) + specifier: 1.1.0 + version: 1.1.0(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@types/webpack@5.28.3)(browserslist@4.22.1)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) browserslist: - specifier: 4.21.10 - version: 4.21.10 + specifier: 4.22.1 + version: 4.22.1 copyfiles: specifier: 2.4.1 version: 2.4.1 @@ -467,7 +461,7 @@ importers: version: 5.2.2 webpack: specifier: 5.88.2 - version: 5.88.2(@swc/core@1.3.86)(webpack-cli@5.1.4) + version: 5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4) webpack-cli: specifier: 5.1.4 version: 5.1.4(webpack-dev-server@4.15.1)(webpack@5.88.2) @@ -483,9 +477,6 @@ importers: '@sample/shell': specifier: workspace:* version: link:../shell - '@squide/fakes': - specifier: workspace:* - version: link:../../packages/fakes '@squide/react-router': specifier: workspace:* version: link:../../packages/react-router @@ -499,30 +490,33 @@ importers: specifier: 6.16.0 version: 6.16.0(react-dom@18.2.0)(react@18.2.0) devDependencies: + '@squide/fakes': + specifier: workspace:* + version: link:../../packages/fakes '@swc/core': - specifier: 1.3.86 - version: 1.3.86(@swc/helpers@0.5.2) + specifier: 1.3.92 + version: 1.3.92(@swc/helpers@0.5.3) '@swc/helpers': - specifier: 0.5.2 - version: 0.5.2 + specifier: 0.5.3 + version: 0.5.3 '@types/react': - specifier: 18.2.22 - version: 18.2.22 + specifier: 18.2.28 + version: 18.2.28 '@types/react-dom': - specifier: 18.2.7 - version: 18.2.7 + specifier: 18.2.13 + version: 18.2.13 '@types/webpack': - specifier: 5.28.2 - version: 5.28.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4) + specifier: 5.28.3 + version: 5.28.3(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4) '@workleap/browserslist-config': specifier: 2.0.0 version: 2.0.0 '@workleap/eslint-plugin': - specifier: 2.1.1 - version: 2.1.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) + specifier: 3.0.0 + version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) '@workleap/swc-configs': specifier: 2.1.2 - version: 2.1.2(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@swc/jest@0.2.29)(browserslist@4.21.10) + version: 2.1.2(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@swc/jest@0.2.29)(browserslist@4.22.1) '@workleap/tsup-configs': specifier: 3.0.1 version: 3.0.1(tsup@7.2.0)(typescript@5.2.2) @@ -530,14 +524,14 @@ importers: specifier: 3.0.2 version: 3.0.2(typescript@5.2.2) '@workleap/webpack-configs': - specifier: 1.0.8 - version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(esbuild@0.18.20)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) + specifier: 1.1.0 + version: 1.1.0(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@types/webpack@5.28.3)(browserslist@4.22.1)(esbuild@0.18.20)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) axios: specifier: 1.5.1 version: 1.5.1(debug@4.3.4) browserslist: - specifier: 4.21.10 - version: 4.21.10 + specifier: 4.22.1 + version: 4.22.1 cross-env: specifier: 7.0.3 version: 7.0.3 @@ -546,13 +540,13 @@ importers: version: 3.0.1 tsup: specifier: 7.2.0 - version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) + version: 7.2.0(@swc/core@1.3.92)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 webpack: specifier: 5.88.2 - version: 5.88.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4) + version: 5.88.2(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4) webpack-cli: specifier: 5.1.4 version: 5.1.4(webpack-dev-server@4.15.1)(webpack@5.88.2) @@ -568,9 +562,6 @@ importers: '@sample/shell': specifier: workspace:* version: link:../shell - '@squide/fakes': - specifier: workspace:* - version: link:../../packages/fakes '@squide/msw': specifier: workspace:* version: link:../../packages/msw @@ -593,45 +584,48 @@ importers: specifier: 6.16.0 version: 6.16.0(react-dom@18.2.0)(react@18.2.0) devDependencies: + '@squide/fakes': + specifier: workspace:* + version: link:../../packages/fakes '@swc/core': - specifier: 1.3.86 - version: 1.3.86(@swc/helpers@0.5.2) + specifier: 1.3.92 + version: 1.3.92(@swc/helpers@0.5.3) '@swc/helpers': - specifier: 0.5.2 - version: 0.5.2 + specifier: 0.5.3 + version: 0.5.3 '@tanstack/react-query-devtools': specifier: rc - version: 5.0.0-rc.7(@tanstack/react-query@5.0.0-rc.7)(react-dom@18.2.0)(react@18.2.0) + version: 5.0.0-rc.10(@tanstack/react-query@5.0.0-rc.7)(react-dom@18.2.0)(react@18.2.0) '@types/react': - specifier: 18.2.22 - version: 18.2.22 + specifier: 18.2.28 + version: 18.2.28 '@types/react-dom': - specifier: 18.2.7 - version: 18.2.7 + specifier: 18.2.13 + version: 18.2.13 '@types/webpack': - specifier: 5.28.2 - version: 5.28.2(@swc/core@1.3.86)(webpack-cli@5.1.4) + specifier: 5.28.3 + version: 5.28.3(@swc/core@1.3.92)(webpack-cli@5.1.4) '@workleap/browserslist-config': specifier: 2.0.0 version: 2.0.0 '@workleap/eslint-plugin': - specifier: 2.1.1 - version: 2.1.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) + specifier: 3.0.0 + version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) '@workleap/swc-configs': specifier: 2.1.2 - version: 2.1.2(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@swc/jest@0.2.29)(browserslist@4.21.10) + version: 2.1.2(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@swc/jest@0.2.29)(browserslist@4.22.1) '@workleap/typescript-configs': specifier: 3.0.2 version: 3.0.2(typescript@5.2.2) '@workleap/webpack-configs': - specifier: 1.0.8 - version: 1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) + specifier: 1.1.0 + version: 1.1.0(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@types/webpack@5.28.3)(browserslist@4.22.1)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) axios: specifier: 1.5.1 version: 1.5.1(debug@4.3.4) browserslist: - specifier: 4.21.10 - version: 4.21.10 + specifier: 4.22.1 + version: 4.22.1 cross-env: specifier: 7.0.3 version: 7.0.3 @@ -649,7 +643,7 @@ importers: version: 5.2.2 webpack: specifier: 5.88.2 - version: 5.88.2(@swc/core@1.3.86)(webpack-cli@5.1.4) + version: 5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4) webpack-cli: specifier: 5.1.4 version: 5.1.4(webpack-dev-server@4.15.1)(webpack@5.88.2) @@ -666,14 +660,14 @@ importers: specifier: workspace:* version: link:../../packages/react-router '@types/react': - specifier: 18.2.22 - version: 18.2.22 + specifier: 18.2.28 + version: 18.2.28 '@types/react-dom': - specifier: 18.2.7 - version: 18.2.7 + specifier: 18.2.13 + version: 18.2.13 '@workleap/eslint-plugin': - specifier: 2.1.1 - version: 2.1.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) + specifier: 3.0.0 + version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) '@workleap/tsup-configs': specifier: 3.0.1 version: 3.0.1(tsup@7.2.0)(typescript@5.2.2) @@ -694,7 +688,7 @@ importers: version: 6.16.0(react-dom@18.2.0)(react@18.2.0) tsup: specifier: 7.2.0 - version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) + version: 7.2.0(@swc/core@1.3.92)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 @@ -707,6 +701,9 @@ importers: '@sample/shared': specifier: workspace:* version: link:../shared + '@squide/fakes': + specifier: workspace:* + version: link:../../packages/fakes '@squide/msw': specifier: workspace:* version: link:../../packages/msw @@ -717,14 +714,14 @@ importers: specifier: workspace:* version: link:../../packages/webpack-module-federation '@types/react': - specifier: 18.2.22 - version: 18.2.22 + specifier: 18.2.28 + version: 18.2.28 '@types/react-dom': - specifier: 18.2.7 - version: 18.2.7 + specifier: 18.2.13 + version: 18.2.13 '@workleap/eslint-plugin': - specifier: 2.1.1 - version: 2.1.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) + specifier: 3.0.0 + version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) '@workleap/tsup-configs': specifier: 3.0.1 version: 3.0.1(tsup@7.2.0)(typescript@5.2.2) @@ -751,7 +748,7 @@ importers: version: 6.16.0(react-dom@18.2.0)(react@18.2.0) tsup: specifier: 7.2.0 - version: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) + version: 7.2.0(@swc/core@1.3.92)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 @@ -779,8 +776,8 @@ packages: chalk: 2.4.2 dev: true - /@babel/compat-data@7.22.20: - resolution: {integrity: sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==} + /@babel/compat-data@7.23.2: + resolution: {integrity: sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==} engines: {node: '>=6.9.0'} dev: true @@ -793,10 +790,10 @@ packages: '@babel/generator': 7.23.0 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) - '@babel/helpers': 7.23.1 + '@babel/helpers': 7.23.2 '@babel/parser': 7.23.0 '@babel/template': 7.22.15 - '@babel/traverse': 7.23.0 + '@babel/traverse': 7.23.2 '@babel/types': 7.23.0 convert-source-map: 2.0.0 debug: 4.3.4(supports-color@9.4.0) @@ -835,9 +832,9 @@ packages: resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/compat-data': 7.22.20 + '@babel/compat-data': 7.23.2 '@babel/helper-validator-option': 7.22.15 - browserslist: 4.21.10 + browserslist: 4.22.1 lru-cache: 5.1.1 semver: 6.3.1 dev: true @@ -872,8 +869,8 @@ packages: semver: 6.3.1 dev: true - /@babel/helper-define-polyfill-provider@0.4.2(@babel/core@7.23.0): - resolution: {integrity: sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==} + /@babel/helper-define-polyfill-provider@0.4.3(@babel/core@7.23.0): + resolution: {integrity: sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: @@ -882,7 +879,7 @@ packages: '@babel/helper-plugin-utils': 7.22.5 debug: 4.3.4(supports-color@9.4.0) lodash.debounce: 4.0.8 - resolve: 1.22.6 + resolve: 1.22.8 transitivePeerDependencies: - supports-color dev: true @@ -1016,12 +1013,12 @@ packages: '@babel/types': 7.23.0 dev: true - /@babel/helpers@7.23.1: - resolution: {integrity: sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==} + /@babel/helpers@7.23.2: + resolution: {integrity: sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==} engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.22.15 - '@babel/traverse': 7.23.0 + '@babel/traverse': 7.23.2 '@babel/types': 7.23.0 transitivePeerDependencies: - supports-color @@ -1283,8 +1280,8 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-async-generator-functions@7.22.15(@babel/core@7.23.0): - resolution: {integrity: sha512-jBm1Es25Y+tVoTi5rfd5t1KLmL8ogLKpXszboWOTTtGFGz2RKnQe2yn7HbZ+kb/B8N0FVSGQo874NSlOU1T4+w==} + /@babel/plugin-transform-async-generator-functions@7.23.2(@babel/core@7.23.0): + resolution: {integrity: sha512-BBYVGxbDVHfoeXbOwcagAkOQAm9NxoTdMGfTqghu1GrvadSaw6iW3Je6IcL5PNOw8VwjxqBECXy50/iCQSY/lQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1604,7 +1601,7 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.22.20 + '@babel/compat-data': 7.23.2 '@babel/core': 7.23.0 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 @@ -1873,13 +1870,13 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/preset-env@7.22.20(@babel/core@7.23.0): - resolution: {integrity: sha512-11MY04gGC4kSzlPHRfvVkNAZhUxOvm7DCJ37hPDnUENwe06npjIRAfInEMTGSb4LZK5ZgDFkv5hw0lGebHeTyg==} + /@babel/preset-env@7.23.2(@babel/core@7.23.0): + resolution: {integrity: sha512-BW3gsuDD+rvHL2VO2SjAUNTBe5YrjsTiDyqamPDWY723na3/yPQ65X5oQkFVJZ0o50/2d+svm1rkPoJeR1KxVQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.22.20 + '@babel/compat-data': 7.23.2 '@babel/core': 7.23.0 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 @@ -1906,7 +1903,7 @@ packages: '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.0) '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.23.0) '@babel/plugin-transform-arrow-functions': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-async-generator-functions': 7.22.15(@babel/core@7.23.0) + '@babel/plugin-transform-async-generator-functions': 7.23.2(@babel/core@7.23.0) '@babel/plugin-transform-async-to-generator': 7.22.5(@babel/core@7.23.0) '@babel/plugin-transform-block-scoped-functions': 7.22.5(@babel/core@7.23.0) '@babel/plugin-transform-block-scoping': 7.23.0(@babel/core@7.23.0) @@ -1955,9 +1952,9 @@ packages: '@babel/plugin-transform-unicode-sets-regex': 7.22.5(@babel/core@7.23.0) '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.23.0) '@babel/types': 7.23.0 - babel-plugin-polyfill-corejs2: 0.4.5(@babel/core@7.23.0) - babel-plugin-polyfill-corejs3: 0.8.4(@babel/core@7.23.0) - babel-plugin-polyfill-regenerator: 0.5.2(@babel/core@7.23.0) + babel-plugin-polyfill-corejs2: 0.4.6(@babel/core@7.23.0) + babel-plugin-polyfill-corejs3: 0.8.5(@babel/core@7.23.0) + babel-plugin-polyfill-regenerator: 0.5.3(@babel/core@7.23.0) core-js-compat: 3.33.0 semver: 6.3.1 transitivePeerDependencies: @@ -1990,8 +1987,8 @@ packages: '@babel/plugin-transform-react-pure-annotations': 7.22.5(@babel/core@7.23.0) dev: true - /@babel/preset-typescript@7.23.0(@babel/core@7.23.0): - resolution: {integrity: sha512-6P6VVa/NM/VlAYj5s2Aq/gdVg8FSENCg3wlZ6Qau9AcPaoF5LbN1nyGlR9DTRIw9PpxI94e+ReydsJHcjwAweg==} + /@babel/preset-typescript@7.23.2(@babel/core@7.23.0): + resolution: {integrity: sha512-u4UJc1XsS1GhIGteM8rnGiIvf9rJpiVgMEeCnwlLA7WJPC+jcXWJAGxYmeqs5hOZD8BbAfnV5ezBOxQbb4OUxA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -2008,8 +2005,8 @@ packages: resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==} dev: true - /@babel/runtime@7.23.1: - resolution: {integrity: sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==} + /@babel/runtime@7.23.2: + resolution: {integrity: sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==} engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.0 @@ -2024,8 +2021,8 @@ packages: '@babel/types': 7.23.0 dev: true - /@babel/traverse@7.23.0: - resolution: {integrity: sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==} + /@babel/traverse@7.23.2: + resolution: {integrity: sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==} engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.22.13 @@ -2042,6 +2039,15 @@ packages: - supports-color dev: true + /@babel/types@7.22.19: + resolution: {integrity: sha512-P7LAw/LbojPzkgp5oznjE6tQEIWbp4PkkfrZDINTro9zgBRtI324/EYsiSI7lhPbpIQ+DCeR2NNmMWANGGfZsg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: true + /@babel/types@7.23.0: resolution: {integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==} engines: {node: '>=6.9.0'} @@ -2100,7 +2106,7 @@ packages: /@changesets/apply-release-plan@6.1.4: resolution: {integrity: sha512-FMpKF1fRlJyCZVYHr3CbinpZZ+6MwvOtWUuO8uo+svcATEoc1zRDcj23pAurJ2TZ/uVz1wFHH6K3NlACy0PLew==} dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.23.2 '@changesets/config': 2.3.1 '@changesets/get-version-range-type': 0.3.2 '@changesets/git': 2.0.0 @@ -2118,7 +2124,7 @@ packages: /@changesets/assemble-release-plan@5.2.4: resolution: {integrity: sha512-xJkWX+1/CUaOUWTguXEbCDTyWJFECEhmdtbkjhn5GVBGxdP/JwaHBIU9sW3FR6gD07UwZ7ovpiPclQZs+j+mvg==} dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.23.2 '@changesets/errors': 0.1.4 '@changesets/get-dependents-graph': 1.3.6 '@changesets/types': 5.2.1 @@ -2146,7 +2152,7 @@ packages: resolution: {integrity: sha512-dnWrJTmRR8bCHikJHl9b9HW3gXACCehz4OasrXpMp7sx97ECuBGGNjJhjPhdZNCvMy9mn4BWdplI323IbqsRig==} hasBin: true dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.23.2 '@changesets/apply-release-plan': 6.1.4 '@changesets/assemble-release-plan': 5.2.4 '@changesets/changelog-git': 0.1.14 @@ -2161,7 +2167,7 @@ packages: '@changesets/types': 5.2.1 '@changesets/write': 0.2.3 '@manypkg/get-packages': 1.1.3 - '@types/is-ci': 3.0.1 + '@types/is-ci': 3.0.2 '@types/semver': 7.5.3 ansi-colors: 4.1.3 chalk: 2.4.2 @@ -2221,7 +2227,7 @@ packages: /@changesets/get-release-plan@3.0.17: resolution: {integrity: sha512-6IwKTubNEgoOZwDontYc2x2cWXfr6IKxP3IhKeK+WjyD6y3M4Gl/jdQvBw+m/5zWILSOCAaGLu2ZF6Q+WiPniw==} dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.23.2 '@changesets/assemble-release-plan': 5.2.4 '@changesets/config': 2.3.1 '@changesets/pre': 1.0.14 @@ -2237,7 +2243,7 @@ packages: /@changesets/git@2.0.0: resolution: {integrity: sha512-enUVEWbiqUTxqSnmesyJGWfzd51PY4H7mH9yUw0hPVpZBJ6tQZFMU3F3mT/t9OJ/GjyiM4770i+sehAn6ymx6A==} dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.23.2 '@changesets/errors': 0.1.4 '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 @@ -2262,7 +2268,7 @@ packages: /@changesets/pre@1.0.14: resolution: {integrity: sha512-dTsHmxQWEQekHYHbg+M1mDVYFvegDh9j/kySNuDKdylwfMEevTeDouR7IfHNyVodxZXu17sXoJuf2D0vi55FHQ==} dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.23.2 '@changesets/errors': 0.1.4 '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 @@ -2272,7 +2278,7 @@ packages: /@changesets/read@0.5.9: resolution: {integrity: sha512-T8BJ6JS6j1gfO1HFq50kU3qawYxa4NTbI/ASNVVCBTsKquy2HYwM9r7ZnzkiMe8IEObAJtUVGSrePCOxAK2haQ==} dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.23.2 '@changesets/git': 2.0.0 '@changesets/logger': 0.0.5 '@changesets/parse': 0.3.16 @@ -2293,7 +2299,7 @@ packages: /@changesets/write@0.2.3: resolution: {integrity: sha512-Dbamr7AIMvslKnNYsLFafaVORx4H0pvCA2MHqgtNCySMe1blImEyAEOzDmcgKAkgz4+uwoLz7demIrX+JBr/Xw==} dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.23.2 '@changesets/types': 5.2.1 fs-extra: 7.0.1 human-id: 1.0.2 @@ -2354,6 +2360,15 @@ packages: dev: true optional: true + /@esbuild/android-arm64@0.19.4: + resolution: {integrity: sha512-mRsi2vJsk4Bx/AFsNBqOH2fqedxn5L/moT58xgg51DjX1la64Z3Npicut2VbhvDFO26qjWtPMsVxCd80YTFVeg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm@0.18.20: resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} engines: {node: '>=12'} @@ -2371,6 +2386,15 @@ packages: dev: true optional: true + /@esbuild/android-arm@0.19.4: + resolution: {integrity: sha512-uBIbiYMeSsy2U0XQoOGVVcpIktjLMEKa7ryz2RLr7L/vTnANNEsPVAh4xOv7ondGz6ac1zVb0F8Jx20rQikffQ==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-x64@0.18.20: resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} engines: {node: '>=12'} @@ -2388,6 +2412,15 @@ packages: dev: true optional: true + /@esbuild/android-x64@0.19.4: + resolution: {integrity: sha512-4iPufZ1TMOD3oBlGFqHXBpa3KFT46aLl6Vy7gwed0ZSYgHaZ/mihbYb4t7Z9etjkC9Al3ZYIoOaHrU60gcMy7g==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-arm64@0.18.20: resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} engines: {node: '>=12'} @@ -2405,6 +2438,15 @@ packages: dev: true optional: true + /@esbuild/darwin-arm64@0.19.4: + resolution: {integrity: sha512-Lviw8EzxsVQKpbS+rSt6/6zjn9ashUZ7Tbuvc2YENgRl0yZTktGlachZ9KMJUsVjZEGFVu336kl5lBgDN6PmpA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-x64@0.18.20: resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} engines: {node: '>=12'} @@ -2422,6 +2464,15 @@ packages: dev: true optional: true + /@esbuild/darwin-x64@0.19.4: + resolution: {integrity: sha512-YHbSFlLgDwglFn0lAO3Zsdrife9jcQXQhgRp77YiTDja23FrC2uwnhXMNkAucthsf+Psr7sTwYEryxz6FPAVqw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-arm64@0.18.20: resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} engines: {node: '>=12'} @@ -2439,6 +2490,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-arm64@0.19.4: + resolution: {integrity: sha512-vz59ijyrTG22Hshaj620e5yhs2dU1WJy723ofc+KUgxVCM6zxQESmWdMuVmUzxtGqtj5heHyB44PjV/HKsEmuQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-x64@0.18.20: resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} engines: {node: '>=12'} @@ -2456,6 +2516,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-x64@0.19.4: + resolution: {integrity: sha512-3sRbQ6W5kAiVQRBWREGJNd1YE7OgzS0AmOGjDmX/qZZecq8NFlQsQH0IfXjjmD0XtUYqr64e0EKNFjMUlPL3Cw==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm64@0.18.20: resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} engines: {node: '>=12'} @@ -2473,6 +2542,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm64@0.19.4: + resolution: {integrity: sha512-ZWmWORaPbsPwmyu7eIEATFlaqm0QGt+joRE9sKcnVUG3oBbr/KYdNE2TnkzdQwX6EDRdg/x8Q4EZQTXoClUqqA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm@0.18.20: resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} engines: {node: '>=12'} @@ -2490,6 +2568,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm@0.19.4: + resolution: {integrity: sha512-z/4ArqOo9EImzTi4b6Vq+pthLnepFzJ92BnofU1jgNlcVb+UqynVFdoXMCFreTK7FdhqAzH0vmdwW5373Hm9pg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ia32@0.18.20: resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} engines: {node: '>=12'} @@ -2507,6 +2594,15 @@ packages: dev: true optional: true + /@esbuild/linux-ia32@0.19.4: + resolution: {integrity: sha512-EGc4vYM7i1GRUIMqRZNCTzJh25MHePYsnQfKDexD8uPTCm9mK56NIL04LUfX2aaJ+C9vyEp2fJ7jbqFEYgO9lQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-loong64@0.18.20: resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} engines: {node: '>=12'} @@ -2524,6 +2620,15 @@ packages: dev: true optional: true + /@esbuild/linux-loong64@0.19.4: + resolution: {integrity: sha512-WVhIKO26kmm8lPmNrUikxSpXcgd6HDog0cx12BUfA2PkmURHSgx9G6vA19lrlQOMw+UjMZ+l3PpbtzffCxFDRg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-mips64el@0.18.20: resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} engines: {node: '>=12'} @@ -2541,6 +2646,15 @@ packages: dev: true optional: true + /@esbuild/linux-mips64el@0.19.4: + resolution: {integrity: sha512-keYY+Hlj5w86hNp5JJPuZNbvW4jql7c1eXdBUHIJGTeN/+0QFutU3GrS+c27L+NTmzi73yhtojHk+lr2+502Mw==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ppc64@0.18.20: resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} engines: {node: '>=12'} @@ -2558,6 +2672,15 @@ packages: dev: true optional: true + /@esbuild/linux-ppc64@0.19.4: + resolution: {integrity: sha512-tQ92n0WMXyEsCH4m32S21fND8VxNiVazUbU4IUGVXQpWiaAxOBvtOtbEt3cXIV3GEBydYsY8pyeRMJx9kn3rvw==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-riscv64@0.18.20: resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} engines: {node: '>=12'} @@ -2575,6 +2698,15 @@ packages: dev: true optional: true + /@esbuild/linux-riscv64@0.19.4: + resolution: {integrity: sha512-tRRBey6fG9tqGH6V75xH3lFPpj9E8BH+N+zjSUCnFOX93kEzqS0WdyJHkta/mmJHn7MBaa++9P4ARiU4ykjhig==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-s390x@0.18.20: resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} engines: {node: '>=12'} @@ -2592,6 +2724,15 @@ packages: dev: true optional: true + /@esbuild/linux-s390x@0.19.4: + resolution: {integrity: sha512-152aLpQqKZYhThiJ+uAM4PcuLCAOxDsCekIbnGzPKVBRUDlgaaAfaUl5NYkB1hgY6WN4sPkejxKlANgVcGl9Qg==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-x64@0.18.20: resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} engines: {node: '>=12'} @@ -2609,6 +2750,15 @@ packages: dev: true optional: true + /@esbuild/linux-x64@0.19.4: + resolution: {integrity: sha512-Mi4aNA3rz1BNFtB7aGadMD0MavmzuuXNTaYL6/uiYIs08U7YMPETpgNn5oue3ICr+inKwItOwSsJDYkrE9ekVg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/netbsd-x64@0.18.20: resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} engines: {node: '>=12'} @@ -2626,6 +2776,15 @@ packages: dev: true optional: true + /@esbuild/netbsd-x64@0.19.4: + resolution: {integrity: sha512-9+Wxx1i5N/CYo505CTT7T+ix4lVzEdz0uCoYGxM5JDVlP2YdDC1Bdz+Khv6IbqmisT0Si928eAxbmGkcbiuM/A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/openbsd-x64@0.18.20: resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} engines: {node: '>=12'} @@ -2643,6 +2802,15 @@ packages: dev: true optional: true + /@esbuild/openbsd-x64@0.19.4: + resolution: {integrity: sha512-MFsHleM5/rWRW9EivFssop+OulYVUoVcqkyOkjiynKBCGBj9Lihl7kh9IzrreDyXa4sNkquei5/DTP4uCk25xw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/sunos-x64@0.18.20: resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} engines: {node: '>=12'} @@ -2660,6 +2828,15 @@ packages: dev: true optional: true + /@esbuild/sunos-x64@0.19.4: + resolution: {integrity: sha512-6Xq8SpK46yLvrGxjp6HftkDwPP49puU4OF0hEL4dTxqCbfx09LyrbUj/D7tmIRMj5D5FCUPksBbxyQhp8tmHzw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-arm64@0.18.20: resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} engines: {node: '>=12'} @@ -2677,6 +2854,15 @@ packages: dev: true optional: true + /@esbuild/win32-arm64@0.19.4: + resolution: {integrity: sha512-PkIl7Jq4mP6ke7QKwyg4fD4Xvn8PXisagV/+HntWoDEdmerB2LTukRZg728Yd1Fj+LuEX75t/hKXE2Ppk8Hh1w==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-ia32@0.18.20: resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} engines: {node: '>=12'} @@ -2694,6 +2880,15 @@ packages: dev: true optional: true + /@esbuild/win32-ia32@0.19.4: + resolution: {integrity: sha512-ga676Hnvw7/ycdKB53qPusvsKdwrWzEyJ+AtItHGoARszIqvjffTwaaW3b2L6l90i7MO9i+dlAW415INuRhSGg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-x64@0.18.20: resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} engines: {node: '>=12'} @@ -2711,13 +2906,22 @@ packages: dev: true optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.49.0): + /@esbuild/win32-x64@0.19.4: + resolution: {integrity: sha512-HP0GDNla1T3ZL8Ko/SHAS2GgtjOg+VmWnnYLhuTksr++EnduYB0f3Y2LzHsUwb2iQ13JGoY6G3R8h6Du/WG6uA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.51.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.49.0 + eslint: 8.51.0 eslint-visitor-keys: 3.4.3 dev: true @@ -2743,8 +2947,8 @@ packages: - supports-color dev: true - /@eslint/js@8.49.0: - resolution: {integrity: sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==} + /@eslint/js@8.51.0: + resolution: {integrity: sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true @@ -2802,7 +3006,7 @@ packages: engines: {node: ^8.13.0 || >=10.10.0} dependencies: '@grpc/proto-loader': 0.7.10 - '@types/node': 20.6.3 + '@types/node': 20.8.4 dev: true /@grpc/proto-loader@0.7.10: @@ -2899,7 +3103,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.6.3 + '@types/node': 20.8.4 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -2920,14 +3124,14 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.6.3 + '@types/node': 20.8.4 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.6.3)(ts-node@10.9.1) + jest-config: 29.7.0(@types/node@20.8.4)(ts-node@10.9.1) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -2962,7 +3166,7 @@ packages: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.6.3 + '@types/node': 20.8.4 jest-mock: 29.7.0 dev: true @@ -2989,7 +3193,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.6.3 + '@types/node': 20.8.4 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -3022,7 +3226,7 @@ packages: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.19 - '@types/node': 20.6.3 + '@types/node': 20.8.4 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -3109,7 +3313,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.2 - '@types/node': 20.6.3 + '@types/node': 20.8.4 '@types/yargs': 16.0.6 chalk: 4.1.2 dev: true @@ -3121,7 +3325,7 @@ packages: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.2 - '@types/node': 20.6.3 + '@types/node': 20.8.4 '@types/yargs': 17.0.28 chalk: 4.1.2 dev: true @@ -3175,7 +3379,7 @@ packages: /@manypkg/find-root@1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.23.2 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 @@ -3184,7 +3388,7 @@ packages: /@manypkg/get-packages@1.1.3: resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.23.2 '@changesets/types': 4.1.0 '@manypkg/find-root': 1.1.0 fs-extra: 8.1.0 @@ -3238,8 +3442,8 @@ packages: resolution: {integrity: sha512-4wMPu9iN3/HL97QblBsBay3E1etIciR84izI3U+4iALY+JHCrI+a2jO0qbAZ/nxKoegypYEaiiqWXylm+/zfrw==} dev: true - /@netlify/build-info@7.8.0: - resolution: {integrity: sha512-4UnAaQUXoxMfs6KAMKPx8Pa5WwdGDcgIliyQpKhc4Xq3A2dgH+Uam9kS4aq5+4ZeHS9/eT1BKLKZPUOOF5ZQZg==} + /@netlify/build-info@7.10.1: + resolution: {integrity: sha512-bBEDehGo47ERzAHvOb0AZ/P5zNeDOzqpQFEGqc2djoLjBOcgDkRhyMxy7VZMBiaJ//ujxyTVIBXxrqVKWCpmrQ==} engines: {node: ^14.16.0 || >=16.0.0} hasBin: true dependencies: @@ -3254,8 +3458,8 @@ packages: yargs: 17.7.2 dev: true - /@netlify/build@29.21.1(@types/node@20.6.3)(debug@4.3.4): - resolution: {integrity: sha512-9twzYq+X8nz16sttkjCB8F8nZIMVspa40P2u3p1h0SWwxk2zq4g3cd8QlZOaO1sfcKQDzzpQkvu+jQ06j2AMfw==} + /@netlify/build@29.22.2(@types/node@20.8.4)(debug@4.3.4): + resolution: {integrity: sha512-cSz8X6UhNJdWY79No6Jxm0XBLtRE2lhoPc1OrXLS6afMspB3V9TsFNWRWSWvCL2Ppzkbu82rTdVA9YzVmkMiDw==} engines: {node: ^14.16.0 || >=16.0.0} hasBin: true dependencies: @@ -3263,13 +3467,13 @@ packages: '@honeycombio/opentelemetry-node': 0.5.0(debug@4.3.4)(supports-color@9.4.0) '@netlify/cache-utils': 5.1.5 '@netlify/config': 20.9.0 - '@netlify/edge-bundler': 8.20.0 + '@netlify/edge-bundler': 9.2.0 '@netlify/framework-info': 9.8.10 - '@netlify/functions-utils': 5.2.31(supports-color@9.4.0) + '@netlify/functions-utils': 5.2.33(supports-color@9.4.0) '@netlify/git-utils': 5.1.1 '@netlify/plugins-list': 6.71.0 '@netlify/run-utils': 5.1.1 - '@netlify/zip-it-and-ship-it': 9.18.1(supports-color@9.4.0) + '@netlify/zip-it-and-ship-it': 9.23.0(supports-color@9.4.0) '@opentelemetry/api': 1.6.0 '@sindresorhus/slugify': 2.2.1 ansi-escapes: 6.2.0 @@ -3302,7 +3506,7 @@ packages: ps-list: 8.1.1 read-pkg-up: 9.1.0 readdirp: 3.6.0 - resolve: 2.0.0-next.4 + resolve: 2.0.0-next.5 rfdc: 1.3.0 safe-json-stringify: 1.2.0 semver: 7.5.4 @@ -3310,7 +3514,7 @@ packages: strip-ansi: 7.1.0 supports-color: 9.4.0 terminal-link: 3.0.0 - ts-node: 10.9.1(@types/node@20.6.3)(typescript@5.2.2) + ts-node: 10.9.1(@types/node@20.8.4)(typescript@5.2.2) typescript: 5.2.2 uuid: 9.0.0 yargs: 17.7.2 @@ -3368,8 +3572,8 @@ packages: yargs: 17.7.2 dev: true - /@netlify/edge-bundler@8.19.1: - resolution: {integrity: sha512-Erj0+dfRFMQjV3R+FX9NtCV35t6qU91rWKtsuawLzS6tAHLvrR8sOmyFxt5Neg4VPjAstYg1ik16lDak2LhQww==} + /@netlify/edge-bundler@9.1.0: + resolution: {integrity: sha512-st05JP+QkNbsvlKelM2KL0nceGD3so86fA9oml47AmemSVvqYlGY0iHRdLGgufqpHfrA4QbQY22IGOP9tZxrXg==} engines: {node: ^14.16.0 || >=16.0.0} dependencies: '@import-maps/resolve': 1.0.1 @@ -3378,6 +3582,7 @@ packages: better-ajv-errors: 1.2.0(ajv@8.12.0) common-path-prefix: 3.0.0 env-paths: 3.0.0 + esbuild: 0.19.2 execa: 6.1.0 find-up: 6.3.0 get-port: 6.1.2 @@ -3395,8 +3600,8 @@ packages: uuid: 9.0.0 dev: true - /@netlify/edge-bundler@8.20.0: - resolution: {integrity: sha512-eIDXLqAzz2XpGzPUKe6DKAjldFFTlyaZCQ6v8zrBJ60jKQde5/2tWM2yfHVW9seTehP/0ssLYZW2xmrIM+WqWQ==} + /@netlify/edge-bundler@9.2.0: + resolution: {integrity: sha512-31YyaCOEvbhOwaTF34l4gAOjyD5eDAqIvpe8/VojhxBdDCrFKAVaQ6tiXaZOCGVwN3bzl49ai5Y/ekrDfIXKtw==} engines: {node: ^14.16.0 || >=16.0.0} dependencies: '@import-maps/resolve': 1.0.1 @@ -3405,7 +3610,7 @@ packages: better-ajv-errors: 1.2.0(ajv@8.12.0) common-path-prefix: 3.0.0 env-paths: 3.0.0 - esbuild: 0.19.2 + esbuild: 0.19.4 execa: 6.1.0 find-up: 6.3.0 get-port: 6.1.2 @@ -3439,11 +3644,11 @@ packages: semver: 7.5.4 dev: true - /@netlify/functions-utils@5.2.31(supports-color@9.4.0): - resolution: {integrity: sha512-EPYizFLstBKw7+p7KNEJu2AcnM5ofHki1CZ5rEkvILuwvIyYnViwQGHWihmbJI8COlHdXj9F9cxUXn0vkSEKOw==} + /@netlify/functions-utils@5.2.33(supports-color@9.4.0): + resolution: {integrity: sha512-YQBgglpH8ilYw0fS7HlWb5NdlzpEHgT/+/1zOqPlvZoKu/6kKE4iPvuQpXtVciXJMTW095pIbC/I4CZHmIv0PQ==} engines: {node: ^14.16.0 || >=16.0.0} dependencies: - '@netlify/zip-it-and-ship-it': 9.23.0(supports-color@9.4.0) + '@netlify/zip-it-and-ship-it': 9.24.2(supports-color@9.4.0) cpy: 9.0.1 path-exists: 5.0.0 transitivePeerDependencies: @@ -3608,30 +3813,30 @@ packages: execa: 6.1.0 dev: true - /@netlify/serverless-functions-api@1.7.3: - resolution: {integrity: sha512-n6/7cJlSWvvbBlUOEAbkGyEld80S6KbG/ldQI9OhLfe1lTatgKmrTNIgqVNpaWpUdTgP2OHWFjmFBzkxxBWs5w==} + /@netlify/serverless-functions-api@1.8.0: + resolution: {integrity: sha512-+dsowkoEA+LF4wS9kKafToHNSace7MxD2q3pgBik3N8UjAXBZo7J9t/E7rpkcm5w2ZXklvrDM815bOrzfDE5Jg==} engines: {node: ^14.18.0 || >=16.0.0} dependencies: '@netlify/node-cookies': 0.1.0 urlpattern-polyfill: 8.0.2 dev: true - /@netlify/serverless-functions-api@1.8.0: - resolution: {integrity: sha512-+dsowkoEA+LF4wS9kKafToHNSace7MxD2q3pgBik3N8UjAXBZo7J9t/E7rpkcm5w2ZXklvrDM815bOrzfDE5Jg==} + /@netlify/serverless-functions-api@1.9.0: + resolution: {integrity: sha512-Jq4uk1Mwa5vyxImupJYXPP+I5yYcp3PtguvXtJRutKdm9DPALXfZVtCQzBWMNdZiqVWCM3La9hvaBsPjSMfeug==} engines: {node: ^14.18.0 || >=16.0.0} dependencies: '@netlify/node-cookies': 0.1.0 urlpattern-polyfill: 8.0.2 dev: true - /@netlify/zip-it-and-ship-it@9.18.1(supports-color@9.4.0): - resolution: {integrity: sha512-XOIqPHUSe5Tstzf4QUrEaCm9dH0QGpm8M3efKcRQr8IXKM10zNH7SZ43ovyv3GxULpvbndLUtGi4bdARaMxAxw==} + /@netlify/zip-it-and-ship-it@9.23.0(supports-color@9.4.0): + resolution: {integrity: sha512-4Khr9U3VKCEpK13zykbP8g2EgK58c7okRAyC0C+7lU5oopwp5K4lkKvGmZCaNyls3hhjXaX/wLv6K+t1NOIGSw==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true dependencies: '@babel/parser': 7.23.0 '@netlify/binary-info': 1.0.0 - '@netlify/serverless-functions-api': 1.7.3 + '@netlify/serverless-functions-api': 1.8.0 '@vercel/nft': 0.23.1(supports-color@9.4.0) archiver: 6.0.1 common-path-prefix: 3.0.0 @@ -3641,7 +3846,6 @@ packages: execa: 6.1.0 filter-obj: 5.1.0 find-up: 6.3.0 - get-tsconfig: 4.7.2 glob: 8.1.0 is-builtin-module: 3.2.1 is-path-inside: 4.0.0 @@ -3654,7 +3858,7 @@ packages: path-exists: 5.0.0 precinct: 11.0.5(supports-color@9.4.0) require-package-name: 2.0.1 - resolve: 2.0.0-next.4 + resolve: 2.0.0-next.5 semver: 7.5.4 tmp-promise: 3.0.3 toml: 3.0.0 @@ -3666,20 +3870,21 @@ packages: - supports-color dev: true - /@netlify/zip-it-and-ship-it@9.23.0(supports-color@9.4.0): - resolution: {integrity: sha512-4Khr9U3VKCEpK13zykbP8g2EgK58c7okRAyC0C+7lU5oopwp5K4lkKvGmZCaNyls3hhjXaX/wLv6K+t1NOIGSw==} + /@netlify/zip-it-and-ship-it@9.24.2(supports-color@9.4.0): + resolution: {integrity: sha512-JENcQgG42n7z7DFIpQhOxnQliVZJ943VamjShkrYD6DZyw2jAEqY9llugDF6mQU/gX83ylaDpJJpfudeprg2KA==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true dependencies: '@babel/parser': 7.23.0 + '@babel/types': 7.22.19 '@netlify/binary-info': 1.0.0 - '@netlify/serverless-functions-api': 1.8.0 + '@netlify/serverless-functions-api': 1.9.0 '@vercel/nft': 0.23.1(supports-color@9.4.0) archiver: 6.0.1 common-path-prefix: 3.0.0 cp-file: 10.0.0 es-module-lexer: 1.3.1 - esbuild: 0.19.2 + esbuild: 0.19.4 execa: 6.1.0 filter-obj: 5.1.0 find-up: 6.3.0 @@ -3695,7 +3900,7 @@ packages: path-exists: 5.0.0 precinct: 11.0.5(supports-color@9.4.0) require-package-name: 2.0.1 - resolve: 2.0.0-next.4 + resolve: 2.0.0-next.5 semver: 7.5.4 tmp-promise: 3.0.3 toml: 3.0.0 @@ -4440,7 +4645,7 @@ packages: tslib: 2.6.2 dev: true - /@pmmmwh/react-refresh-webpack-plugin@0.5.11(@types/webpack@5.28.2)(react-refresh@0.14.0)(webpack-dev-server@4.15.1)(webpack@5.88.2): + /@pmmmwh/react-refresh-webpack-plugin@0.5.11(@types/webpack@5.28.3)(react-refresh@0.14.0)(webpack-dev-server@4.15.1)(webpack@5.88.2): resolution: {integrity: sha512-7j/6vdTym0+qZ6u4XbSAxrWBGYSdCfTzySkj7WAFgDLmSyWlOrWvpyzxlFh5jtw9dn0oL/jtW+06XfFiisN3JQ==} engines: {node: '>= 10.13'} peerDependencies: @@ -4466,7 +4671,7 @@ packages: webpack-plugin-serve: optional: true dependencies: - '@types/webpack': 5.28.2(@swc/core@1.3.86)(webpack-cli@5.1.4) + '@types/webpack': 5.28.3(@swc/core@1.3.92)(webpack-cli@5.1.4) ansi-html-community: 0.0.8 common-path-prefix: 3.0.0 core-js-pure: 3.33.0 @@ -4477,7 +4682,7 @@ packages: react-refresh: 0.14.0 schema-utils: 3.3.0 source-map: 0.7.4 - webpack: 5.88.2(@swc/core@1.3.86)(webpack-cli@5.1.4) + webpack: 5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4) webpack-dev-server: 4.15.1(webpack-cli@5.1.4)(webpack@5.88.2) dev: true @@ -4517,7 +4722,7 @@ packages: react-refresh: 0.14.0 schema-utils: 3.3.0 source-map: 0.7.4 - webpack: 5.88.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.88.2(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4) dev: true /@pnpm/config.env-replace@1.1.0: @@ -4802,9 +5007,9 @@ packages: dependencies: '@babel/core': 7.23.0 '@babel/plugin-transform-react-constant-elements': 7.22.5(@babel/core@7.23.0) - '@babel/preset-env': 7.22.20(@babel/core@7.23.0) + '@babel/preset-env': 7.23.2(@babel/core@7.23.0) '@babel/preset-react': 7.22.15(@babel/core@7.23.0) - '@babel/preset-typescript': 7.23.0(@babel/core@7.23.0) + '@babel/preset-typescript': 7.23.2(@babel/core@7.23.0) '@svgr/core': 8.1.0(typescript@5.2.2) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0) '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0)(typescript@5.2.2) @@ -4813,88 +5018,88 @@ packages: - typescript dev: true - /@swc/core-darwin-arm64@1.3.86: - resolution: {integrity: sha512-hMvSDms0sJJHNtRa3Vhmr9StWN1vmikbf5VE0IZUYGnF1/JZTkXU1h6CdNUY4Hr6i7uCZjH6BEhxFHX1JtKV4w==} + /@swc/core-darwin-arm64@1.3.92: + resolution: {integrity: sha512-v7PqZUBtIF6Q5Cp48gqUiG8zQQnEICpnfNdoiY3xjQAglCGIQCjJIDjreZBoeZQZspB27lQN4eZ43CX18+2SnA==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] requiresBuild: true optional: true - /@swc/core-darwin-x64@1.3.86: - resolution: {integrity: sha512-Jro6HVH4uSOBM7tTDaQNKLNc8BJV7n+SO+Ft2HAZINyeKJS/8MfEYneG7Vmqg18gv00c6dz9AOCcyz+BR7BFkQ==} + /@swc/core-darwin-x64@1.3.92: + resolution: {integrity: sha512-Q3XIgQfXyxxxms3bPN+xGgvwk0TtG9l89IomApu+yTKzaIIlf051mS+lGngjnh9L0aUiCp6ICyjDLtutWP54fw==} engines: {node: '>=10'} cpu: [x64] os: [darwin] requiresBuild: true optional: true - /@swc/core-linux-arm-gnueabihf@1.3.86: - resolution: {integrity: sha512-wYB9m0pzXJVSzedXSl4JwS3gKtvcPinpe9MbkddezpqL7OjyDP6pHHW9qIucsfgCrtMtbPC2nqulXLPtAAyIjw==} + /@swc/core-linux-arm-gnueabihf@1.3.92: + resolution: {integrity: sha512-tnOCoCpNVXC+0FCfG84PBZJyLlz0Vfj9MQhyhCvlJz9hQmvpf8nTdKH7RHrOn8VfxtUBLdVi80dXgIFgbvl7qA==} engines: {node: '>=10'} cpu: [arm] os: [linux] requiresBuild: true optional: true - /@swc/core-linux-arm64-gnu@1.3.86: - resolution: {integrity: sha512-fR44IyK5cdCaO8cC++IEH0Jn03tWnunJnjzA99LxlE5TRInSIOvFm+g5OSUQZDAvEXmQ38sd31LO2HOoDS1Edw==} + /@swc/core-linux-arm64-gnu@1.3.92: + resolution: {integrity: sha512-lFfGhX32w8h1j74Iyz0Wv7JByXIwX11OE9UxG+oT7lG0RyXkF4zKyxP8EoxfLrDXse4Oop434p95e3UNC3IfCw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@swc/core-linux-arm64-musl@1.3.86: - resolution: {integrity: sha512-EUPfdbK4dUk/nkX3Vmv/47XH+DqHOa9JI0CTthvJ8/ZXei1MKDUsUc+tI1zMQX2uCuSkSWsEIEpCmA0tMwFhtw==} + /@swc/core-linux-arm64-musl@1.3.92: + resolution: {integrity: sha512-rOZtRcLj57MSAbiecMsqjzBcZDuaCZ8F6l6JDwGkQ7u1NYR57cqF0QDyU7RKS1Jq27Z/Vg21z5cwqoH5fLN+Sg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@swc/core-linux-x64-gnu@1.3.86: - resolution: {integrity: sha512-snVZZWv8XgNVaKrTxtO3rUN+BbbB6I8Fqwe8zM/DWGJ096J13r89doQ48x5ZyO+bW4D48eZIWP5pdfSW7oBE3w==} + /@swc/core-linux-x64-gnu@1.3.92: + resolution: {integrity: sha512-qptoMGnBL6v89x/Qpn+l1TH1Y0ed+v0qhNfAEVzZvCvzEMTFXphhlhYbDdpxbzRmCjH6GOGq7Y+xrWt9T1/ARg==} engines: {node: '>=10'} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@swc/core-linux-x64-musl@1.3.86: - resolution: {integrity: sha512-PnnksUJymEJkdnbV2orOSOSB441UqsxYbJge9zbr5UTRXUfWO3eFRV0iTBegjTlOQGbW6yN+YRSDkenTbmCI6g==} + /@swc/core-linux-x64-musl@1.3.92: + resolution: {integrity: sha512-g2KrJ43bZkCZHH4zsIV5ErojuV1OIpUHaEyW1gf7JWKaFBpWYVyubzFPvPkjcxHGLbMsEzO7w/NVfxtGMlFH/Q==} engines: {node: '>=10'} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@swc/core-win32-arm64-msvc@1.3.86: - resolution: {integrity: sha512-XlGEGyHwLndm08VvgeAPGj40L+Hx575MQC+2fsyB1uSNUN+uf7fvke+wc7k50a92CaQe/8foLyIR5faayozEJA==} + /@swc/core-win32-arm64-msvc@1.3.92: + resolution: {integrity: sha512-3MCRGPAYDoQ8Yyd3WsCMc8eFSyKXY5kQLyg/R5zEqA0uthomo0m0F5/fxAJMZGaSdYkU1DgF73ctOWOf+Z/EzQ==} engines: {node: '>=10'} cpu: [arm64] os: [win32] requiresBuild: true optional: true - /@swc/core-win32-ia32-msvc@1.3.86: - resolution: {integrity: sha512-U1BhZa1x9yn+wZGTQmt1cYR79a0FzW/wL6Jas1Pn0bykKLxdRU4mCeZt2P+T3buLm8jr8LpPWiCrbvr658PzwA==} + /@swc/core-win32-ia32-msvc@1.3.92: + resolution: {integrity: sha512-zqTBKQhgfWm73SVGS8FKhFYDovyRl1f5dTX1IwSKynO0qHkRCqJwauFJv/yevkpJWsI2pFh03xsRs9HncTQKSA==} engines: {node: '>=10'} cpu: [ia32] os: [win32] requiresBuild: true optional: true - /@swc/core-win32-x64-msvc@1.3.86: - resolution: {integrity: sha512-wRoQUajqpE3wITHhZVj/6BPu/QwHriFHLHuJA+9y6PeGtUtTmntL42aBKXIFhfL767dYFtohyNg1uZ9eqbGyGQ==} + /@swc/core-win32-x64-msvc@1.3.92: + resolution: {integrity: sha512-41bE66ddr9o/Fi1FBh0sHdaKdENPTuDpv1IFHxSg0dJyM/jX8LbkjnpdInYXHBxhcLVAPraVRrNsC4SaoPw2Pg==} engines: {node: '>=10'} cpu: [x64] os: [win32] requiresBuild: true optional: true - /@swc/core@1.3.86(@swc/helpers@0.5.2): - resolution: {integrity: sha512-bEXUtm37bcmJ3q+geG7Zy4rJNUzpxalXQUrrqX1ZoGj3HRtzdeVZ0L/um3fG2j16qe61t8TX/OIZ2G6j6dkG/w==} + /@swc/core@1.3.92(@swc/helpers@0.5.3): + resolution: {integrity: sha512-vx0vUrf4YTEw59njOJ46Ha5i0cZTMYdRHQ7KXU29efN1MxcmJH2RajWLPlvQarOP1ab9iv9cApD7SMchDyx2vA==} engines: {node: '>=10'} requiresBuild: true peerDependencies: @@ -4903,33 +5108,37 @@ packages: '@swc/helpers': optional: true dependencies: - '@swc/helpers': 0.5.2 + '@swc/counter': 0.1.2 + '@swc/helpers': 0.5.3 '@swc/types': 0.1.5 optionalDependencies: - '@swc/core-darwin-arm64': 1.3.86 - '@swc/core-darwin-x64': 1.3.86 - '@swc/core-linux-arm-gnueabihf': 1.3.86 - '@swc/core-linux-arm64-gnu': 1.3.86 - '@swc/core-linux-arm64-musl': 1.3.86 - '@swc/core-linux-x64-gnu': 1.3.86 - '@swc/core-linux-x64-musl': 1.3.86 - '@swc/core-win32-arm64-msvc': 1.3.86 - '@swc/core-win32-ia32-msvc': 1.3.86 - '@swc/core-win32-x64-msvc': 1.3.86 - - /@swc/helpers@0.5.2: - resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} + '@swc/core-darwin-arm64': 1.3.92 + '@swc/core-darwin-x64': 1.3.92 + '@swc/core-linux-arm-gnueabihf': 1.3.92 + '@swc/core-linux-arm64-gnu': 1.3.92 + '@swc/core-linux-arm64-musl': 1.3.92 + '@swc/core-linux-x64-gnu': 1.3.92 + '@swc/core-linux-x64-musl': 1.3.92 + '@swc/core-win32-arm64-msvc': 1.3.92 + '@swc/core-win32-ia32-msvc': 1.3.92 + '@swc/core-win32-x64-msvc': 1.3.92 + + /@swc/counter@0.1.2: + resolution: {integrity: sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==} + + /@swc/helpers@0.5.3: + resolution: {integrity: sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==} dependencies: tslib: 2.6.2 - /@swc/jest@0.2.29(@swc/core@1.3.86): + /@swc/jest@0.2.29(@swc/core@1.3.92): resolution: {integrity: sha512-8reh5RvHBsSikDC3WGCd5ZTd2BXKkyOdK7QwynrCH58jk2cQFhhHhFBg/jvnWZehUQe/EoOImLENc9/DwbBFow==} engines: {npm: '>= 7.0.0'} peerDependencies: '@swc/core': '*' dependencies: '@jest/create-cache-key-function': 27.5.1 - '@swc/core': 1.3.86(@swc/helpers@0.5.2) + '@swc/core': 1.3.92(@swc/helpers@0.5.3) jsonc-parser: 3.2.0 dev: true @@ -4946,18 +5155,18 @@ packages: /@tanstack/query-core@5.0.0-rc.6: resolution: {integrity: sha512-aBGionFwNoPsxnOZPVDIWOStRFWh0/jJgLgC238XyjoML/gN+pwcGNvhp995Pt+uOZPhw5XemzoKCVX9Vpq9KA==} - /@tanstack/query-devtools@5.0.0-rc.5: - resolution: {integrity: sha512-1MBzK9LwKPYxzxm+4oScgxN0Cqn3af4ULsNBK5GTAG7WtxkJjUHAK9Qd6PoXlrzymSGH6xtYdFwdY1zgQ84Vpw==} + /@tanstack/query-devtools@5.0.0-rc.10: + resolution: {integrity: sha512-wtxlnd/j6sTUGX3l5xH3WB2NKptsLelVYuRO49NMV79Bm1aNVgGh21rJmWDmsig+4MNRA22CfpouuTMh8+3AbA==} dev: true - /@tanstack/react-query-devtools@5.0.0-rc.7(@tanstack/react-query@5.0.0-rc.7)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-ZnqHgLH5WicurJcs32+ThwppnSztv9oQp+qGWl/5nfAQh/yt2d+bH0VcqbAx7wKynjA8Vg/LXBSdiVhHsy30gA==} + /@tanstack/react-query-devtools@5.0.0-rc.10(@tanstack/react-query@5.0.0-rc.7)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-NSQrNPoRW5MY1g1r9Q4coEOf1BO87a5GxYTOeZvHFJGvVPIDToOo4NsKqaWQ8hMLR9QBoLqzoUx1MUg8awuRQA==} peerDependencies: '@tanstack/react-query': ^5.0.0-rc.7 react: ^18.0.0 react-dom: ^18.0.0 dependencies: - '@tanstack/query-devtools': 5.0.0-rc.5 + '@tanstack/query-devtools': 5.0.0-rc.10 '@tanstack/react-query': 5.0.0-rc.7(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -4985,7 +5194,7 @@ packages: engines: {node: '>=14'} dependencies: '@babel/code-frame': 7.22.13 - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.23.2 '@types/aria-query': 5.0.2 aria-query: 5.1.3 chalk: 4.1.2 @@ -5001,9 +5210,9 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.23.2 '@testing-library/dom': 9.3.3 - '@types/react-dom': 18.2.7 + '@types/react-dom': 18.2.13 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true @@ -5081,29 +5290,29 @@ packages: resolution: {integrity: sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==} dependencies: '@types/connect': 3.4.36 - '@types/node': 20.6.3 + '@types/node': 20.8.4 /@types/bonjour@3.5.11: resolution: {integrity: sha512-isGhjmBtLIxdHBDl2xGwUzEM8AOyOvWsADWq7rqirdi/ZQoHnLWErHvsThcEzTX8juDRiZtzp2Qkv5bgNh6mAg==} dependencies: - '@types/node': 20.6.3 + '@types/node': 20.8.4 /@types/concat-stream@2.0.0: resolution: {integrity: sha512-t3YCerNM7NTVjLuICZo5gYAXYoDvpuuTceCcFQWcDQz26kxUR5uIWolxbIR5jRNIXpMqhOpW/b8imCR1LEmuJw==} dependencies: - '@types/node': 20.6.3 + '@types/node': 18.18.4 dev: true /@types/connect-history-api-fallback@1.5.1: resolution: {integrity: sha512-iaQslNbARe8fctL5Lk+DsmgWOM83lM+7FzP0eQUJs1jd3kBE8NWqBTIT2S8SqQOJjxvt2eyIjpOuYeRXq2AdMw==} dependencies: '@types/express-serve-static-core': 4.17.37 - '@types/node': 20.6.3 + '@types/node': 20.8.4 /@types/connect@3.4.36: resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} dependencies: - '@types/node': 20.6.3 + '@types/node': 20.8.4 /@types/cookie@0.4.1: resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} @@ -5118,11 +5327,11 @@ packages: /@types/eslint-scope@3.7.5: resolution: {integrity: sha512-JNvhIEyxVW6EoMIFIvj93ZOywYFatlpu9deeH6eSx6PE3WHYvHaQtmHmQeNw7aA81bYGBPPQqdtBm6b1SsQMmA==} dependencies: - '@types/eslint': 8.44.3 + '@types/eslint': 8.44.4 '@types/estree': 1.0.2 - /@types/eslint@8.44.3: - resolution: {integrity: sha512-iM/WfkwAhwmPff3wZuPLYiHX18HI24jU8k1ZSH7P8FHwxTjZ2P6CoX2wnF43oprR+YXJM6UUxATkNvyv/JHd+g==} + /@types/eslint@8.44.4: + resolution: {integrity: sha512-lOzjyfY/D9QR4hY9oblZ76B90MYTB3RrQ4z2vBIJKj9ROCRqdkYl2gSUx1x1a4IWPjKJZLL4Aw1Zfay7eMnmnA==} dependencies: '@types/estree': 1.0.2 '@types/json-schema': 7.0.13 @@ -5139,13 +5348,13 @@ packages: /@types/express-serve-static-core@4.17.37: resolution: {integrity: sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==} dependencies: - '@types/node': 20.6.3 + '@types/node': 20.8.4 '@types/qs': 6.9.8 '@types/range-parser': 1.2.5 '@types/send': 0.17.2 - /@types/express@4.17.18: - resolution: {integrity: sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ==} + /@types/express@4.17.19: + resolution: {integrity: sha512-UtOfBtzN9OvpZPPbnnYunfjM7XCI4jyk1NvnFhTVz5krYAnW4o5DCoIekvms+8ApqhB4+9wSge1kBijdfTSmfg==} dependencies: '@types/body-parser': 1.19.3 '@types/express-serve-static-core': 4.17.37 @@ -5155,7 +5364,7 @@ packages: /@types/graceful-fs@4.1.7: resolution: {integrity: sha512-MhzcwU8aUygZroVwL2jeYk6JisJrPl/oov/gsgGCue9mkgl9wjGbzReYQClxiUgFDnib9FuHqTndccKeZKxTRw==} dependencies: - '@types/node': 20.6.3 + '@types/node': 20.8.4 dev: true /@types/hast@2.3.6: @@ -5177,10 +5386,10 @@ packages: /@types/http-proxy@1.17.12: resolution: {integrity: sha512-kQtujO08dVtQ2wXAuSFfk9ASy3sug4+ogFR8Kd8UgP8PEuc1/G/8yjYRmp//PcDNJEUKOza/MrQu15bouEUCiw==} dependencies: - '@types/node': 20.6.3 + '@types/node': 20.8.4 - /@types/is-ci@3.0.1: - resolution: {integrity: sha512-mnb1ngaGQPm6LFZaNdh3xPOoQMkrQb/KBPhPPN2p2Wk8XgeUqWj6xPnvyQ8rvcK/VFritVmQG8tvQuy7g+9/nQ==} + /@types/is-ci@3.0.2: + resolution: {integrity: sha512-9PyP1rgCro6xO3R7zOEoMgx5U9HpLhIg1FFb9p2mWX/x5QI8KMuCWWYtCT1dUQpicp84OsxEAw3iqwIKQY5Pog==} dependencies: ci-info: 3.9.0 dev: true @@ -5219,7 +5428,7 @@ packages: /@types/jsdom@20.0.1: resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} dependencies: - '@types/node': 20.6.3 + '@types/node': 20.8.4 '@types/tough-cookie': 4.0.3 parse5: 7.1.2 dev: true @@ -5259,8 +5468,10 @@ packages: resolution: {integrity: sha512-t3rNFBgJRugIhackit2mVcLfF6IRc0JE4oeizPQL8Zrm8n2WY/0wOdpOPhdtG0V9Q2TlW/axbF1MJ6z+Yj/kKQ==} dev: true - /@types/node@20.6.3: - resolution: {integrity: sha512-HksnYH4Ljr4VQgEy2lTStbCKv/P590tmPe5HqOnv9Gprffgv5WXAY+Y5Gqniu0GGqeTCUdBnzC3QSrzPkBkAMA==} + /@types/node@20.8.4: + resolution: {integrity: sha512-ZVPnqU58giiCjSxjVUESDtdPk4QR5WQhhINbc9UBrKLU68MX5BF6kbQzTrkwbolyr0X8ChBpXfavr5mZFKZQ5A==} + dependencies: + undici-types: 5.25.3 /@types/normalize-package-data@2.4.2: resolution: {integrity: sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A==} @@ -5276,20 +5487,20 @@ packages: /@types/range-parser@1.2.5: resolution: {integrity: sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==} - /@types/react-dom@18.2.7: - resolution: {integrity: sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==} + /@types/react-dom@18.2.13: + resolution: {integrity: sha512-eJIUv7rPP+EC45uNYp/ThhSpE16k22VJUknt5OLoH9tbXoi8bMhwLf5xRuWMywamNbWzhrSmU7IBJfPup1+3fw==} dependencies: - '@types/react': 18.2.22 + '@types/react': 18.2.28 dev: true - /@types/react-test-renderer@18.0.2: - resolution: {integrity: sha512-tJzMn+9GHDrdrLe0O4rwJELDfTrmdJbCn/UdYyzjlnPiXYXDl5FBNzdw4PVk2R3hJvSHKFjZcRgvZc12lV0p5Q==} + /@types/react-test-renderer@18.0.3: + resolution: {integrity: sha512-4wcNLnY6nIT+L6g94CpzL4CXX2P18JvKPU9CDlaHr3DnbP3GiaQLhDotJqjWlVqOcE4UhLRjp0MtxqwuNKONnA==} dependencies: - '@types/react': 18.2.22 + '@types/react': 18.2.28 dev: true - /@types/react@18.2.22: - resolution: {integrity: sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==} + /@types/react@18.2.28: + resolution: {integrity: sha512-ad4aa/RaaJS3hyGz0BGegdnSRXQBkd1CCYDCdNjBPg90UUpLgo+WlJqb9fMYUxtehmzF3PJaTWqRZjko6BRzBg==} dependencies: '@types/prop-types': 15.7.8 '@types/scheduler': 0.16.4 @@ -5315,30 +5526,30 @@ packages: resolution: {integrity: sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==} dependencies: '@types/mime': 1.3.3 - '@types/node': 20.6.3 + '@types/node': 20.8.4 /@types/serve-index@1.9.2: resolution: {integrity: sha512-asaEIoc6J+DbBKXtO7p2shWUpKacZOoMBEGBgPG91P8xhO53ohzHWGCs4ScZo5pQMf5ukQzVT9fhX1WzpHihig==} dependencies: - '@types/express': 4.17.18 + '@types/express': 4.17.19 /@types/serve-static@1.15.3: resolution: {integrity: sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==} dependencies: '@types/http-errors': 2.0.2 '@types/mime': 3.0.2 - '@types/node': 20.6.3 + '@types/node': 20.8.4 /@types/set-cookie-parser@2.4.4: resolution: {integrity: sha512-xCfTC/eL/GmvMC24b42qJpYSTdCIBwWcfskDF80ztXtnU6pKXyvuZP2EConb2K9ps0s7gMhCa0P1foy7wiItMA==} dependencies: - '@types/node': 20.6.3 + '@types/node': 20.8.4 dev: true /@types/sockjs@0.3.34: resolution: {integrity: sha512-R+n7qBFnm/6jinlteC9DBL5dGiDGjWAvjo4viUanpnc/dG1y7uDoacXPIQ/PQEg1fI912SMHIa014ZjRpvDw4g==} dependencies: - '@types/node': 20.6.3 + '@types/node': 20.8.4 /@types/stack-utils@2.0.1: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} @@ -5360,12 +5571,12 @@ packages: resolution: {integrity: sha512-d0XxK3YTObnWVp6rZuev3c49+j4Lo8g4L1ZRm9z5L0xpoZycUPshHgczK5gsUMaZOstjVYYi09p5gYvUtfChYw==} dev: true - /@types/webpack@5.28.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4): - resolution: {integrity: sha512-7tcxyrIOd7WGimZIcWU6pDsNh2edGGnwYExOvd3l/nMvuxqwVPrFXnnTbYCnplqV9BJoU7Mo2mfFtiH8CNFvYw==} + /@types/webpack@5.28.3(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4): + resolution: {integrity: sha512-Hd0GBzpP0mO2ZKChw2V7flK45m01/2g9FalpMum2X66uouzG3P1vkxvOtLVzAWLna4N9h0s2sVjND9Slnlef8A==} dependencies: - '@types/node': 20.6.3 + '@types/node': 20.8.4 tapable: 2.2.1 - webpack: 5.88.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.88.2(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - '@swc/core' - esbuild @@ -5373,12 +5584,12 @@ packages: - webpack-cli dev: true - /@types/webpack@5.28.2(@swc/core@1.3.86)(webpack-cli@5.1.4): - resolution: {integrity: sha512-7tcxyrIOd7WGimZIcWU6pDsNh2edGGnwYExOvd3l/nMvuxqwVPrFXnnTbYCnplqV9BJoU7Mo2mfFtiH8CNFvYw==} + /@types/webpack@5.28.3(@swc/core@1.3.92)(webpack-cli@5.1.4): + resolution: {integrity: sha512-Hd0GBzpP0mO2ZKChw2V7flK45m01/2g9FalpMum2X66uouzG3P1vkxvOtLVzAWLna4N9h0s2sVjND9Slnlef8A==} dependencies: - '@types/node': 20.6.3 + '@types/node': 20.8.4 tapable: 2.2.1 - webpack: 5.88.2(@swc/core@1.3.86)(webpack-cli@5.1.4) + webpack: 5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4) transitivePeerDependencies: - '@swc/core' - esbuild @@ -5386,10 +5597,10 @@ packages: - webpack-cli dev: true - /@types/ws@8.5.6: - resolution: {integrity: sha512-8B5EO9jLVCy+B58PLHvLDuOD8DRVMgQzq8d55SjLCOn9kqGyqOvy27exVaTio1q1nX5zLu8/6N0n2ThSxOM6tg==} + /@types/ws@8.5.7: + resolution: {integrity: sha512-6UrLjiDUvn40CMrAubXuIVtj2PEfKDffJS7ychvnPU44j+KVeXmdHHTgqcM/dxLUTHxlXHiFM8Skmb8ozGdTnQ==} dependencies: - '@types/node': 20.6.3 + '@types/node': 20.8.4 /@types/yargs-parser@21.0.1: resolution: {integrity: sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==} @@ -5411,11 +5622,11 @@ packages: resolution: {integrity: sha512-CHzgNU3qYBnp/O4S3yv2tXPlvMTq0YWSTVg2/JYLqWZGHwwgJGAwd00poay/11asPq8wLFwHzubyInqHIFmmiw==} requiresBuild: true dependencies: - '@types/node': 20.6.3 + '@types/node': 20.8.4 dev: true optional: true - /@typescript-eslint/eslint-plugin@6.7.5(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2): + /@typescript-eslint/eslint-plugin@6.7.5(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(typescript@5.2.2): resolution: {integrity: sha512-JhtAwTRhOUcP96D0Y6KYnwig/MRQbOoLGXTON2+LlyB/N35SP9j1boai2zzwXb7ypKELXMx3DVk9UTaEq1vHEw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -5427,13 +5638,13 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.9.1 - '@typescript-eslint/parser': 6.7.2(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.7.5(eslint@8.51.0)(typescript@5.2.2) '@typescript-eslint/scope-manager': 6.7.5 - '@typescript-eslint/type-utils': 6.7.5(eslint@8.49.0)(typescript@5.2.2) - '@typescript-eslint/utils': 6.7.5(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/type-utils': 6.7.5(eslint@8.51.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.5(eslint@8.51.0)(typescript@5.2.2) '@typescript-eslint/visitor-keys': 6.7.5 debug: 4.3.4(supports-color@9.4.0) - eslint: 8.49.0 + eslint: 8.51.0 graphemer: 1.4.0 ignore: 5.2.4 natural-compare: 1.4.0 @@ -5444,8 +5655,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@6.7.2(eslint@8.49.0)(typescript@5.2.2): - resolution: {integrity: sha512-KA3E4ox0ws+SPyxQf9iSI25R6b4Ne78ORhNHeVKrPQnoYsb9UhieoiRoJgrzgEeKGOXhcY1i8YtOeCHHTDa6Fw==} + /@typescript-eslint/parser@6.7.5(eslint@8.51.0)(typescript@5.2.2): + resolution: {integrity: sha512-bIZVSGx2UME/lmhLcjdVc7ePBwn7CLqKarUBL4me1C5feOd663liTGjMBGVcGr+BhnSLeP4SgwdvNnnkbIdkCw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -5454,12 +5665,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.7.2 - '@typescript-eslint/types': 6.7.2 - '@typescript-eslint/typescript-estree': 6.7.2(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.7.2 + '@typescript-eslint/scope-manager': 6.7.5 + '@typescript-eslint/types': 6.7.5 + '@typescript-eslint/typescript-estree': 6.7.5(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.7.5 debug: 4.3.4(supports-color@9.4.0) - eslint: 8.49.0 + eslint: 8.51.0 typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -5473,14 +5684,6 @@ packages: '@typescript-eslint/visitor-keys': 5.62.0 dev: true - /@typescript-eslint/scope-manager@6.7.2: - resolution: {integrity: sha512-bgi6plgyZjEqapr7u2mhxGR6E8WCzKNUFWNh6fkpVe9+yzRZeYtDTbsIBzKbcxI+r1qVWt6VIoMSNZ4r2A+6Yw==} - engines: {node: ^16.0.0 || >=18.0.0} - dependencies: - '@typescript-eslint/types': 6.7.2 - '@typescript-eslint/visitor-keys': 6.7.2 - dev: true - /@typescript-eslint/scope-manager@6.7.5: resolution: {integrity: sha512-GAlk3eQIwWOJeb9F7MKQ6Jbah/vx1zETSDw8likab/eFcqkjSD7BI75SDAeC5N2L0MmConMoPvTsmkrg71+B1A==} engines: {node: ^16.0.0 || >=18.0.0} @@ -5489,7 +5692,7 @@ packages: '@typescript-eslint/visitor-keys': 6.7.5 dev: true - /@typescript-eslint/type-utils@6.7.5(eslint@8.49.0)(typescript@5.2.2): + /@typescript-eslint/type-utils@6.7.5(eslint@8.51.0)(typescript@5.2.2): resolution: {integrity: sha512-Gs0qos5wqxnQrvpYv+pf3XfcRXW6jiAn9zE/K+DlmYf6FcpxeNYN0AIETaPR7rHO4K2UY+D0CIbDP9Ut0U4m1g==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -5500,9 +5703,9 @@ packages: optional: true dependencies: '@typescript-eslint/typescript-estree': 6.7.5(typescript@5.2.2) - '@typescript-eslint/utils': 6.7.5(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.5(eslint@8.51.0)(typescript@5.2.2) debug: 4.3.4(supports-color@9.4.0) - eslint: 8.49.0 + eslint: 8.51.0 ts-api-utils: 1.0.3(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: @@ -5514,11 +5717,6 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/types@6.7.2: - resolution: {integrity: sha512-flJYwMYgnUNDAN9/GAI3l8+wTmvTYdv64fcH8aoJK76Y+1FCZ08RtI5zDerM/FYT5DMkAc+19E4aLmd5KqdFyg==} - engines: {node: ^16.0.0 || >=18.0.0} - dev: true - /@typescript-eslint/types@6.7.5: resolution: {integrity: sha512-WboQBlOXtdj1tDFPyIthpKrUb+kZf2VroLZhxKa/VlwLlLyqv/PwUNgL30BlTVZV1Wu4Asu2mMYPqarSO4L5ZQ==} engines: {node: ^16.0.0 || >=18.0.0} @@ -5545,27 +5743,6 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree@6.7.2(typescript@5.2.2): - resolution: {integrity: sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/types': 6.7.2 - '@typescript-eslint/visitor-keys': 6.7.2 - debug: 4.3.4(supports-color@9.4.0) - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.2.2) - typescript: 5.2.2 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/typescript-estree@6.7.5(typescript@5.2.2): resolution: {integrity: sha512-NhJiJ4KdtwBIxrKl0BqG1Ur+uw7FiOnOThcYx9DpOGJ/Abc9z2xNzLeirCG02Ig3vkvrc2qFLmYSSsaITbKjlg==} engines: {node: ^16.0.0 || >=18.0.0} @@ -5587,19 +5764,19 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@5.62.0(eslint@8.49.0)(typescript@5.2.2): + /@typescript-eslint/utils@5.62.0(eslint@8.51.0)(typescript@5.2.2): resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) '@types/json-schema': 7.0.13 '@types/semver': 7.5.3 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(supports-color@9.4.0)(typescript@5.2.2) - eslint: 8.49.0 + eslint: 8.51.0 eslint-scope: 5.1.1 semver: 7.5.4 transitivePeerDependencies: @@ -5607,19 +5784,19 @@ packages: - typescript dev: true - /@typescript-eslint/utils@6.7.5(eslint@8.49.0)(typescript@5.2.2): + /@typescript-eslint/utils@6.7.5(eslint@8.51.0)(typescript@5.2.2): resolution: {integrity: sha512-pfRRrH20thJbzPPlPc4j0UNGvH1PjPlhlCMq4Yx7EGjV7lvEeGX0U6MJYe8+SyFutWgSHsdbJ3BXzZccYggezA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) '@types/json-schema': 7.0.13 '@types/semver': 7.5.3 '@typescript-eslint/scope-manager': 6.7.5 '@typescript-eslint/types': 6.7.5 '@typescript-eslint/typescript-estree': 6.7.5(typescript@5.2.2) - eslint: 8.49.0 + eslint: 8.51.0 semver: 7.5.4 transitivePeerDependencies: - supports-color @@ -5634,14 +5811,6 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@typescript-eslint/visitor-keys@6.7.2: - resolution: {integrity: sha512-uVw9VIMFBUTz8rIeaUT3fFe8xIUx8r4ywAdlQv1ifH+6acn/XF8Y6rwJ7XNmkNMDrTW+7+vxFFPIF40nJCVsMQ==} - engines: {node: ^16.0.0 || >=18.0.0} - dependencies: - '@typescript-eslint/types': 6.7.2 - eslint-visitor-keys: 3.4.3 - dev: true - /@typescript-eslint/visitor-keys@6.7.5: resolution: {integrity: sha512-3MaWdDZtLlsexZzDSdQWsFQ9l9nL8B80Z4fImSpyllFC/KLqWQRdEcB+gGGO+N3Q2uL40EsG66wZLsohPxNXvg==} engines: {node: ^16.0.0 || >=18.0.0} @@ -5769,7 +5938,7 @@ packages: webpack: 5.x.x webpack-cli: 5.x.x dependencies: - webpack: 5.88.2(@swc/core@1.3.86)(webpack-cli@5.1.4) + webpack: 5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack-dev-server@4.15.1)(webpack@5.88.2) /@webpack-cli/info@2.0.2(webpack-cli@5.1.4)(webpack@5.88.2): @@ -5779,7 +5948,7 @@ packages: webpack: 5.x.x webpack-cli: 5.x.x dependencies: - webpack: 5.88.2(@swc/core@1.3.86)(webpack-cli@5.1.4) + webpack: 5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack-dev-server@4.15.1)(webpack@5.88.2) /@webpack-cli/serve@2.0.5(webpack-cli@5.1.4)(webpack-dev-server@4.15.1)(webpack@5.88.2): @@ -5793,7 +5962,7 @@ packages: webpack-dev-server: optional: true dependencies: - webpack: 5.88.2(@swc/core@1.3.86)(webpack-cli@5.1.4) + webpack: 5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack-dev-server@4.15.1)(webpack@5.88.2) webpack-dev-server: 4.15.1(webpack-cli@5.1.4)(webpack@5.88.2) @@ -5801,8 +5970,8 @@ packages: resolution: {integrity: sha512-FPaGB35ALxtIWMmi1yrai1uyI+EKrkCwzLl6UAaHwcPoIhHBWqUPMlhP3N5wpY1+HyYFzKeOIk847re3javgiw==} dev: true - /@workleap/eslint-plugin@2.1.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2): - resolution: {integrity: sha512-lTemTwgotQP4KKtN5LGDu/nkLSdtHM1+rqmkWmilVfxVfzYnpRJYCjmB1OtSCVZZTreP0oU+xwkgo9MQdJfoIQ==} + /@workleap/eslint-plugin@3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2): + resolution: {integrity: sha512-GRvezpKhUW/UcjQrzTQ6T9KsF3Hr6UnCMS137+VVMjGd0hfGdUMwUf9rMpe2L2pFKlYfJRXuvHoVTzBlnVUPeg==} peerDependencies: '@typescript-eslint/parser': '*' eslint: '*' @@ -5815,17 +5984,17 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 6.7.5(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2) - '@typescript-eslint/parser': 6.7.2(eslint@8.49.0)(typescript@5.2.2) - eslint: 8.49.0 - eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0) - eslint-plugin-jest: 27.4.2(@typescript-eslint/eslint-plugin@6.7.5)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2) - eslint-plugin-jsx-a11y: 6.7.1(eslint@8.49.0) - eslint-plugin-mdx: 2.2.0(eslint@8.49.0) - eslint-plugin-react: 7.33.2(eslint@8.49.0) - eslint-plugin-react-hooks: 4.6.0(eslint@8.49.0) - eslint-plugin-storybook: 0.6.15(eslint@8.49.0)(typescript@5.2.2) - eslint-plugin-testing-library: 6.0.2(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/eslint-plugin': 6.7.5(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.7.5(eslint@8.51.0)(typescript@5.2.2) + eslint: 8.51.0 + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.5)(eslint@8.51.0) + eslint-plugin-jest: 27.4.2(@typescript-eslint/eslint-plugin@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) + eslint-plugin-jsx-a11y: 6.7.1(eslint@8.51.0) + eslint-plugin-mdx: 2.2.0(eslint@8.51.0) + eslint-plugin-react: 7.33.2(eslint@8.51.0) + eslint-plugin-react-hooks: 4.6.0(eslint@8.51.0) + eslint-plugin-storybook: 0.6.15(eslint@8.51.0)(typescript@5.2.2) + eslint-plugin-testing-library: 6.0.2(eslint@8.51.0)(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - eslint-import-resolver-typescript @@ -5834,7 +6003,7 @@ packages: - supports-color dev: true - /@workleap/swc-configs@2.1.2(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@swc/jest@0.2.29)(browserslist@4.21.10): + /@workleap/swc-configs@2.1.2(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@swc/jest@0.2.29)(browserslist@4.22.1): resolution: {integrity: sha512-qMTxLKvNoTys4YxXtvIRWVQiuB46FfCajVpgVrMshtAA3Zydtjy4IGirCOGcalcSbuc12mOseBpxiLV9wayneg==} peerDependencies: '@swc/core': '*' @@ -5847,10 +6016,10 @@ packages: browserslist: optional: true dependencies: - '@swc/core': 1.3.86(@swc/helpers@0.5.2) - '@swc/helpers': 0.5.2 - '@swc/jest': 0.2.29(@swc/core@1.3.86) - browserslist: 4.21.10 + '@swc/core': 1.3.92(@swc/helpers@0.5.3) + '@swc/helpers': 0.5.3 + '@swc/jest': 0.2.29(@swc/core@1.3.92) + browserslist: 4.22.1 dev: true /@workleap/tsup-configs@3.0.1(tsup@7.2.0)(typescript@5.2.2): @@ -5859,7 +6028,7 @@ packages: tsup: '*' typescript: '*' dependencies: - tsup: 7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) + tsup: 7.2.0(@swc/core@1.3.92)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: 5.2.2 dev: true @@ -5871,8 +6040,8 @@ packages: typescript: 5.2.2 dev: true - /@workleap/webpack-configs@1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(esbuild@0.18.20)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2): - resolution: {integrity: sha512-AVTvHu5qeFVbfp9eA4KftUU8gJRRx7Rcx17jO4gGtFpLf4ADSGi4fE6eDNwGbGgHlhRrZTPYjCyHLNwPID+Pfg==} + /@workleap/webpack-configs@1.1.0(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@types/webpack@5.28.3)(browserslist@4.22.1)(esbuild@0.18.20)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2): + resolution: {integrity: sha512-bz5xU0LYdrTTOe+PVTgHrn+dBoLBURGxZx7xTckJ8q9H6DaRWAT9NbRTSzbG2N+Wer2pRISdvkeJ7MmubD11ng==} peerDependencies: '@swc/core': '*' '@swc/helpers': '*' @@ -5884,20 +6053,20 @@ packages: webpack-dev-server: optional: true dependencies: - '@pmmmwh/react-refresh-webpack-plugin': 0.5.11(@types/webpack@5.28.2)(react-refresh@0.14.0)(webpack-dev-server@4.15.1)(webpack@5.88.2) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.11(@types/webpack@5.28.3)(react-refresh@0.14.0)(webpack-dev-server@4.15.1)(webpack@5.88.2) '@svgr/webpack': 8.1.0(typescript@5.2.2) - '@swc/core': 1.3.86(@swc/helpers@0.5.2) - '@swc/helpers': 0.5.2 - browserslist: 4.21.10 + '@swc/core': 1.3.92(@swc/helpers@0.5.3) + '@swc/helpers': 0.5.3 + browserslist: 4.22.1 css-loader: 6.8.1(webpack@5.88.2) html-webpack-plugin: 5.5.3(webpack@5.88.2) mini-css-extract-plugin: 2.7.6(webpack@5.88.2) postcss: 8.4.31 postcss-loader: 7.3.3(postcss@8.4.31)(typescript@5.2.2)(webpack@5.88.2) style-loader: 3.3.3(webpack@5.88.2) - swc-loader: 0.2.3(@swc/core@1.3.86)(webpack@5.88.2) - terser-webpack-plugin: 5.3.9(@swc/core@1.3.86)(esbuild@0.18.20)(webpack@5.88.2) - webpack: 5.88.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4) + swc-loader: 0.2.3(@swc/core@1.3.92)(webpack@5.88.2) + terser-webpack-plugin: 5.3.9(@swc/core@1.3.92)(esbuild@0.18.20)(webpack@5.88.2) + webpack: 5.88.2(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4) webpack-dev-server: 4.15.1(webpack-cli@5.1.4)(webpack@5.88.2) transitivePeerDependencies: - '@types/webpack' @@ -5912,8 +6081,8 @@ packages: - webpack-plugin-serve dev: true - /@workleap/webpack-configs@1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(@types/webpack@5.28.2)(browserslist@4.21.10)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2): - resolution: {integrity: sha512-AVTvHu5qeFVbfp9eA4KftUU8gJRRx7Rcx17jO4gGtFpLf4ADSGi4fE6eDNwGbGgHlhRrZTPYjCyHLNwPID+Pfg==} + /@workleap/webpack-configs@1.1.0(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@types/webpack@5.28.3)(browserslist@4.22.1)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2): + resolution: {integrity: sha512-bz5xU0LYdrTTOe+PVTgHrn+dBoLBURGxZx7xTckJ8q9H6DaRWAT9NbRTSzbG2N+Wer2pRISdvkeJ7MmubD11ng==} peerDependencies: '@swc/core': '*' '@swc/helpers': '*' @@ -5925,20 +6094,20 @@ packages: webpack-dev-server: optional: true dependencies: - '@pmmmwh/react-refresh-webpack-plugin': 0.5.11(@types/webpack@5.28.2)(react-refresh@0.14.0)(webpack-dev-server@4.15.1)(webpack@5.88.2) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.11(@types/webpack@5.28.3)(react-refresh@0.14.0)(webpack-dev-server@4.15.1)(webpack@5.88.2) '@svgr/webpack': 8.1.0(typescript@5.2.2) - '@swc/core': 1.3.86(@swc/helpers@0.5.2) - '@swc/helpers': 0.5.2 - browserslist: 4.21.10 + '@swc/core': 1.3.92(@swc/helpers@0.5.3) + '@swc/helpers': 0.5.3 + browserslist: 4.22.1 css-loader: 6.8.1(webpack@5.88.2) html-webpack-plugin: 5.5.3(webpack@5.88.2) mini-css-extract-plugin: 2.7.6(webpack@5.88.2) postcss: 8.4.31 postcss-loader: 7.3.3(postcss@8.4.31)(typescript@5.2.2)(webpack@5.88.2) style-loader: 3.3.3(webpack@5.88.2) - swc-loader: 0.2.3(@swc/core@1.3.86)(webpack@5.88.2) - terser-webpack-plugin: 5.3.9(@swc/core@1.3.86)(webpack@5.88.2) - webpack: 5.88.2(@swc/core@1.3.86)(webpack-cli@5.1.4) + swc-loader: 0.2.3(@swc/core@1.3.92)(webpack@5.88.2) + terser-webpack-plugin: 5.3.9(@swc/core@1.3.92)(webpack@5.88.2) + webpack: 5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4) webpack-dev-server: 4.15.1(webpack-cli@5.1.4)(webpack@5.88.2) transitivePeerDependencies: - '@types/webpack' @@ -5953,8 +6122,8 @@ packages: - webpack-plugin-serve dev: true - /@workleap/webpack-configs@1.0.8(@swc/core@1.3.86)(@swc/helpers@0.5.2)(browserslist@4.21.10)(esbuild@0.18.20)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack@5.88.2): - resolution: {integrity: sha512-AVTvHu5qeFVbfp9eA4KftUU8gJRRx7Rcx17jO4gGtFpLf4ADSGi4fE6eDNwGbGgHlhRrZTPYjCyHLNwPID+Pfg==} + /@workleap/webpack-configs@1.1.0(@swc/core@1.3.92)(@swc/helpers@0.5.3)(browserslist@4.22.1)(esbuild@0.18.20)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack@5.88.2): + resolution: {integrity: sha512-bz5xU0LYdrTTOe+PVTgHrn+dBoLBURGxZx7xTckJ8q9H6DaRWAT9NbRTSzbG2N+Wer2pRISdvkeJ7MmubD11ng==} peerDependencies: '@swc/core': '*' '@swc/helpers': '*' @@ -5968,18 +6137,18 @@ packages: dependencies: '@pmmmwh/react-refresh-webpack-plugin': 0.5.11(react-refresh@0.14.0)(webpack@5.88.2) '@svgr/webpack': 8.1.0(typescript@5.2.2) - '@swc/core': 1.3.86(@swc/helpers@0.5.2) - '@swc/helpers': 0.5.2 - browserslist: 4.21.10 + '@swc/core': 1.3.92(@swc/helpers@0.5.3) + '@swc/helpers': 0.5.3 + browserslist: 4.22.1 css-loader: 6.8.1(webpack@5.88.2) html-webpack-plugin: 5.5.3(webpack@5.88.2) mini-css-extract-plugin: 2.7.6(webpack@5.88.2) postcss: 8.4.31 postcss-loader: 7.3.3(postcss@8.4.31)(typescript@5.2.2)(webpack@5.88.2) style-loader: 3.3.3(webpack@5.88.2) - swc-loader: 0.2.3(@swc/core@1.3.86)(webpack@5.88.2) - terser-webpack-plugin: 5.3.9(@swc/core@1.3.86)(esbuild@0.18.20)(webpack@5.88.2) - webpack: 5.88.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4) + swc-loader: 0.2.3(@swc/core@1.3.92)(webpack@5.88.2) + terser-webpack-plugin: 5.3.9(@swc/core@1.3.92)(esbuild@0.18.20)(webpack@5.88.2) + webpack: 5.88.2(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - '@types/webpack' - esbuild @@ -6680,38 +6849,38 @@ packages: '@types/babel__traverse': 7.20.2 dev: true - /babel-plugin-polyfill-corejs2@0.4.5(@babel/core@7.23.0): - resolution: {integrity: sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==} + /babel-plugin-polyfill-corejs2@0.4.6(@babel/core@7.23.0): + resolution: {integrity: sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/compat-data': 7.22.20 + '@babel/compat-data': 7.23.2 '@babel/core': 7.23.0 - '@babel/helper-define-polyfill-provider': 0.4.2(@babel/core@7.23.0) + '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.0) semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-corejs3@0.8.4(@babel/core@7.23.0): - resolution: {integrity: sha512-9l//BZZsPR+5XjyJMPtZSK4jv0BsTO1zDac2GC6ygx9WLGlcsnRd1Co0B2zT5fF5Ic6BZy+9m3HNZ3QcOeDKfg==} + /babel-plugin-polyfill-corejs3@0.8.5(@babel/core@7.23.0): + resolution: {integrity: sha512-Q6CdATeAvbScWPNLB8lzSO7fgUVBkQt6zLgNlfyeCr/EQaEQR+bWiBYYPYAFyE528BMjRhL+1QBMOI4jc/c5TA==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: '@babel/core': 7.23.0 - '@babel/helper-define-polyfill-provider': 0.4.2(@babel/core@7.23.0) + '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.0) core-js-compat: 3.33.0 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-regenerator@0.5.2(@babel/core@7.23.0): - resolution: {integrity: sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==} + /babel-plugin-polyfill-regenerator@0.5.3(@babel/core@7.23.0): + resolution: {integrity: sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: '@babel/core': 7.23.0 - '@babel/helper-define-polyfill-provider': 0.4.2(@babel/core@7.23.0) + '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.0) transitivePeerDependencies: - supports-color dev: true @@ -6952,26 +7121,15 @@ packages: wcwidth: 1.0.1 dev: true - /browserslist@4.21.10: - resolution: {integrity: sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - dependencies: - caniuse-lite: 1.0.30001547 - electron-to-chromium: 1.4.548 - node-releases: 2.0.13 - update-browserslist-db: 1.0.13(browserslist@4.21.10) - /browserslist@4.22.1: resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: caniuse-lite: 1.0.30001547 - electron-to-chromium: 1.4.548 + electron-to-chromium: 1.4.551 node-releases: 2.0.13 update-browserslist-db: 1.0.13(browserslist@4.22.1) - dev: true /bs-logger@0.2.6: resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} @@ -7763,7 +7921,7 @@ packages: readable-stream: 3.6.2 dev: true - /create-jest@29.7.0(@types/node@20.6.3)(ts-node@10.9.1): + /create-jest@29.7.0(@types/node@20.8.4)(ts-node@10.9.1): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -7772,7 +7930,7 @@ packages: chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.6.3)(ts-node@10.9.1) + jest-config: 29.7.0(@types/node@20.8.4)(ts-node@10.9.1) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -7838,7 +7996,7 @@ packages: postcss-modules-values: 4.0.0(postcss@8.4.31) postcss-value-parser: 4.2.0 semver: 7.5.4 - webpack: 5.88.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.88.2(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4) dev: true /css-select@4.3.0: @@ -8445,8 +8603,8 @@ packages: /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - /electron-to-chromium@1.4.548: - resolution: {integrity: sha512-R77KD6mXv37DOyKLN/eW1rGS61N6yHOfapNSX9w+y9DdPG83l9Gkuv7qkCFZ4Ta4JPhrjgQfYbv4Y3TnM1Hi2Q==} + /electron-to-chromium@1.4.551: + resolution: {integrity: sha512-/Ng/W/kFv7wdEHYzxdK7Cv0BHEGSkSB3M0Ssl8Ndr1eMiYeas/+Mv4cNaDqamqWx6nd2uQZfPz6g25z25M/sdw==} /elegant-spinner@1.0.1: resolution: {integrity: sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ==} @@ -8711,6 +8869,36 @@ packages: '@esbuild/win32-x64': 0.19.2 dev: true + /esbuild@0.19.4: + resolution: {integrity: sha512-x7jL0tbRRpv4QUyuDMjONtWFciygUxWaUM1kMX2zWxI0X2YWOt7MSA0g4UdeSiHM8fcYVzpQhKYOycZwxTdZkA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.19.4 + '@esbuild/android-arm64': 0.19.4 + '@esbuild/android-x64': 0.19.4 + '@esbuild/darwin-arm64': 0.19.4 + '@esbuild/darwin-x64': 0.19.4 + '@esbuild/freebsd-arm64': 0.19.4 + '@esbuild/freebsd-x64': 0.19.4 + '@esbuild/linux-arm': 0.19.4 + '@esbuild/linux-arm64': 0.19.4 + '@esbuild/linux-ia32': 0.19.4 + '@esbuild/linux-loong64': 0.19.4 + '@esbuild/linux-mips64el': 0.19.4 + '@esbuild/linux-ppc64': 0.19.4 + '@esbuild/linux-riscv64': 0.19.4 + '@esbuild/linux-s390x': 0.19.4 + '@esbuild/linux-x64': 0.19.4 + '@esbuild/netbsd-x64': 0.19.4 + '@esbuild/openbsd-x64': 0.19.4 + '@esbuild/sunos-x64': 0.19.4 + '@esbuild/win32-arm64': 0.19.4 + '@esbuild/win32-ia32': 0.19.4 + '@esbuild/win32-x64': 0.19.4 + dev: true + /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -8760,12 +8948,12 @@ packages: dependencies: debug: 3.2.7(supports-color@5.5.0) is-core-module: 2.13.0 - resolve: 1.22.6 + resolve: 1.22.8 transitivePeerDependencies: - supports-color dev: true - /eslint-mdx@2.2.0(eslint@8.49.0): + /eslint-mdx@2.2.0(eslint@8.51.0): resolution: {integrity: sha512-AriN6lCW6KhWQ9GEiXapR1DokKHefOUqKvCmHxnE9puCWYhWiycU2SNKH8jmrasDBreZ+RtJDLi+RcUNLJatjg==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} peerDependencies: @@ -8773,7 +8961,7 @@ packages: dependencies: acorn: 8.10.0 acorn-jsx: 5.3.2(acorn@8.10.0) - eslint: 8.49.0 + eslint: 8.51.0 espree: 9.6.1 estree-util-visit: 1.2.1 remark-mdx: 2.3.0 @@ -8790,7 +8978,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.2)(eslint-import-resolver-node@0.3.9)(eslint@8.49.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-node@0.3.9)(eslint@8.51.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -8811,15 +8999,15 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.7.2(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.7.5(eslint@8.51.0)(typescript@5.2.2) debug: 3.2.7(supports-color@5.5.0) - eslint: 8.49.0 + eslint: 8.51.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0): + /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.5)(eslint@8.51.0): resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==} engines: {node: '>=4'} peerDependencies: @@ -8829,16 +9017,16 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 6.7.2(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.7.5(eslint@8.51.0)(typescript@5.2.2) array-includes: 3.1.7 array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 debug: 3.2.7(supports-color@5.5.0) doctrine: 2.1.0 - eslint: 8.49.0 + eslint: 8.51.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.2)(eslint-import-resolver-node@0.3.9)(eslint@8.49.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-node@0.3.9)(eslint@8.51.0) has: 1.0.4 is-core-module: 2.13.0 is-glob: 4.0.3 @@ -8854,7 +9042,7 @@ packages: - supports-color dev: true - /eslint-plugin-jest@27.4.2(@typescript-eslint/eslint-plugin@6.7.5)(eslint@8.49.0)(jest@29.7.0)(typescript@5.2.2): + /eslint-plugin-jest@27.4.2(@typescript-eslint/eslint-plugin@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2): resolution: {integrity: sha512-3Nfvv3wbq2+PZlRTf2oaAWXWwbdBejFRBR2O8tAO67o+P8zno+QGbcDYaAXODlreXVg+9gvWhKKmG2rgfb8GEg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -8867,22 +9055,22 @@ packages: jest: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 6.7.5(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2) - '@typescript-eslint/utils': 5.62.0(eslint@8.49.0)(typescript@5.2.2) - eslint: 8.49.0 - jest: 29.7.0(@types/node@20.6.3)(ts-node@10.9.1) + '@typescript-eslint/eslint-plugin': 6.7.5(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(typescript@5.2.2) + '@typescript-eslint/utils': 5.62.0(eslint@8.51.0)(typescript@5.2.2) + eslint: 8.51.0 + jest: 29.7.0(@types/node@20.8.4)(ts-node@10.9.1) transitivePeerDependencies: - supports-color - typescript dev: true - /eslint-plugin-jsx-a11y@6.7.1(eslint@8.49.0): + /eslint-plugin-jsx-a11y@6.7.1(eslint@8.51.0): resolution: {integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==} engines: {node: '>=4.0'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.23.2 aria-query: 5.3.0 array-includes: 3.1.7 array.prototype.flatmap: 1.3.2 @@ -8891,7 +9079,7 @@ packages: axobject-query: 3.2.1 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 8.49.0 + eslint: 8.51.0 has: 1.0.4 jsx-ast-utils: 3.3.5 language-tags: 1.0.5 @@ -8901,27 +9089,27 @@ packages: semver: 6.3.1 dev: true - /eslint-plugin-markdown@3.0.1(eslint@8.49.0): + /eslint-plugin-markdown@3.0.1(eslint@8.51.0): resolution: {integrity: sha512-8rqoc148DWdGdmYF6WSQFT3uQ6PO7zXYgeBpHAOAakX/zpq+NvFYbDA/H7PYzHajwtmaOzAwfxyl++x0g1/N9A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - eslint: 8.49.0 + eslint: 8.51.0 mdast-util-from-markdown: 0.8.5 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-mdx@2.2.0(eslint@8.49.0): + /eslint-plugin-mdx@2.2.0(eslint@8.51.0): resolution: {integrity: sha512-OseoMXUIr8iy3E0me+wJLVAxuB0kxHP1plxuYAJDynzorzOj2OKv8Fhr+rIOJ32zfl3bnEWsqFnUiCnyznr1JQ==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} peerDependencies: eslint: '>=8.0.0' dependencies: - eslint: 8.49.0 - eslint-mdx: 2.2.0(eslint@8.49.0) - eslint-plugin-markdown: 3.0.1(eslint@8.49.0) + eslint: 8.51.0 + eslint-mdx: 2.2.0(eslint@8.51.0) + eslint-plugin-markdown: 3.0.1(eslint@8.51.0) remark-mdx: 2.3.0 remark-parse: 10.0.2 remark-stringify: 10.0.3 @@ -8932,16 +9120,16 @@ packages: - supports-color dev: true - /eslint-plugin-react-hooks@4.6.0(eslint@8.49.0): + /eslint-plugin-react-hooks@4.6.0(eslint@8.51.0): resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 dependencies: - eslint: 8.49.0 + eslint: 8.51.0 dev: true - /eslint-plugin-react@7.33.2(eslint@8.49.0): + /eslint-plugin-react@7.33.2(eslint@8.51.0): resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} engines: {node: '>=4'} peerDependencies: @@ -8952,7 +9140,7 @@ packages: array.prototype.tosorted: 1.1.2 doctrine: 2.1.0 es-iterator-helpers: 1.0.15 - eslint: 8.49.0 + eslint: 8.51.0 estraverse: 5.3.0 jsx-ast-utils: 3.3.5 minimatch: 3.1.2 @@ -8961,20 +9149,20 @@ packages: object.hasown: 1.1.3 object.values: 1.1.7 prop-types: 15.8.1 - resolve: 2.0.0-next.4 + resolve: 2.0.0-next.5 semver: 6.3.1 string.prototype.matchall: 4.0.10 dev: true - /eslint-plugin-storybook@0.6.15(eslint@8.49.0)(typescript@5.2.2): + /eslint-plugin-storybook@0.6.15(eslint@8.51.0)(typescript@5.2.2): resolution: {integrity: sha512-lAGqVAJGob47Griu29KXYowI4G7KwMoJDOkEip8ujikuDLxU+oWJ1l0WL6F2oDO4QiyUFXvtDkEkISMOPzo+7w==} engines: {node: 12.x || 14.x || >= 16} peerDependencies: eslint: '>=6' dependencies: '@storybook/csf': 0.0.1 - '@typescript-eslint/utils': 5.62.0(eslint@8.49.0)(typescript@5.2.2) - eslint: 8.49.0 + '@typescript-eslint/utils': 5.62.0(eslint@8.51.0)(typescript@5.2.2) + eslint: 8.51.0 requireindex: 1.2.0 ts-dedent: 2.2.0 transitivePeerDependencies: @@ -8982,14 +9170,14 @@ packages: - typescript dev: true - /eslint-plugin-testing-library@6.0.2(eslint@8.49.0)(typescript@5.2.2): + /eslint-plugin-testing-library@6.0.2(eslint@8.51.0)(typescript@5.2.2): resolution: {integrity: sha512-3BV6FWtLbpKFb4Y1obSdt8PC9xSqz6T+7EHB/6pSCXqVjKPoS67ck903feKMKQphd5VhrX+N51yHuVaPa7elsw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6'} peerDependencies: eslint: ^7.5.0 || ^8.0.0 dependencies: - '@typescript-eslint/utils': 5.62.0(eslint@8.49.0)(typescript@5.2.2) - eslint: 8.49.0 + '@typescript-eslint/utils': 5.62.0(eslint@8.51.0)(typescript@5.2.2) + eslint: 8.51.0 transitivePeerDependencies: - supports-color - typescript @@ -9015,15 +9203,15 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.49.0: - resolution: {integrity: sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==} + /eslint@8.51.0: + resolution: {integrity: sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) '@eslint-community/regexpp': 4.9.1 '@eslint/eslintrc': 2.1.2 - '@eslint/js': 8.49.0 + '@eslint/js': 8.51.0 '@humanwhocodes/config-array': 0.11.11 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 @@ -9897,12 +10085,6 @@ packages: get-intrinsic: 1.2.1 dev: true - /get-tsconfig@4.7.2: - resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} - dependencies: - resolve-pkg-maps: 1.0.0 - dev: true - /get-value@2.0.6: resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} engines: {node: '>=0.10.0'} @@ -10292,7 +10474,7 @@ packages: lodash: 4.17.21 pretty-error: 4.0.0 tapable: 2.2.1 - webpack: 5.88.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.88.2(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4) /htmlparser2@6.1.0: resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} @@ -10353,7 +10535,7 @@ packages: - supports-color dev: true - /http-proxy-middleware@2.0.6(@types/express@4.17.18): + /http-proxy-middleware@2.0.6(@types/express@4.17.19): resolution: {integrity: sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==} engines: {node: '>=12.0.0'} peerDependencies: @@ -10362,7 +10544,7 @@ packages: '@types/express': optional: true dependencies: - '@types/express': 4.17.18 + '@types/express': 4.17.19 '@types/http-proxy': 1.17.12 http-proxy: 1.18.1(debug@4.3.4) is-glob: 4.0.3 @@ -11270,7 +11452,7 @@ packages: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.6.3 + '@types/node': 20.8.4 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.1 @@ -11291,7 +11473,7 @@ packages: - supports-color dev: true - /jest-cli@29.7.0(@types/node@20.6.3)(ts-node@10.9.1): + /jest-cli@29.7.0(@types/node@20.8.4)(ts-node@10.9.1): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -11305,10 +11487,10 @@ packages: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.6.3)(ts-node@10.9.1) + create-jest: 29.7.0(@types/node@20.8.4)(ts-node@10.9.1) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.6.3)(ts-node@10.9.1) + jest-config: 29.7.0(@types/node@20.8.4)(ts-node@10.9.1) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -11319,7 +11501,7 @@ packages: - ts-node dev: true - /jest-config@29.7.0(@types/node@20.6.3)(ts-node@10.9.1): + /jest-config@29.7.0(@types/node@20.8.4)(ts-node@10.9.1): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -11334,7 +11516,7 @@ packages: '@babel/core': 7.23.0 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.6.3 + '@types/node': 20.8.4 babel-jest: 29.7.0(@babel/core@7.23.0) chalk: 4.1.2 ci-info: 3.9.0 @@ -11354,7 +11536,7 @@ packages: pretty-format: 29.7.0 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.1(@types/node@20.6.3)(typescript@5.2.2) + ts-node: 10.9.1(@types/node@20.8.4)(typescript@5.2.2) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -11401,7 +11583,7 @@ packages: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.1 - '@types/node': 20.6.3 + '@types/node': 20.8.4 jest-mock: 29.7.0 jest-util: 29.7.0 jsdom: 20.0.3 @@ -11418,7 +11600,7 @@ packages: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.6.3 + '@types/node': 20.8.4 jest-mock: 29.7.0 jest-util: 29.7.0 dev: true @@ -11439,7 +11621,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.7 - '@types/node': 20.6.3 + '@types/node': 20.8.4 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -11490,7 +11672,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.6.3 + '@types/node': 20.8.4 jest-util: 29.7.0 dev: true @@ -11531,7 +11713,7 @@ packages: jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) jest-util: 29.7.0 jest-validate: 29.7.0 - resolve: 1.22.6 + resolve: 1.22.8 resolve.exports: 2.0.2 slash: 3.0.0 dev: true @@ -11545,7 +11727,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.6.3 + '@types/node': 20.8.4 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -11576,7 +11758,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.6.3 + '@types/node': 20.8.4 chalk: 4.1.2 cjs-module-lexer: 1.2.3 collect-v8-coverage: 1.0.2 @@ -11628,7 +11810,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.6.3 + '@types/node': 20.8.4 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -11665,7 +11847,7 @@ packages: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.6.3 + '@types/node': 20.8.4 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -11677,7 +11859,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 20.6.3 + '@types/node': 20.8.4 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -11685,13 +11867,13 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 20.6.3 + '@types/node': 20.8.4 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest@29.7.0(@types/node@20.6.3)(ts-node@10.9.1): + /jest@29.7.0(@types/node@20.8.4)(ts-node@10.9.1): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -11704,7 +11886,7 @@ packages: '@jest/core': 29.7.0(ts-node@10.9.1) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.6.3)(ts-node@10.9.1) + jest-cli: 29.7.0(@types/node@20.8.4)(ts-node@10.9.1) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -12917,7 +13099,7 @@ packages: webpack: ^5.0.0 dependencies: schema-utils: 4.2.0 - webpack: 5.88.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.88.2(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4) dev: true /minimalistic-assert@1.0.1: @@ -13161,21 +13343,21 @@ packages: resolution: {integrity: sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==} dev: true - /netlify-cli@16.4.1(@types/node@20.6.3): - resolution: {integrity: sha512-HRigS1GNctNr7tV6VxedYWpaskP25+Xb4dEiMzCD9kuR1lQoo1wyjUJL9Pp7xtaQDiC1ddcBl5oOU5Hj+H2HOg==} + /netlify-cli@16.6.1(@types/node@20.8.4): + resolution: {integrity: sha512-XaJpLuEItcnSp90FWU1mbC1HThc1No+4crfE8xtNcKWP1x/JfjIJFcCug+7J5ouy5a/twYLAlPuKctB2Fyg4/g==} engines: {node: '>=16.16.0'} hasBin: true requiresBuild: true dependencies: '@bugsnag/js': 7.20.2 '@fastify/static': 6.10.2 - '@netlify/build': 29.21.1(@types/node@20.6.3)(debug@4.3.4) - '@netlify/build-info': 7.8.0 + '@netlify/build': 29.22.2(@types/node@20.8.4)(debug@4.3.4) + '@netlify/build-info': 7.10.1 '@netlify/config': 20.9.0 - '@netlify/edge-bundler': 8.19.1 + '@netlify/edge-bundler': 9.1.0 '@netlify/local-functions-proxy': 1.1.1 - '@netlify/serverless-functions-api': 1.7.3 - '@netlify/zip-it-and-ship-it': 9.18.1(supports-color@9.4.0) + '@netlify/serverless-functions-api': 1.8.0 + '@netlify/zip-it-and-ship-it': 9.23.0(supports-color@9.4.0) '@octokit/rest': 19.0.13 ansi-escapes: 6.2.0 ansi-styles: 6.2.1 @@ -13474,7 +13656,7 @@ packages: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: hosted-git-info: 2.8.9 - resolve: 1.22.6 + resolve: 1.22.8 semver: 5.7.2 validate-npm-package-license: 3.0.4 dev: true @@ -14198,7 +14380,7 @@ packages: dependencies: lilconfig: 2.1.0 postcss: 8.4.31 - ts-node: 10.9.1(@types/node@20.6.3)(typescript@5.2.2) + ts-node: 10.9.1(@types/node@20.8.4)(typescript@5.2.2) yaml: 2.3.2 dev: true @@ -14213,7 +14395,7 @@ packages: jiti: 1.20.0 postcss: 8.4.31 semver: 7.5.4 - webpack: 5.88.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.88.2(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - typescript dev: true @@ -14435,7 +14617,7 @@ packages: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 20.6.3 + '@types/node': 20.8.4 long: 5.2.3 dev: true @@ -14789,7 +14971,7 @@ packages: resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} engines: {node: '>= 10.13.0'} dependencies: - resolve: 1.22.6 + resolve: 1.22.8 /redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} @@ -14829,7 +15011,7 @@ packages: /regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.23.2 dev: true /regex-not@1.0.2: @@ -14956,7 +15138,7 @@ packages: dependencies: debug: 4.3.4(supports-color@9.4.0) module-details-from-path: 1.0.3 - resolve: 1.22.6 + resolve: 1.22.8 transitivePeerDependencies: - supports-color dev: true @@ -14996,10 +15178,6 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - /resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - dev: true - /resolve-url@0.2.1: resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==} deprecated: https://github.com/lydell/resolve-url#deprecated @@ -15010,16 +15188,16 @@ packages: engines: {node: '>=10'} dev: true - /resolve@1.22.6: - resolution: {integrity: sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==} + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true dependencies: is-core-module: 2.13.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - /resolve@2.0.0-next.4: - resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} + /resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true dependencies: is-core-module: 2.13.0 @@ -15927,7 +16105,7 @@ packages: peerDependencies: webpack: ^5.0.0 dependencies: - webpack: 5.88.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.88.2(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4) dev: true /sucrase@3.34.0: @@ -16002,14 +16180,14 @@ packages: picocolors: 1.0.0 dev: true - /swc-loader@0.2.3(@swc/core@1.3.86)(webpack@5.88.2): + /swc-loader@0.2.3(@swc/core@1.3.92)(webpack@5.88.2): resolution: {integrity: sha512-D1p6XXURfSPleZZA/Lipb3A8pZ17fP4NObZvFCDjK/OKljroqDpPmsBdTraWhVBqUNpcWBQY1imWdoPScRlQ7A==} peerDependencies: '@swc/core': ^1.2.147 webpack: '>=2' dependencies: - '@swc/core': 1.3.86(@swc/helpers@0.5.2) - webpack: 5.88.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4) + '@swc/core': 1.3.92(@swc/helpers@0.5.3) + webpack: 5.88.2(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4) dev: true /symbol-observable@1.2.0: @@ -16094,7 +16272,7 @@ packages: supports-hyperlinks: 2.3.0 dev: true - /terser-webpack-plugin@5.3.9(@swc/core@1.3.86)(esbuild@0.18.20)(webpack@5.88.2): + /terser-webpack-plugin@5.3.9(@swc/core@1.3.92)(esbuild@0.18.20)(webpack@5.88.2): resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -16111,15 +16289,15 @@ packages: optional: true dependencies: '@jridgewell/trace-mapping': 0.3.19 - '@swc/core': 1.3.86(@swc/helpers@0.5.2) + '@swc/core': 1.3.92(@swc/helpers@0.5.3) esbuild: 0.18.20 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.1 terser: 5.21.0 - webpack: 5.88.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack: 5.88.2(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4) - /terser-webpack-plugin@5.3.9(@swc/core@1.3.86)(webpack@5.88.2): + /terser-webpack-plugin@5.3.9(@swc/core@1.3.92)(webpack@5.88.2): resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -16136,12 +16314,12 @@ packages: optional: true dependencies: '@jridgewell/trace-mapping': 0.3.19 - '@swc/core': 1.3.86(@swc/helpers@0.5.2) + '@swc/core': 1.3.92(@swc/helpers@0.5.3) jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.1 terser: 5.21.0 - webpack: 5.88.2(@swc/core@1.3.86)(webpack-cli@5.1.4) + webpack: 5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4) /terser@5.21.0: resolution: {integrity: sha512-WtnFKrxu9kaoXuiZFSGrcAvvBqAdmKx0SFNmVNYdJamMu9yyN3I/QF0FbH4QcqJQ+y1CJnzxGIKH0cSj+FGYRw==} @@ -16437,7 +16615,7 @@ packages: bs-logger: 0.2.6 esbuild: 0.18.20 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.6.3)(ts-node@10.9.1) + jest: 29.7.0(@types/node@20.8.4)(ts-node@10.9.1) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -16447,7 +16625,7 @@ packages: yargs-parser: 21.1.1 dev: true - /ts-node@10.9.1(@types/node@20.6.3)(typescript@5.2.2): + /ts-node@10.9.1(@types/node@20.8.4)(typescript@5.2.2): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -16466,7 +16644,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.6.3 + '@types/node': 20.8.4 acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 @@ -16494,7 +16672,7 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - /tsup@7.2.0(@swc/core@1.3.86)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2): + /tsup@7.2.0(@swc/core@1.3.92)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2): resolution: {integrity: sha512-vDHlczXbgUvY3rWvqFEbSqmC1L7woozbzngMqTtL2PGBODTtWlRwGDDawhvWzr5c1QjKe4OAKqJGfE1xeXUvtQ==} engines: {node: '>=16.14'} hasBin: true @@ -16510,7 +16688,7 @@ packages: typescript: optional: true dependencies: - '@swc/core': 1.3.86(@swc/helpers@0.5.2) + '@swc/core': 1.3.92(@swc/helpers@0.5.3) bundle-require: 4.0.2(esbuild@0.18.20) cac: 6.7.14 chokidar: 3.5.3 @@ -16701,6 +16879,9 @@ packages: resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} dev: true + /undici-types@5.25.3: + resolution: {integrity: sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==} + /unicode-canonical-property-names-ecmascript@2.0.0: resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} engines: {node: '>=4'} @@ -16894,16 +17075,6 @@ packages: engines: {node: '>=8'} dev: true - /update-browserslist-db@1.0.13(browserslist@4.21.10): - resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - dependencies: - browserslist: 4.21.10 - escalade: 3.1.1 - picocolors: 1.0.0 - /update-browserslist-db@1.0.13(browserslist@4.22.1): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} hasBin: true @@ -16913,7 +17084,6 @@ packages: browserslist: 4.22.1 escalade: 3.1.1 picocolors: 1.0.0 - dev: true /update-notifier@6.0.2: resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} @@ -17181,7 +17351,7 @@ packages: import-local: 3.1.0 interpret: 3.1.1 rechoir: 0.8.0 - webpack: 5.88.2(@swc/core@1.3.86)(webpack-cli@5.1.4) + webpack: 5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4) webpack-dev-server: 4.15.1(webpack-cli@5.1.4)(webpack@5.88.2) webpack-merge: 5.9.0 @@ -17196,7 +17366,7 @@ packages: mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.2.0 - webpack: 5.88.2(@swc/core@1.3.86)(webpack-cli@5.1.4) + webpack: 5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4) /webpack-dev-server@4.15.1(webpack-cli@5.1.4)(webpack@5.88.2): resolution: {integrity: sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==} @@ -17213,11 +17383,11 @@ packages: dependencies: '@types/bonjour': 3.5.11 '@types/connect-history-api-fallback': 1.5.1 - '@types/express': 4.17.18 + '@types/express': 4.17.19 '@types/serve-index': 1.9.2 '@types/serve-static': 1.15.3 '@types/sockjs': 0.3.34 - '@types/ws': 8.5.6 + '@types/ws': 8.5.7 ansi-html-community: 0.0.8 bonjour-service: 1.1.1 chokidar: 3.5.3 @@ -17228,7 +17398,7 @@ packages: express: 4.18.2 graceful-fs: 4.2.11 html-entities: 2.4.0 - http-proxy-middleware: 2.0.6(@types/express@4.17.18) + http-proxy-middleware: 2.0.6(@types/express@4.17.19) ipaddr.js: 2.1.0 launch-editor: 2.6.1 open: 8.4.2 @@ -17239,7 +17409,7 @@ packages: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack: 5.88.2(@swc/core@1.3.86)(webpack-cli@5.1.4) + webpack: 5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack-dev-server@4.15.1)(webpack@5.88.2) webpack-dev-middleware: 5.3.3(webpack@5.88.2) ws: 8.14.2 @@ -17260,7 +17430,7 @@ packages: resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} engines: {node: '>=10.13.0'} - /webpack@5.88.2(@swc/core@1.3.86)(esbuild@0.18.20)(webpack-cli@5.1.4): + /webpack@5.88.2(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4): resolution: {integrity: sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==} engines: {node: '>=10.13.0'} hasBin: true @@ -17277,7 +17447,7 @@ packages: '@webassemblyjs/wasm-parser': 1.11.6 acorn: 8.10.0 acorn-import-assertions: 1.9.0(acorn@8.10.0) - browserslist: 4.21.10 + browserslist: 4.22.1 chrome-trace-event: 1.0.3 enhanced-resolve: 5.15.0 es-module-lexer: 1.3.1 @@ -17291,7 +17461,7 @@ packages: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.9(@swc/core@1.3.86)(esbuild@0.18.20)(webpack@5.88.2) + terser-webpack-plugin: 5.3.9(@swc/core@1.3.92)(esbuild@0.18.20)(webpack@5.88.2) watchpack: 2.4.0 webpack-cli: 5.1.4(webpack-dev-server@4.15.1)(webpack@5.88.2) webpack-sources: 3.2.3 @@ -17300,7 +17470,7 @@ packages: - esbuild - uglify-js - /webpack@5.88.2(@swc/core@1.3.86)(webpack-cli@5.1.4): + /webpack@5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4): resolution: {integrity: sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==} engines: {node: '>=10.13.0'} hasBin: true @@ -17317,7 +17487,7 @@ packages: '@webassemblyjs/wasm-parser': 1.11.6 acorn: 8.10.0 acorn-import-assertions: 1.9.0(acorn@8.10.0) - browserslist: 4.21.10 + browserslist: 4.22.1 chrome-trace-event: 1.0.3 enhanced-resolve: 5.15.0 es-module-lexer: 1.3.1 @@ -17331,7 +17501,7 @@ packages: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.9(@swc/core@1.3.86)(webpack@5.88.2) + terser-webpack-plugin: 5.3.9(@swc/core@1.3.92)(webpack@5.88.2) watchpack: 2.4.0 webpack-cli: 5.1.4(webpack-dev-server@4.15.1)(webpack@5.88.2) webpack-sources: 3.2.3 diff --git a/sample/another-remote-module/package.json b/sample/another-remote-module/package.json index 3ecd9b5e9..a4251f520 100644 --- a/sample/another-remote-module/package.json +++ b/sample/another-remote-module/package.json @@ -14,19 +14,19 @@ "serve-build": "pnpm build && pnpm http-server dist -p 8082 -P http://localhost:8082? -c-1" }, "devDependencies": { - "@swc/core": "1.3.86", - "@swc/helpers": "0.5.2", - "@tanstack/react-query-devtools": "rc", - "@types/react": "18.2.22", - "@types/react-dom": "18.2.7", - "@types/webpack": "5.28.2", + "@swc/core": "1.3.92", + "@swc/helpers": "0.5.3", + "@squide/fakes": "workspace:*", + "@types/react": "18.2.28", + "@types/react-dom": "18.2.13", + "@types/webpack": "5.28.3", "@workleap/browserslist-config": "2.0.0", - "@workleap/eslint-plugin": "2.1.1", + "@workleap/eslint-plugin": "3.0.0", "@workleap/swc-configs": "2.1.2", "@workleap/typescript-configs": "3.0.2", - "@workleap/webpack-configs": "1.0.8", + "@workleap/webpack-configs": "1.1.0", "axios": "1.5.1", - "browserslist": "4.21.10", + "browserslist": "4.22.1", "cross-env": "7.0.3", "http-server": "14.1.1", "typescript": "5.2.2", @@ -37,10 +37,8 @@ "dependencies": { "@sample/shared": "workspace:*", "@sample/shell": "workspace:*", - "@squide/fakes": "workspace:*", "@squide/react-router": "workspace:*", "@squide/webpack-module-federation": "workspace:*", - "@tanstack/react-query": "rc", "react": "18.2.0", "react-dom": "18.2.0", "react-router-dom": "6.16.0" diff --git a/sample/another-remote-module/webpack.build.js b/sample/another-remote-module/webpack.build.js index 7f80523e9..901112abf 100644 --- a/sample/another-remote-module/webpack.build.js +++ b/sample/another-remote-module/webpack.build.js @@ -8,9 +8,6 @@ const publicPath = process.env.NETLIFY === "true" ? "https://squide-another-remo export default defineBuildRemoteModuleConfig(swcConfig, "remote2", publicPath, { sharedDependencies: { - "@tanstack/react-query": { - singleton: true - }, "@sample/shared": { singleton: true } diff --git a/sample/another-remote-module/webpack.dev.js b/sample/another-remote-module/webpack.dev.js index bc83b72cc..418be45e6 100644 --- a/sample/another-remote-module/webpack.dev.js +++ b/sample/another-remote-module/webpack.dev.js @@ -10,9 +10,6 @@ let config; if (!process.env.LOCAL) { config = defineDevRemoteModuleConfig(swcConfig, "remote2", 8082, { sharedDependencies: { - "@tanstack/react-query": { - singleton: true - }, "@sample/shared": { singleton: true } @@ -23,7 +20,8 @@ if (!process.env.LOCAL) { }); } else { config = defineDevConfig(swcConfig, { - entry: path.resolve("./src/dev/index.tsx") + entry: path.resolve("./src/dev/index.tsx"), + overlay: false }); } diff --git a/sample/host/package.json b/sample/host/package.json index 62e0476d4..507ae8fa2 100644 --- a/sample/host/package.json +++ b/sample/host/package.json @@ -15,17 +15,18 @@ "serve-build": "pnpm build && pnpm http-server dist -p 8080 -P http://localhost:8080? -c-1" }, "devDependencies": { - "@swc/core": "1.3.86", - "@swc/helpers": "0.5.2", - "@types/react": "18.2.22", - "@types/react-dom": "18.2.7", - "@types/webpack": "5.28.2", + "@swc/core": "1.3.92", + "@swc/helpers": "0.5.3", + "@squide/fakes": "workspace:*", + "@types/react": "18.2.28", + "@types/react-dom": "18.2.13", + "@types/webpack": "5.28.3", "@workleap/browserslist-config": "2.0.0", - "@workleap/eslint-plugin": "2.1.1", + "@workleap/eslint-plugin": "3.0.0", "@workleap/swc-configs": "2.1.2", "@workleap/typescript-configs": "3.0.2", - "@workleap/webpack-configs": "1.0.8", - "browserslist": "4.21.10", + "@workleap/webpack-configs": "1.1.0", + "browserslist": "4.22.1", "copyfiles": "2.4.1", "cross-env": "7.0.3", "http-server": "14.1.1", @@ -40,9 +41,8 @@ "@sample/local-module": "workspace:*", "@sample/shared": "workspace:*", "@sample/shell": "workspace:*", - "@squide/fakes": "workspace:*", - "@squide/react-router": "workspace:*", "@squide/msw": "workspace:*", + "@squide/react-router": "workspace:*", "@squide/webpack-module-federation": "workspace:*", "axios": "1.5.1", "react": "18.2.0", diff --git a/sample/host/webpack.build.js b/sample/host/webpack.build.js index b2ab59699..87916cb96 100644 --- a/sample/host/webpack.build.js +++ b/sample/host/webpack.build.js @@ -8,10 +8,6 @@ const publicPath = process.env.NETLIFY === "true" ? "https://squide-host.netlify export default defineBuildHostConfig(swcConfig, "host", publicPath, { sharedDependencies: { - "@tanstack/react-query": { - singleton: true, - eager: true - }, "@sample/shared": { singleton: true, eager: true diff --git a/sample/host/webpack.dev.js b/sample/host/webpack.dev.js index b83721ab0..ce00b5e19 100644 --- a/sample/host/webpack.dev.js +++ b/sample/host/webpack.dev.js @@ -4,11 +4,8 @@ import { defineDevHostConfig } from "@squide/webpack-module-federation/defineCon import { swcConfig } from "./swc.dev.js"; export default defineDevHostConfig(swcConfig, "host", 8080, { + overlay: false, sharedDependencies: { - "@tanstack/react-query": { - singleton: true, - eager: true - }, "@sample/shared": { singleton: true, eager: true diff --git a/sample/local-module/package.json b/sample/local-module/package.json index 434ed44b1..48e92dba2 100644 --- a/sample/local-module/package.json +++ b/sample/local-module/package.json @@ -26,19 +26,20 @@ "react-router-dom": "*" }, "devDependencies": { - "@swc/core": "1.3.86", - "@swc/helpers": "0.5.2", - "@types/react": "18.2.22", - "@types/react-dom": "18.2.7", - "@types/webpack": "5.28.2", + "@swc/core": "1.3.92", + "@swc/helpers": "0.5.3", + "@squide/fakes": "workspace:*", + "@types/react": "18.2.28", + "@types/react-dom": "18.2.13", + "@types/webpack": "5.28.3", "@workleap/browserslist-config": "2.0.0", - "@workleap/eslint-plugin": "2.1.1", + "@workleap/eslint-plugin": "3.0.0", "@workleap/swc-configs": "2.1.2", "@workleap/tsup-configs": "3.0.1", "@workleap/typescript-configs": "3.0.2", - "@workleap/webpack-configs": "1.0.8", + "@workleap/webpack-configs": "1.1.0", "axios": "1.5.1", - "browserslist": "4.21.10", + "browserslist": "4.22.1", "cross-env": "7.0.3", "nodemon": "3.0.1", "tsup": "7.2.0", @@ -50,7 +51,6 @@ "dependencies": { "@sample/shared": "workspace:*", "@sample/shell": "workspace:*", - "@squide/fakes": "workspace:*", "@squide/react-router": "workspace:*", "react": "18.2.0", "react-dom": "18.2.0", diff --git a/sample/local-module/webpack.config.js b/sample/local-module/webpack.config.js index 442f96b30..d830cda47 100644 --- a/sample/local-module/webpack.config.js +++ b/sample/local-module/webpack.config.js @@ -5,6 +5,7 @@ import path from "node:path"; import { swcConfig } from "./swc.config.js"; export default defineDevConfig(swcConfig, { - entry: path.resolve("./src/dev/index.tsx") + entry: path.resolve("./src/dev/index.tsx"), + overlay: false }); diff --git a/sample/remote-module/mocks/handlers.ts b/sample/remote-module/mocks/handlers.ts index c24c83720..aa2bdb0ed 100644 --- a/sample/remote-module/mocks/handlers.ts +++ b/sample/remote-module/mocks/handlers.ts @@ -2,16 +2,11 @@ import { rest, type RestHandler } from "msw"; export const requestHandlers: RestHandler[] = [ rest.get("https://rickandmortyapi.com/api/character/1,2,3,4,5", async (req, res, ctx) => { - // return res( - // ctx.status(401) - // ); - return res( ctx.status(200), ctx.json([{ "id": 1, - // "name": "Rick Sanchez", - "name": "IT'S WORKING", + "name": "DATA FROM MSW", "status": "Alive", "species": "Human", "type": "", diff --git a/sample/remote-module/package.json b/sample/remote-module/package.json index eca2659d4..135c12950 100644 --- a/sample/remote-module/package.json +++ b/sample/remote-module/package.json @@ -15,19 +15,20 @@ "serve-build": "pnpm build && pnpm http-server dist -p 8081 -P http://localhost:8081? -c-1" }, "devDependencies": { - "@swc/core": "1.3.86", - "@swc/helpers": "0.5.2", + "@swc/core": "1.3.92", + "@swc/helpers": "0.5.3", + "@squide/fakes": "workspace:*", "@tanstack/react-query-devtools": "rc", - "@types/react": "18.2.22", - "@types/react-dom": "18.2.7", - "@types/webpack": "5.28.2", + "@types/react": "18.2.28", + "@types/react-dom": "18.2.13", + "@types/webpack": "5.28.3", "@workleap/browserslist-config": "2.0.0", - "@workleap/eslint-plugin": "2.1.1", + "@workleap/eslint-plugin": "3.0.0", "@workleap/swc-configs": "2.1.2", "@workleap/typescript-configs": "3.0.2", - "@workleap/webpack-configs": "1.0.8", + "@workleap/webpack-configs": "1.1.0", "axios": "1.5.1", - "browserslist": "4.21.10", + "browserslist": "4.22.1", "cross-env": "7.0.3", "http-server": "14.1.1", "msw": "1.3.2", @@ -40,7 +41,6 @@ "dependencies": { "@sample/shared": "workspace:*", "@sample/shell": "workspace:*", - "@squide/fakes": "workspace:*", "@squide/msw": "workspace:*", "@squide/react-router": "workspace:*", "@squide/webpack-module-federation": "workspace:*", diff --git a/sample/remote-module/webpack.build.js b/sample/remote-module/webpack.build.js index 84e40ed0a..9197cf8ab 100644 --- a/sample/remote-module/webpack.build.js +++ b/sample/remote-module/webpack.build.js @@ -8,9 +8,6 @@ const publicPath = process.env.NETLIFY === "true" ? "https://squide-remote-modul export default defineBuildRemoteModuleConfig(swcConfig, "remote1", publicPath, { sharedDependencies: { - "@tanstack/react-query": { - singleton: true - }, "@sample/shared": { singleton: true } diff --git a/sample/remote-module/webpack.dev.js b/sample/remote-module/webpack.dev.js index 440851bca..684e7cbe4 100644 --- a/sample/remote-module/webpack.dev.js +++ b/sample/remote-module/webpack.dev.js @@ -10,9 +10,6 @@ let config; if (!process.env.LOCAL) { config = defineDevRemoteModuleConfig(swcConfig, "remote1", 8081, { sharedDependencies: { - "@tanstack/react-query": { - singleton: true - }, "@sample/shared": { singleton: true } @@ -25,6 +22,7 @@ if (!process.env.LOCAL) { } else { config = defineDevConfig(swcConfig, { entry: path.resolve("./src/dev/index.tsx"), + overlay: false, environmentVariables: { "USE_MSW": process.env.USE_MSW === "true" } diff --git a/sample/shared/package.json b/sample/shared/package.json index e41d19ca7..8ea2c8d71 100644 --- a/sample/shared/package.json +++ b/sample/shared/package.json @@ -20,6 +20,7 @@ }, "scripts": { "dev": "nodemon", + "dev-msw": "pnpm nodemon", "build": "tsup --config ./tsup.build.ts", "serve-build": "pnpm build" }, @@ -32,9 +33,9 @@ "devDependencies": { "@remix-run/router": "1.9.0", "@squide/react-router": "workspace:*", - "@types/react": "18.2.22", - "@types/react-dom": "18.2.7", - "@workleap/eslint-plugin": "2.1.1", + "@types/react": "18.2.28", + "@types/react-dom": "18.2.13", + "@workleap/eslint-plugin": "3.0.0", "@workleap/tsup-configs": "3.0.1", "@workleap/typescript-configs": "3.0.2", "nodemon": "3.0.1", diff --git a/sample/shared/src/index.ts b/sample/shared/src/index.ts index c487c2f74..65bf5bc34 100644 --- a/sample/shared/src/index.ts +++ b/sample/shared/src/index.ts @@ -3,4 +3,5 @@ export * from "./appContext.ts"; export * from "./eventBus.ts"; export * from "./isNetlify.ts"; export * from "./session.ts"; +export * from "./subscription.ts"; diff --git a/sample/shared/src/session.ts b/sample/shared/src/session.ts index 8c3f4633c..0f33aff7d 100644 --- a/sample/shared/src/session.ts +++ b/sample/shared/src/session.ts @@ -1,5 +1,6 @@ export interface Session { user: { + id: number; name: string; }; } diff --git a/sample/shared/src/subscription.ts b/sample/shared/src/subscription.ts new file mode 100644 index 000000000..07fea2c00 --- /dev/null +++ b/sample/shared/src/subscription.ts @@ -0,0 +1,30 @@ +import { createContext, useContext } from "react"; + +export type SubscriptionStatus = "unknown" | "trial" | "paid" | "not-paid"; + +export interface Subscription { + status: SubscriptionStatus; +} + +export const SubscriptionContext = createContext(undefined); + +export function useSubscription() { + return useContext(SubscriptionContext); +} + +export function toSubscriptionStatusLabel(status?: SubscriptionStatus) { + if (!status) { + return "-"; + } + + switch (status) { + case "unknown": + return "-"; + case "trial": + return "Trial"; + case "paid": + return "Paid"; + case "not-paid": + return "Not paid"; + } +} diff --git a/sample/shell/mocks/authenticationHandlers.ts b/sample/shell/mocks/authenticationHandlers.ts index c53547b8f..2721332a9 100644 --- a/sample/shell/mocks/authenticationHandlers.ts +++ b/sample/shell/mocks/authenticationHandlers.ts @@ -1,6 +1,5 @@ import { rest, type RestHandler } from "msw"; - -const SessionKey = "msw-session"; +import { sessionManager } from "./session.ts"; function simulateDelay(delay: number) { return new Promise(resolve => { @@ -22,9 +21,10 @@ export const authenticationHandlers: RestHandler[] = [ await simulateDelay(2000); - window.localStorage.setItem(SessionKey, JSON.stringify({ + sessionManager.setSession({ + userId: Math.random(), username - })); + }); return res( ctx.status(200) @@ -32,7 +32,7 @@ export const authenticationHandlers: RestHandler[] = [ }), rest.post("/logout", async (req, res, ctx) => { - window.localStorage.removeItem(SessionKey); + sessionManager.clearSession(); return res( ctx.status(200) @@ -40,7 +40,7 @@ export const authenticationHandlers: RestHandler[] = [ }), rest.get("/session", async (req, res, ctx) => { - const session = window.localStorage.getItem(SessionKey); + const session = sessionManager.getSession(); if (!session) { return res( @@ -52,7 +52,7 @@ export const authenticationHandlers: RestHandler[] = [ return res( ctx.status(200), - ctx.json(JSON.parse(session)) + ctx.json(session) ); }) ]; diff --git a/sample/shell/mocks/session.ts b/sample/shell/mocks/session.ts new file mode 100644 index 000000000..41352d673 --- /dev/null +++ b/sample/shell/mocks/session.ts @@ -0,0 +1,8 @@ +import { LocalStorageSessionManager } from "@squide/fakes"; + +export interface Session { + userId: number; + username: string; +} + +export const sessionManager = new LocalStorageSessionManager({ key: "squide-sample-msw-session" }); diff --git a/sample/shell/mocks/subscriptionHandlers.ts b/sample/shell/mocks/subscriptionHandlers.ts new file mode 100644 index 000000000..bc864c9a5 --- /dev/null +++ b/sample/shell/mocks/subscriptionHandlers.ts @@ -0,0 +1,19 @@ +import { rest, type RestHandler } from "msw"; +import { sessionManager } from "./session.ts"; + +export const subscriptionHandlers: RestHandler[] = [ + rest.get("/subscription", async (req, res, ctx) => { + if (!sessionManager.getSession()) { + return res( + ctx.status(401) + ); + } + + return res( + ctx.status(200), + ctx.json({ + status: "paid" + }) + ); + }) +]; diff --git a/sample/shell/package.json b/sample/shell/package.json index a38561975..b7ea2fb79 100644 --- a/sample/shell/package.json +++ b/sample/shell/package.json @@ -21,8 +21,9 @@ }, "peerDependencies": { "@sample/shared": "*", - "@squide/react-router": "*", + "@squide/fakes": "*", "@squide/msw": "*", + "@squide/react-router": "*", "@squide/webpack-module-federation": "*", "axios": "*", "msw": "*", @@ -33,12 +34,13 @@ "devDependencies": { "@remix-run/router": "1.9.0", "@sample/shared": "workspace:*", - "@squide/react-router": "workspace:*", + "@squide/fakes": "workspace:*", "@squide/msw": "workspace:*", + "@squide/react-router": "workspace:*", "@squide/webpack-module-federation": "workspace:*", - "@types/react": "18.2.22", - "@types/react-dom": "18.2.7", - "@workleap/eslint-plugin": "2.1.1", + "@types/react": "18.2.28", + "@types/react-dom": "18.2.13", + "@workleap/eslint-plugin": "3.0.0", "@workleap/tsup-configs": "3.0.1", "@workleap/typescript-configs": "3.0.2", "axios": "1.5.1", diff --git a/sample/shell/src/AuthenticatedLayout.tsx b/sample/shell/src/AuthenticatedLayout.tsx index 5998aa89b..c6a1c9d75 100644 --- a/sample/shell/src/AuthenticatedLayout.tsx +++ b/sample/shell/src/AuthenticatedLayout.tsx @@ -1,4 +1,4 @@ -import { useApplicationEventBusListener, type Session, type SessionManager } from "@sample/shared"; +import { toSubscriptionStatusLabel, useApplicationEventBusListener, useSubscription, type Session, type SessionManager } from "@sample/shared"; import { isNavigationLink, useLogger, useNavigationItems, useRenderedNavigationItems, useSession, type NavigationLinkRenderProps, type NavigationSectionRenderProps, type RenderItemFunction, type RenderSectionFunction } from "@squide/react-router"; import axios from "axios"; import { Suspense, useCallback, type MouseEvent, type ReactNode } from "react"; @@ -47,7 +47,9 @@ export interface AuthenticatedLayoutProps { export function AuthenticatedLayout({ sessionManager }: AuthenticatedLayoutProps) { const logger = useLogger(); + const session = useSession() as Session; + const subscription = useSubscription(); const navigate = useNavigate(); @@ -84,7 +86,7 @@ export function AuthenticatedLayout({ sessionManager }: AuthenticatedLayoutProps
{/* Must check for a null session because when the disconnect button is clicked, it will clear the session and rerender this layout. */} - (User: {session?.user?.name}) + (Subscription: {toSubscriptionStatusLabel(subscription?.status)}-User: {session?.user?.name})
diff --git a/sample/shell/src/ModuleErrorBoundary.tsx b/sample/shell/src/ModuleErrorBoundary.tsx index 3076f4fa3..a593fe37c 100644 --- a/sample/shell/src/ModuleErrorBoundary.tsx +++ b/sample/shell/src/ModuleErrorBoundary.tsx @@ -28,9 +28,8 @@ export function ModuleErrorBoundary() {

Unmanaged error

An unmanaged error occurred inside a module. Still, other parts of the application are fully functional!

👉 {getErrorMessage(error)} -
- -
+

+
); } diff --git a/sample/shell/src/RootErrorBoundary.tsx b/sample/shell/src/RootErrorBoundary.tsx index ebaecb73c..c3c995fd8 100644 --- a/sample/shell/src/RootErrorBoundary.tsx +++ b/sample/shell/src/RootErrorBoundary.tsx @@ -28,9 +28,8 @@ export function RootErrorBoundary() {

Unmanaged error

An unmanaged error occurred and the application is broken, try refreshing your browser.

👉 {getErrorMessage(error)} -
- -
+

+
); } diff --git a/sample/shell/src/register.tsx b/sample/shell/src/register.tsx index d6ee55d22..40d28644b 100644 --- a/sample/shell/src/register.tsx +++ b/sample/shell/src/register.tsx @@ -3,17 +3,10 @@ import { getMswPlugin } from "@squide/msw"; import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { ManagedRoutes } from "@squide/react-router"; import { authenticationHandlers } from "../mocks/authenticationHandlers.ts"; +import { subscriptionHandlers } from "../mocks/subscriptionHandlers.ts"; import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; import { RootLayout } from "./RootLayout.tsx"; -// async function lazyPage(componentPath: string, props: object = {}) { -// const { Component } = await import(`${componentPath}`); - -// return { -// element: -// }; -// } - function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { runtime.registerRoute({ // Pathless route to declare a root layout and a root error boundary. @@ -28,7 +21,6 @@ function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { children: [ { // Pathless route to declare an authenticated boundary. - // lazy: () => lazyElement("./AuthenticationBoundary.tsx"), lazy: async () => { const { AuthenticationBoundary } = await import("./AuthenticationBoundary.tsx"); @@ -39,7 +31,6 @@ function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { children: [ { // Pathless route to declare an authenticated layout. - // lazy: () => lazyPage("./AuthenticatedLayout.tsx", { sessionManager }), lazy: async () => { const { AuthenticatedLayout } = await import("./AuthenticatedLayout.tsx"); @@ -119,7 +110,10 @@ function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { function registerMsw(runtime: Runtime) { const mswPlugin = getMswPlugin(runtime); - mswPlugin.registerRequestHandlers(authenticationHandlers); + mswPlugin.registerRequestHandlers([ + ...authenticationHandlers, + ...subscriptionHandlers + ]); } export function registerShell(sessionManager: SessionManager) { diff --git a/sample/shell/src/useAppRouter.tsx b/sample/shell/src/useAppRouter.tsx index 5deab9621..81ed866c2 100644 --- a/sample/shell/src/useAppRouter.tsx +++ b/sample/shell/src/useAppRouter.tsx @@ -1,4 +1,5 @@ -import type { Session, SessionManager } from "@sample/shared"; +import type { Session, SessionManager, Subscription } from "@sample/shared"; +import { SubscriptionContext } from "@sample/shared"; import { useIsMswStarted } from "@squide/msw"; import { useIsMatchingRouteProtected, useLogger, useRoutes } from "@squide/react-router"; import { useAreModulesReady } from "@squide/webpack-module-federation"; @@ -9,6 +10,10 @@ import { RouterProvider, createBrowserRouter } from "react-router-dom"; export function useAppRouter(waitForMsw: boolean, sessionManager: SessionManager) { const [isReady, setIsReady] = useState(false); + // Could have been done with a ref (https://react.dev/reference/react/useRef) to save a re-render but for this sample + // it seemed unnecessary. If your app have multiple global data structures like this one thought, it should be considered. + const [subscription, setSubscription] = useState(); + const logger = useLogger(); const routes = useRoutes(); @@ -29,10 +34,11 @@ export function useAppRouter(waitForMsw: boolean, sessionManager: SessionManager if (isActiveRouteProtected) { logger.debug(`[shell] Fetching session data as "${window.location}" is a protected route.`); - axios.get("/session") + const sessionPromise = axios.get("/session") .then(({ data }) => { const session: Session = { user: { + id: data.userId, name: data.username } }; @@ -40,18 +46,30 @@ export function useAppRouter(waitForMsw: boolean, sessionManager: SessionManager logger.debug("[shell] %cSession is ready%c:", "color: white; background-color: green;", "", session); sessionManager.setSession(session); + }); - setIsReady(true); - }) - .catch((error: unknown) => { - setIsReady(true); + const subscriptionPromise = axios.get("/subscription") + .then(({ data }) => { + const _subscription: Subscription = { + status: data.status + }; + + logger.debug("[shell] %cSubscription is ready%c:", "color: white; background-color: green;", "", _subscription); + + setSubscription(_subscription); + }); + Promise.all([sessionPromise, subscriptionPromise]) + .catch((error: unknown) => { if (axios.isAxiosError(error) && error.response?.status === 401) { // The authentication boundary will redirect to the login page. return; } throw error; + }) + .finally(() => { + setIsReady(true); }); } else { logger.debug(`[shell] Passing through as "${window.location}" is a public route.`); @@ -70,9 +88,11 @@ export function useAppRouter(waitForMsw: boolean, sessionManager: SessionManager } return ( - + + + ); } From 567a7c8fefc331b017ef1c6c92985df1f81528d2 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Thu, 12 Oct 2023 11:57:38 -0400 Subject: [PATCH 16/51] Prefixed route and nav items custom props with $ --- .../src/navigationItemRegistry.ts | 10 +- packages/react-router/src/outlets.ts | 4 +- packages/react-router/src/routeRegistry.ts | 18 +-- packages/react-router/src/runtime.ts | 4 +- packages/react-router/src/useMatchingRoute.ts | 2 +- .../src/useRenderedNavigationItems.tsx | 18 +-- .../react-router/tests/routeRegistry.test.tsx | 14 +- packages/react-router/tests/runtime.test.tsx | 146 +++++++++--------- .../tests/useNavigationItems.test.tsx | 48 +++--- .../tests/useRenderedNavigationItems.test.tsx | 106 ++++++------- sample/another-remote-module/src/register.tsx | 2 +- sample/local-module/src/dev/register.tsx | 4 +- sample/local-module/src/register.tsx | 18 +-- sample/remote-module/src/Fetch.tsx | 2 +- sample/remote-module/src/dev/register.tsx | 4 +- sample/remote-module/src/register.tsx | 36 ++--- sample/shell/src/register.tsx | 12 +- 17 files changed, 229 insertions(+), 219 deletions(-) diff --git a/packages/react-router/src/navigationItemRegistry.ts b/packages/react-router/src/navigationItemRegistry.ts index 7703488e5..330b67b2e 100644 --- a/packages/react-router/src/navigationItemRegistry.ts +++ b/packages/react-router/src/navigationItemRegistry.ts @@ -3,15 +3,15 @@ import type { ReactNode } from "react"; import type { LinkProps } from "react-router-dom"; export interface NavigationLink extends Omit { - label: ReactNode; - additionalProps?: Record; + $label: ReactNode; + $additionalProps?: Record; children?: never; } export interface NavigationSection { - label: ReactNode; + $label: ReactNode; + $additionalProps?: Record; children: NavigationItem[]; - additionalProps?: Record; to?: never; } @@ -24,7 +24,7 @@ export function isLinkItem(item: NavigationItem): item is NavigationLink { export type RootNavigationItem = NavigationItem & { // Highest priority is rendered first. - priority?: number; + $priority?: number; }; export class NavigationItemRegistry { diff --git a/packages/react-router/src/outlets.ts b/packages/react-router/src/outlets.ts index 49cb8941d..c6631f225 100644 --- a/packages/react-router/src/outlets.ts +++ b/packages/react-router/src/outlets.ts @@ -3,9 +3,9 @@ import type { Route } from "./routeRegistry.ts"; export const ManagedRoutesOutletName = "__squide-managed-routes-outlet__"; export const ManagedRoutes: Route = { - name: ManagedRoutesOutletName + $name: ManagedRoutesOutletName }; export function isManagedRoutesOutletRoute(route: Route) { - return route.name === ManagedRoutesOutletName; + return route.$name === ManagedRoutesOutletName; } diff --git a/packages/react-router/src/routeRegistry.ts b/packages/react-router/src/routeRegistry.ts index 1414769d1..9baedd18c 100644 --- a/packages/react-router/src/routeRegistry.ts +++ b/packages/react-router/src/routeRegistry.ts @@ -5,13 +5,13 @@ import { ManagedRoutesOutletName, isManagedRoutesOutletRoute } from "./outlets.t export type RouteVisibility = "public" | "protected"; export interface IndexRoute extends IndexRouteObject { - name?: string; - visibility?: RouteVisibility; + $name?: string; + $visibility?: RouteVisibility; } export interface NonIndexRoute extends Omit { - name?: string; - visibility?: RouteVisibility; + $name?: string; + $visibility?: RouteVisibility; children?: Route[]; } @@ -32,8 +32,8 @@ export function createIndexKey(route: Route) { return normalizePath(route.path); } - if (route.name) { - return route.name; + if (route.$name) { + return route.$name; } return undefined; @@ -77,7 +77,7 @@ export class RouteRegistry { // Creates a copy of the route object and add the default properties. const route = { ...x, - visibility: x.visibility ?? "protected" + $visibility: x.$visibility ?? "protected" }; if (route.children) { @@ -130,11 +130,11 @@ export class RouteRegistry { #validateRouteRegistrationOptions(route: Route, { hoist, parentPath, parentName }: RegisterRouteOptions = {}) { if (hoist && parentPath) { - throw new Error(`[squide] A route cannot have the "hoist" property when a "publicPath" option is provided. Route id: "${route.path ?? route.name ?? "(no identifier)"}".`); + throw new Error(`[squide] A route cannot have the "hoist" property when a "publicPath" option is provided. Route id: "${route.path ?? route.$name ?? "(no identifier)"}".`); } if (hoist && parentName) { - throw new Error(`[squide] A route cannot have the "hoist" property when a "parentName" option is provided. Route id: "${route.path ?? route.name ?? "(no identifier)"}".`); + throw new Error(`[squide] A route cannot have the "hoist" property when a "parentName" option is provided. Route id: "${route.path ?? route.$name ?? "(no identifier)"}".`); } } diff --git a/packages/react-router/src/runtime.ts b/packages/react-router/src/runtime.ts index 5185594fe..35ff5218b 100644 --- a/packages/react-router/src/runtime.ts +++ b/packages/react-router/src/runtime.ts @@ -62,7 +62,7 @@ export class Runtime extends AbstractRuntime { const pendingRegistrations = this.#routeRegistry.pendingRegistrations; if (pendingRegistrations.size > 0) { - if (pendingRegistrations.has(ManagedRoutes.name!)) { + if (pendingRegistrations.has(ManagedRoutes.$name!)) { // eslint-disable-next-line max-len throw new Error("[squide] The \"ManagedRoutes\" outlet route is missing from the router configuration. The \"ManagedRoutes\" outlet route must be added as a children of an hoisted route. Did you forget to include the \"ManagedRoutes\" outlet route or hoist the parent route that includes the \"ManagedRoutes\" outlet route?"); } @@ -78,7 +78,7 @@ export class Runtime extends AbstractRuntime { message += " Pending registrations:\r\n"; for (const x of nestedRoutes) { - message += ` - "${x.path ?? x.name ?? "(no identifier)"}"\r\n`; + message += ` - "${x.path ?? x.$name ?? "(no identifier)"}"\r\n`; } message += "\r\n"; diff --git a/packages/react-router/src/useMatchingRoute.ts b/packages/react-router/src/useMatchingRoute.ts index a85a395d0..990cd1556 100644 --- a/packages/react-router/src/useMatchingRoute.ts +++ b/packages/react-router/src/useMatchingRoute.ts @@ -22,5 +22,5 @@ export function useIsMatchingRouteProtected(locationArg: Partial) { throw new Error(`[squide] There's no matching route for the location: "${locationArg.pathname}". Did you add routes to React Router without using the runtime.registerRoute() function?`); } - return activeRoute.visibility === "protected"; + return activeRoute.$visibility === "protected"; } diff --git a/packages/react-router/src/useRenderedNavigationItems.tsx b/packages/react-router/src/useRenderedNavigationItems.tsx index 560114b51..5927f87ac 100644 --- a/packages/react-router/src/useRenderedNavigationItems.tsx +++ b/packages/react-router/src/useRenderedNavigationItems.tsx @@ -25,19 +25,19 @@ export type RenderItemFunction = (item: NavigationItemRenderProps, index: number export type RenderSectionFunction = (elements: ReactNode[], index: number, level: number) => ReactNode; -function toLinkProps({ label, additionalProps, ...linkProps }: NavigationLink): NavigationLinkRenderProps { +function toLinkProps({ $label, $additionalProps, ...linkProps }: NavigationLink): NavigationLinkRenderProps { return { - label, + label: $label, linkProps, - additionalProps: additionalProps ?? {} + additionalProps: $additionalProps ?? {} }; } -function toMenuProps({ label, additionalProps }: NavigationSection, sectionElement: ReactNode): NavigationSectionRenderProps { +function toMenuProps({ $label, $additionalProps }: NavigationSection, sectionElement: ReactNode): NavigationSectionRenderProps { return { - label, + label: $label, section: sectionElement, - additionalProps: additionalProps ?? {} + additionalProps: $additionalProps ?? {} }; } @@ -69,8 +69,8 @@ export function useRenderedNavigationItems( const sortedItems = [...navigationItems] .sort((x, y) => { // Default an item priority to 0 to support negative priority. - const xp = x.priority ?? 0; - const yp = y.priority ?? 0; + const xp = x.$priority ?? 0; + const yp = y.$priority ?? 0; if (xp === yp) { return 0; @@ -80,7 +80,7 @@ export function useRenderedNavigationItems( }) // priority is intentionally omitted. // eslint-disable-next-line @typescript-eslint/no-unused-vars - .map(({ priority, ...itemProps }) => itemProps); + .map(({ $priority, ...itemProps }) => itemProps); return renderItems(sortedItems, renderItem, renderSection, 0, 0); }, [navigationItems, renderItem, renderSection]); diff --git a/packages/react-router/tests/routeRegistry.test.tsx b/packages/react-router/tests/routeRegistry.test.tsx index 1293997ea..c778512d8 100644 --- a/packages/react-router/tests/routeRegistry.test.tsx +++ b/packages/react-router/tests/routeRegistry.test.tsx @@ -37,7 +37,7 @@ describe("createIndexKey", () => { test("when the route has a name, return the route name", () => { const result = createIndexKey({ - name: "foo", + $name: "foo", element:
Hello!
}); @@ -60,6 +60,8 @@ describe("add", () => { const result = registry.add({ path: "/root", element:
Hello
+ }, { + hoist: true }); expect(result.registrationStatus).toBe("registered"); @@ -85,6 +87,8 @@ describe("add", () => { const result = registry.add({ path: "/root", element:
Hello
+ }, { + hoist: true }); expect(result.completedPendingRegistrations![0].path).toBe("/root/another-level-1"); @@ -111,6 +115,8 @@ describe("add", () => { const result = registry.add({ path: "/toto", element:
Hello
+ }, { + hoist: true }); expect(result.completedPendingRegistrations!.length).toBe(0); @@ -135,6 +141,8 @@ describe("add", () => { registry.add({ path: "/root", element:
Hello
+ }, { + hoist: true }); const result = registry.add({ @@ -147,7 +155,7 @@ describe("add", () => { expect(result.registrationStatus).toBe("registered"); }); - test("when a nested route is added and complete the pending registration of nested routes, add the registered routes to the to the returned \"completedPendingRegistrations\" array", () => { + test("when a nested route is added and complete the pending registration of nested routes, add the registered routes to the returned \"completedPendingRegistrations\" array", () => { const registry = new RouteRegistry(); registry.add({ @@ -167,6 +175,8 @@ describe("add", () => { registry.add({ path: "/root", element:
Hello
+ }, { + hoist: true }); const result = registry.add({ diff --git a/packages/react-router/tests/runtime.test.tsx b/packages/react-router/tests/runtime.test.tsx index 443baab0a..523e97586 100644 --- a/packages/react-router/tests/runtime.test.tsx +++ b/packages/react-router/tests/runtime.test.tsx @@ -171,16 +171,16 @@ describe("registerRoute", () => { registerManagedRoutesOutlet(runtime); runtime.registerRoute({ + $visibility: "public", path: "/public", - element:
Hello!
, - visibility: "public" + element:
Hello!
}); const routes = getManagedRoutes(runtime.routes)!; expect(routes.length).toBe(1); expect(routes[0].path).toBe("/public"); - expect(routes[0].visibility).toBe("public"); + expect(routes[0].$visibility).toBe("public"); }); test("can register a root route with a \"protected\" visibility", () => { @@ -189,16 +189,16 @@ describe("registerRoute", () => { registerManagedRoutesOutlet(runtime); runtime.registerRoute({ + $visibility: "protected", path: "/protected", - element:
Hello!
, - visibility: "protected" + element:
Hello!
}); const routes = getManagedRoutes(runtime.routes)!; expect(routes.length).toBe(1); expect(routes[0].path).toBe("/protected"); - expect(routes[0].visibility).toBe("protected"); + expect(routes[0].$visibility).toBe("protected"); }); test("when a root route has no visibility property, it is considered as an \"protected\" route", () => { @@ -215,7 +215,7 @@ describe("registerRoute", () => { expect(routes.length).toBe(1); expect(routes[0].path).toBe("/foo"); - expect(routes[0].visibility).toBe("protected"); + expect(routes[0].$visibility).toBe("protected"); }); test("can register a nested route with a \"public\" visibility", () => { @@ -228,7 +228,7 @@ describe("registerRoute", () => { element:
Hello!
, children: [ { - visibility: "public", + $visibility: "public", path: "/layout/nested", element:
Hello!
} @@ -238,7 +238,7 @@ describe("registerRoute", () => { const routes = getManagedRoutes(runtime.routes)!; expect(routes[0].children![0].path).toBe("/layout/nested"); - expect(routes[0].children![0].visibility).toBe("public"); + expect(routes[0].children![0].$visibility).toBe("public"); }); test("can register a nested route with a \"protected\" visibility", () => { @@ -251,7 +251,7 @@ describe("registerRoute", () => { element:
Hello!
, children: [ { - visibility: "protected", + $visibility: "protected", path: "/layout/nested", element:
Hello!
} @@ -261,7 +261,7 @@ describe("registerRoute", () => { const routes = getManagedRoutes(runtime.routes)!; expect(routes[0].children![0].path).toBe("/layout/nested"); - expect(routes[0].children![0].visibility).toBe("protected"); + expect(routes[0].children![0].$visibility).toBe("protected"); }); test("when a nested route has no visibility property, it is considered as a \"protected\" route", () => { @@ -283,7 +283,7 @@ describe("registerRoute", () => { const routes = getManagedRoutes(runtime.routes)!; expect(routes[0].children![0].path).toBe("/layout/nested"); - expect(routes[0].children![0].visibility).toBe("protected"); + expect(routes[0].children![0].$visibility).toBe("protected"); }); test("can register a root route with a name", () => { @@ -292,14 +292,14 @@ describe("registerRoute", () => { registerManagedRoutesOutlet(runtime); runtime.registerRoute({ - name: "foo", + $name: "foo", element:
Hello!
}); const routes = getManagedRoutes(runtime.routes)!; expect(routes.length).toBe(1); - expect(routes[0].name).toBe("foo"); + expect(routes[0].$name).toBe("foo"); }); test("can register a nested route with a name", () => { @@ -311,7 +311,7 @@ describe("registerRoute", () => { element:
Hello
, children: [ { - name: "foo", + $name: "foo", element:
You!
} ] @@ -320,7 +320,7 @@ describe("registerRoute", () => { const routes = getManagedRoutes(runtime.routes)!; expect(routes.length).toBe(1); - expect(routes[0].children![0].name).toBe("foo"); + expect(routes[0].children![0].$name).toBe("foo"); }); }); @@ -435,30 +435,30 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ + $visibility: "public", path: "/public", - element:
Hello!
, - visibility: "public" + element:
Hello!
}, { hoist: true }); expect(runtime.routes[0].path).toBe("/public"); - expect(runtime.routes[0].visibility).toBe("public"); + expect(runtime.routes[0].$visibility).toBe("public"); }); test("can register a root route with a \"protected\" visibility", () => { const runtime = new Runtime(); runtime.registerRoute({ + $visibility: "protected", path: "/protected", - element:
Hello!
, - visibility: "protected" + element:
Hello!
}, { hoist: true }); expect(runtime.routes[0].path).toBe("/protected"); - expect(runtime.routes[0].visibility).toBe("protected"); + expect(runtime.routes[0].$visibility).toBe("protected"); }); test("when a root route has no visibility property, it is considered as an \"protected\" route", () => { @@ -472,7 +472,7 @@ describe("registerRoute", () => { }); expect(runtime.routes[0].path).toBe("/foo"); - expect(runtime.routes[0].visibility).toBe("protected"); + expect(runtime.routes[0].$visibility).toBe("protected"); }); test("can register a nested route with a \"public\" visibility", () => { @@ -483,7 +483,7 @@ describe("registerRoute", () => { element:
Hello!
, children: [ { - visibility: "public", + $visibility: "public", path: "/layout/nested", element:
Hello!
} @@ -493,7 +493,7 @@ describe("registerRoute", () => { }); expect(runtime.routes[0].children![0].path).toBe("/layout/nested"); - expect(runtime.routes[0].children![0].visibility).toBe("public"); + expect(runtime.routes[0].children![0].$visibility).toBe("public"); }); test("can register a nested route with a \"protected\" visibility", () => { @@ -504,7 +504,7 @@ describe("registerRoute", () => { element:
Hello!
, children: [ { - visibility: "protected", + $visibility: "protected", path: "/layout/nested", element:
Hello!
} @@ -514,7 +514,7 @@ describe("registerRoute", () => { }); expect(runtime.routes[0].children![0].path).toBe("/layout/nested"); - expect(runtime.routes[0].children![0].visibility).toBe("protected"); + expect(runtime.routes[0].children![0].$visibility).toBe("protected"); }); test("when a nested route has no visibility property, it is considered as an \"protected\" route", () => { @@ -534,21 +534,21 @@ describe("registerRoute", () => { }); expect(runtime.routes[0].children![0].path).toBe("/layout/nested"); - expect(runtime.routes[0].children![0].visibility).toBe("protected"); + expect(runtime.routes[0].children![0].$visibility).toBe("protected"); }); test("can register a root route with a name", () => { const runtime = new Runtime(); runtime.registerRoute({ - name: "foo", + $name: "foo", element:
Hello!
}, { hoist: true }); expect(runtime.routes.length).toBe(1); - expect(runtime.routes[0].name).toBe("foo"); + expect(runtime.routes[0].$name).toBe("foo"); }); test("can register a nested route with a name", () => { @@ -558,7 +558,7 @@ describe("registerRoute", () => { element:
Hello
, children: [ { - name: "foo", + $name: "foo", element:
You!
} ] @@ -567,7 +567,7 @@ describe("registerRoute", () => { }); expect(runtime.routes.length).toBe(1); - expect(runtime.routes[0].children![0].name).toBe("foo"); + expect(runtime.routes[0].children![0].$name).toBe("foo"); }); }); @@ -808,7 +808,7 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - name: "layout", + $name: "layout", element:
Hello!
}, { hoist: true @@ -870,7 +870,7 @@ describe("registerRoute", () => { expect(runtime.routes[0].children).toBeUndefined(); runtime.registerRoute({ - name: "layout", + $name: "layout", element:
Hello!
}, { hoist: true @@ -899,7 +899,7 @@ describe("registerRoute", () => { element:
You!
, children: [ { - name: "layout", + $name: "layout", element:
Hello from nested!
} ] @@ -918,14 +918,14 @@ describe("registerRoute", () => { const runtime = new Runtime(); runtime.registerRoute({ - name: "layout", + $name: "layout", element:
Hello!
}, { hoist: true }); runtime.registerRoute({ - name: "layout-nested", + $name: "layout-nested", element:
Hello!
} , { @@ -953,7 +953,7 @@ describe("registerRoute", () => { element:
You!
, children: [ { - name: "deeply-nested-layout", + $name: "deeply-nested-layout", element:
Hello from nested!
} ] @@ -964,14 +964,14 @@ describe("registerRoute", () => { }); runtime.registerRoute({ - name: "deeply-nested-layout/another-level", + $name: "deeply-nested-layout/another-level", element:
Hello!
}, { parentName: "deeply-nested-layout" }); expect(runtime.routes.length).toBe(1); - expect(runtime.routes[0].children![0].children![0].children![0].name).toBe("deeply-nested-layout/another-level"); + expect(runtime.routes[0].children![0].children![0].children![0].$name).toBe("deeply-nested-layout/another-level"); runtime.registerRoute({ path: "/deeply-nested-layout/another-level/yet-another-level", @@ -1002,8 +1002,8 @@ describe("registerNavigationItem", () => { const runtime = new Runtime(); runtime.registerNavigationItem({ - to: "/root", - label: "Root" + $label: "Root", + to: "/root" }); expect(runtime.getNavigationItems()[0].to).toBe("/root"); @@ -1013,24 +1013,24 @@ describe("registerNavigationItem", () => { const runtime = new Runtime(); runtime.registerNavigationItem({ - label: "Section", + $label: "Section", children: [ { - to: "/child", - label: "Child" + $label: "Child", + to: "/child" } ] }); - expect(runtime.getNavigationItems()[0].label).toBe("Section"); + expect(runtime.getNavigationItems()[0].$label).toBe("Section"); }); test("can register a navigation link for a specific menu id", () => { const runtime = new Runtime(); runtime.registerNavigationItem({ - to: "/link", - label: "Link" + $label: "Link", + to: "/link" }, { menuId: "link-menu" }); @@ -1042,18 +1042,18 @@ describe("registerNavigationItem", () => { const runtime = new Runtime(); runtime.registerNavigationItem({ - label: "Section", + $label: "Section", children: [ { - to: "/child", - label: "Child" + $label: "Child", + to: "/child" } ] }, { menuId: "section-menu" }); - expect(runtime.getNavigationItems("section-menu")[0].label).toBe("Section"); + expect(runtime.getNavigationItems("section-menu")[0].$label).toBe("Section"); }); }); @@ -1062,30 +1062,30 @@ describe("getNavigationItems", () => { const runtime = new Runtime(); runtime.registerNavigationItem({ - to: "/item-1", - label: "Item 1" + $label: "Item 1", + to: "/item-1" }); runtime.registerNavigationItem({ - to: "/item-2", - label: "Item 2" + $label: "Item 2", + to: "/item-2" }); runtime.registerNavigationItem({ - to: "/item-3", - label: "Item 3" + $label: "Item 3", + to: "/item-3" }); runtime.registerNavigationItem({ - to: "/item-4", - label: "Item 4" + $label: "Item 4", + to: "/item-4" }, { menuId: "menu-1" }); runtime.registerNavigationItem({ - to: "/item-5", - label: "Item 5" + $label: "Item 5", + to: "/item-5" }, { menuId: "menu-2" }); @@ -1099,30 +1099,30 @@ describe("getNavigationItems", () => { const runtime = new Runtime(); runtime.registerNavigationItem({ - to: "/item-1", - label: "Item 1" + $label: "Item 1", + to: "/item-1" }); runtime.registerNavigationItem({ - to: "/item-2", - label: "Item 2" + $label: "Item 2", + to: "/item-2" }); runtime.registerNavigationItem({ - to: "/item-3", - label: "Item 3" + $label: "Item 3", + to: "/item-3" }); runtime.registerNavigationItem({ - to: "/item-4", - label: "Item 4" + $label: "Item 4", + to: "/item-4" }, { menuId: "menu-1" }); runtime.registerNavigationItem({ - to: "/item-5", - label: "Item 5" + $label: "Item 5", + to: "/item-5" }, { menuId: "menu-2" }); @@ -1199,7 +1199,7 @@ describe("_completeRegistration", () => { }); runtime.registerRoute({ - name: "layout", + $name: "layout", element:
Hello!
}, { hoist: true diff --git a/packages/react-router/tests/useNavigationItems.test.tsx b/packages/react-router/tests/useNavigationItems.test.tsx index a07f9236e..1b3f56419 100644 --- a/packages/react-router/tests/useNavigationItems.test.tsx +++ b/packages/react-router/tests/useNavigationItems.test.tsx @@ -18,30 +18,30 @@ test("when no menu id is specified, returns all the registered navigation items const runtime = new Runtime(); runtime.registerNavigationItem({ - to: "/item-1", - label: "Item 1" + $label: "Item 1", + to: "/item-1" }); runtime.registerNavigationItem({ - to: "/item-2", - label: "Item 2" + $label: "Item 2", + to: "/item-2" }); runtime.registerNavigationItem({ - to: "/item-3", - label: "Item 3" + $label: "Item 3", + to: "/item-3" }); runtime.registerNavigationItem({ - to: "/item-4", - label: "Item 4" + $label: "Item 4", + to: "/item-4" }, { menuId: "menu-1" }); runtime.registerNavigationItem({ - to: "/item-5", - label: "Item 5" + $label: "Item 5", + to: "/item-5" }, { menuId: "menu-2" }); @@ -55,30 +55,30 @@ test("when a menu id is specified, returns all the registered navigation items f const runtime = new Runtime(); runtime.registerNavigationItem({ - to: "/item-1", - label: "Item 1" + $label: "Item 1", + to: "/item-1" }); runtime.registerNavigationItem({ - to: "/item-2", - label: "Item 2" + $label: "Item 2", + to: "/item-2" }); runtime.registerNavigationItem({ - to: "/item-3", - label: "Item 3" + $label: "Item 3", + to: "/item-3" }); runtime.registerNavigationItem({ - to: "/item-4", - label: "Item 4" + $label: "Item 4", + to: "/item-4" }, { menuId: "menu-1" }); runtime.registerNavigationItem({ - to: "/item-5", - label: "Item 5" + $label: "Item 5", + to: "/item-5" }, { menuId: "menu-2" }); @@ -92,8 +92,8 @@ test("returned array is immutable", () => { const runtime = new Runtime(); runtime.registerNavigationItem({ - to: "/foo", - label: "Foo" + $label: "Foo", + to: "/foo" }); const { result, rerender } = renderWithRuntime(runtime); @@ -106,8 +106,8 @@ test("returned array is immutable", () => { const array2 = result.current; runtime.registerNavigationItem({ - to: "/bar", - label: "Bar" + $label: "Bar", + to: "/bar" }); // Added a new navigation item, the returned array should be a new instance. diff --git a/packages/react-router/tests/useRenderedNavigationItems.test.tsx b/packages/react-router/tests/useRenderedNavigationItems.test.tsx index f8519fb6e..d9b5edd96 100644 --- a/packages/react-router/tests/useRenderedNavigationItems.test.tsx +++ b/packages/react-router/tests/useRenderedNavigationItems.test.tsx @@ -1,7 +1,7 @@ import { renderHook } from "@testing-library/react"; import { useCallback, type ReactNode } from "react"; import renderer from "react-test-renderer"; -import type { RootNavigationItem } from "../src/navigationItemRegistry.ts"; +import type { NavigationItem, RootNavigationItem } from "../src/navigationItemRegistry.ts"; import { isNavigationLink, useRenderedNavigationItems, type NavigationLinkRenderProps, type NavigationSectionRenderProps, type RenderItemFunction, type RenderSectionFunction } from "../src/useRenderedNavigationItems.tsx"; type RenderLinkItemFunction = (item: NavigationLinkRenderProps, index: number, level: number) => ReactNode; @@ -63,22 +63,22 @@ function TestComponent({ navigationItems }: TestComponentProps) { test("highest priority goes first", () => { const navigationItems: RootNavigationItem[] = [ { - to: "/foo", - label: "Foo" + $label: "Foo", + to: "/foo" }, { - to: "/bar", - label: "Bar", - priority: 5 + $label: "Bar", + $priority: 5, + to: "/bar" }, { - to: "/toto", - label: "Toto", - priority: 99 + $label: "Toto", + $priority: 99, + to: "/toto" }, { - to: "/tutu", - label: "Tutu" + $label: "Tutu", + to: "/tutu" } ]; @@ -92,21 +92,21 @@ test("highest priority goes first", () => { test("negative priority goes last", () => { const navigationItems: RootNavigationItem[] = [ { - to: "/foo", - label: "Foo" + $label: "Foo", + to: "/foo" }, { - to: "/bar", - label: "Bar" + $label: "Bar", + to: "/bar" }, { - to: "/toto", - label: "Toto", - priority: -1 + $label: "Toto", + $priority: -1, + to: "/toto" }, { - to: "/tutu", - label: "Tutu" + $label: "Tutu", + to: "/tutu" } ]; @@ -120,19 +120,19 @@ test("negative priority goes last", () => { test("support 2 section levels", () => { const navigationItems: RootNavigationItem[] = [ { - to: "/foo", - label: "Foo" + $label: "Foo", + to: "/foo" }, { - label: "Bar", + $label: "Bar", children: [ { - to: "/toto", - label: "Toto" + $label: "Toto", + to: "/toto" }, { - to: "/tutu", - label: "Tutu" + $label: "Tutu", + to: "/tutu" } ] } @@ -148,22 +148,22 @@ test("support 2 section levels", () => { test("support 3 section levels", () => { const navigationItems: RootNavigationItem[] = [ { - to: "/foo", - label: "Foo" + $label: "Foo", + to: "/foo" }, { - label: "Bar", + $label: "Bar", children: [ { - to: "/toto", - label: "Toto" + $label: "Toto", + to: "/toto" }, { - label: "Tutu", + $label: "Tutu", children: [ { - to: "/titi", - label: "Titi" + $label: "Titi", + to: "/titi" } ] } @@ -181,15 +181,15 @@ test("support 3 section levels", () => { test("Link item additionalProps are rendered", () => { const navigationItems: RootNavigationItem[] = [ { - to: "/foo", - label: "Foo", - additionalProps: { + $label: "Foo", + $additionalProps: { style: { color: "red" } - } + }, + to: "/foo" }, { - to: "/bar", - label: "Bar" + $label: "Bar", + to: "/bar" } ]; @@ -203,14 +203,14 @@ test("Link item additionalProps are rendered", () => { test("Section item additionalProps are rendered", () => { const navigationItems: RootNavigationItem[] = [ { - label: "Foo", + $label: "Foo", children: [ { - to: "/bar", - label: "Bar" + $label: "Bar", + to: "/bar" } ], - additionalProps: { + $additionalProps: { style: { color: "red" } } } @@ -224,10 +224,10 @@ test("Section item additionalProps are rendered", () => { }); test("doesn't rerender when the navigation items haven't changed", () => { - const initialItems = [ + const initialItems: NavigationItem[] = [ { - to: "/foo", - label: "Foo" + $label: "Foo", + to: "/foo" } ]; @@ -249,10 +249,10 @@ test("doesn't rerender when the navigation items haven't changed", () => { }); test("rerender when the navigation items change", () => { - const initialItems = [ + const initialItems: NavigationItem[] = [ { - to: "/foo", - label: "Foo" + $label: "Foo", + to: "/foo" } ]; @@ -268,8 +268,8 @@ test("rerender when the navigation items change", () => { rerender({ navigationItems: [ { - to: "/bar", - label: "Bar" + $label: "Bar", + to: "/bar" } ] }); diff --git a/sample/another-remote-module/src/register.tsx b/sample/another-remote-module/src/register.tsx index 88b2fd58b..5714b8b20 100644 --- a/sample/another-remote-module/src/register.tsx +++ b/sample/another-remote-module/src/register.tsx @@ -8,7 +8,7 @@ function registerRoutes(runtime: Runtime) { runtime.registerNavigationItem({ to: "/federated-tabs", - label: "Tabs" + $label: "Tabs" }); } diff --git a/sample/local-module/src/dev/register.tsx b/sample/local-module/src/dev/register.tsx index d16f01c45..607f60cd5 100644 --- a/sample/local-module/src/dev/register.tsx +++ b/sample/local-module/src/dev/register.tsx @@ -13,7 +13,7 @@ export const registerDev: ModuleRegisterFunction = runtime => { }); runtime.registerNavigationItem({ - to: "/federated-tabs", - label: "Tabs" + $label: "Tabs", + to: "/federated-tabs" }); }; diff --git a/sample/local-module/src/register.tsx b/sample/local-module/src/register.tsx index 58c02fd2e..ad8688c8a 100644 --- a/sample/local-module/src/register.tsx +++ b/sample/local-module/src/register.tsx @@ -13,19 +13,19 @@ function registerRoutes(runtime: Runtime) { }); runtime.registerNavigationItem({ - to: "/about", - label: "About" + $label: "About", + to: "/about" }); runtime.registerNavigationItem({ - to: "/message", - label: "Message", + $label: "Message", // Higher numbers gets rendered first. - priority: 999, + $priority: 999, // Will be forwarded to the host application render function. - additionalProps: { + $additionalProps: { highlight: true - } + }, + to: "/message" }); // Register federated tabs. @@ -38,8 +38,8 @@ function registerRoutes(runtime: Runtime) { }); runtime.registerNavigationItem({ - to: "/federated-tabs", - label: "Workleap" + $label: "Workleap", + to: "/federated-tabs" }, { menuId: "/federated-tabs" }); diff --git a/sample/remote-module/src/Fetch.tsx b/sample/remote-module/src/Fetch.tsx index 09216849b..205075c07 100644 --- a/sample/remote-module/src/Fetch.tsx +++ b/sample/remote-module/src/Fetch.tsx @@ -19,7 +19,7 @@ export function Fetch() { return (

Fetch

-

An example fetching data with React Router loaders.

+

An example fetching data with React Query.

{characters.map((x: Character) => { return ( diff --git a/sample/remote-module/src/dev/register.tsx b/sample/remote-module/src/dev/register.tsx index d16f01c45..607f60cd5 100644 --- a/sample/remote-module/src/dev/register.tsx +++ b/sample/remote-module/src/dev/register.tsx @@ -13,7 +13,7 @@ export const registerDev: ModuleRegisterFunction = runtime => { }); runtime.registerNavigationItem({ - to: "/federated-tabs", - label: "Tabs" + $label: "Tabs", + to: "/federated-tabs" }); }; diff --git a/sample/remote-module/src/register.tsx b/sample/remote-module/src/register.tsx index fa3d386f6..0197fcc82 100644 --- a/sample/remote-module/src/register.tsx +++ b/sample/remote-module/src/register.tsx @@ -77,42 +77,42 @@ function registerRoutes(runtime: Runtime) { }); runtime.registerNavigationItem({ - to: "/remote", - label: "Remote" + $label: "Remote", + to: "/remote" }); runtime.registerNavigationItem({ - to: "/fetch", - label: "Fetch" + $label: "Fetch", + to: "/fetch" }); runtime.registerNavigationItem({ - to: "/hoisted", - label: Hoisted + $label: Hoisted, + to: "/hoisted" }); runtime.registerNavigationItem({ - label: "Section", + $label: "Section", children: [ { to: "#", - label: "Child 1" + $label: "Child 1" }, { to: "#", - label: "Child 2" + $label: "Child 2" } ] }); runtime.registerNavigationItem({ - to: "/no-context-override", - label: "No context override" + $label: "No context override", + to: "/no-context-override" }); runtime.registerNavigationItem({ - to: "/context-override", - label: "Context override" + $label: "Context override", + to: "/context-override" }); // Register federated tabs. @@ -132,16 +132,16 @@ function registerRoutes(runtime: Runtime) { }); runtime.registerNavigationItem({ - to: "/federated-tabs/officevibe", - label: "Officevibe" + $label: "Officevibe", + to: "/federated-tabs/officevibe" }, { menuId: "/federated-tabs" }); runtime.registerNavigationItem({ - to: "/federated-tabs/skills", - label: "Skills", - priority: 999 + $label: "Skills", + $priority: 999, + to: "/federated-tabs/skills" }, { menuId: "/federated-tabs" }); diff --git a/sample/shell/src/register.tsx b/sample/shell/src/register.tsx index 40d28644b..c1c8e0406 100644 --- a/sample/shell/src/register.tsx +++ b/sample/shell/src/register.tsx @@ -10,13 +10,13 @@ import { RootLayout } from "./RootLayout.tsx"; function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { runtime.registerRoute({ // Pathless route to declare a root layout and a root error boundary. - visibility: "public", + $visibility: "public", element: , children: [ { // Public pages like the login and logout pages will be rendered under this pathless route. - visibility: "public", - name: "root-error-boundary", + $visibility: "public", + $name: "root-error-boundary", errorElement: , children: [ { @@ -65,7 +65,7 @@ function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { }); runtime.registerRoute({ - visibility: "public", + $visibility: "public", path: "/login", lazy: async () => { const { Login } = await import("./Login.tsx"); @@ -79,7 +79,7 @@ function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { }); runtime.registerRoute({ - visibility: "public", + $visibility: "public", path: "/logout", lazy: async () => { const { Logout } = await import("./Logout.tsx"); @@ -93,7 +93,7 @@ function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { }); runtime.registerRoute({ - visibility: "public", + $visibility: "public", path: "*", lazy: async () => { const { NoMatch } = await import("./NoMatch.tsx"); From bab385428119e34e9d58248089c9f73f9b7d33b5 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Thu, 12 Oct 2023 12:23:44 -0400 Subject: [PATCH 17/51] Updated sample dev & build script to include MSW --- package.json | 1 - sample/another-remote-module/package.json | 1 - sample/host/package.json | 5 ++--- sample/host/webpack.build.js | 3 ++- sample/local-module/package.json | 1 - sample/remote-module/package.json | 8 +++----- sample/remote-module/webpack.build.js | 3 ++- 7 files changed, 9 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 9ba70db0b..343f547bf 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "reset": "pnpm clean && pnpm reset:modules", "reset:modules": "pnpm -r --parallel --include-workspace-root exec pnpm dlx rimraf node_modules pnpm-lock.yaml", "dev-sample": "pnpm --filter \"./sample/*\" -r --parallel dev", - "dev-sample-msw": "pnpm --filter \"./sample/*\" -r --parallel dev-msw", "build-sample": "pnpm --filter \"./sample/*\" -r --parallel build", "serve-sample": "pnpm --filter \"./sample/*\" -r --parallel serve-build", "deploy-sample": "cross-env NETLIFY=true pnpm build-sample && pnpm run deploy-sample:host && pnpm run deploy-sample:remote-module && pnpm run deploy-sample:another-remote-module", diff --git a/sample/another-remote-module/package.json b/sample/another-remote-module/package.json index a4251f520..73b67345e 100644 --- a/sample/another-remote-module/package.json +++ b/sample/another-remote-module/package.json @@ -8,7 +8,6 @@ "type": "module", "scripts": { "dev": "webpack serve --config webpack.dev.js", - "dev-msw": "pnpm dev", "dev-local": "cross-env LOCAL=true webpack serve --config webpack.dev.js", "build": "webpack --config webpack.build.js", "serve-build": "pnpm build && pnpm http-server dist -p 8082 -P http://localhost:8082? -c-1" diff --git a/sample/host/package.json b/sample/host/package.json index 507ae8fa2..521864ce0 100644 --- a/sample/host/package.json +++ b/sample/host/package.json @@ -7,9 +7,8 @@ "license": "Apache-2.0", "type": "module", "scripts": { - "dev": "nodemon", - "dev-msw": "cross-env USE_MSW=true pnpm dev", - "build": "pnpm build:webpack && pnpm build:copy-redirects", + "dev": "cross-env USE_MSW=true nodemon", + "build": "cross-env USE_MSW=true pnpm build:webpack && pnpm build:copy-redirects", "build:webpack": "webpack --config webpack.build.js", "build:copy-redirects": "copyfiles _redirects dist", "serve-build": "pnpm build && pnpm http-server dist -p 8080 -P http://localhost:8080? -c-1" diff --git a/sample/host/webpack.build.js b/sample/host/webpack.build.js index 87916cb96..8a7004552 100644 --- a/sample/host/webpack.build.js +++ b/sample/host/webpack.build.js @@ -14,7 +14,8 @@ export default defineBuildHostConfig(swcConfig, "host", publicPath, { } }, environmentVariables: { - "NETLIFY": process.env.NETLIFY === "true" + "NETLIFY": process.env.NETLIFY === "true", + "USE_MSW": process.env.USE_MSW === "true" } }); diff --git a/sample/local-module/package.json b/sample/local-module/package.json index 48e92dba2..a83b05632 100644 --- a/sample/local-module/package.json +++ b/sample/local-module/package.json @@ -15,7 +15,6 @@ }, "scripts": { "dev": "nodemon", - "dev-msw": "pnpm dev", "dev-local": "nodemon --config nodemon.local.json", "build": "tsup --config ./tsup.build.ts", "serve-build": "pnpm build" diff --git a/sample/remote-module/package.json b/sample/remote-module/package.json index 135c12950..3be3fb370 100644 --- a/sample/remote-module/package.json +++ b/sample/remote-module/package.json @@ -7,11 +7,9 @@ "license": "Apache-2.0", "type": "module", "scripts": { - "dev": "nodemon", - "dev-msw": "cross-env USE_MSW=true pnpm dev", - "dev-local": "cross-env LOCAL=true pnpm dev", - "dev-local-msw": "cross-env USE_MSW=true pnpm dev-local", - "build": "webpack --config webpack.build.js", + "dev": "cross-env USE_MSW=true nodemon", + "dev-local": "cross-env USE_MSW=true LOCAL=true pnpm dev", + "build": "cross-env USE_MSW=true webpack --config webpack.build.js", "serve-build": "pnpm build && pnpm http-server dist -p 8081 -P http://localhost:8081? -c-1" }, "devDependencies": { diff --git a/sample/remote-module/webpack.build.js b/sample/remote-module/webpack.build.js index 9197cf8ab..d8fe26392 100644 --- a/sample/remote-module/webpack.build.js +++ b/sample/remote-module/webpack.build.js @@ -13,6 +13,7 @@ export default defineBuildRemoteModuleConfig(swcConfig, "remote1", publicPath, { } }, environmentVariables: { - "NETLIFY": process.env.NETLIFY === "true" + "NETLIFY": process.env.NETLIFY === "true", + "USE_MSW": process.env.USE_MSW === "true" } }); From 051461918e8075ce329e8b5ffb796488051a5101 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Thu, 12 Oct 2023 13:24:46 -0400 Subject: [PATCH 18/51] Added a telemetry service example --- sample/another-remote-module/src/dev/App.tsx | 8 +++- sample/host/src/App.tsx | 9 ++++- sample/local-module/src/dev/App.tsx | 8 +++- sample/remote-module/src/dev/App.tsx | 9 ++++- sample/shared/src/index.ts | 1 + sample/shared/src/telemetryService.ts | 24 ++++++++++++ sample/shell/src/useAppRouter.tsx | 41 ++++++++++++++------ 7 files changed, 82 insertions(+), 18 deletions(-) create mode 100644 sample/shared/src/telemetryService.ts diff --git a/sample/another-remote-module/src/dev/App.tsx b/sample/another-remote-module/src/dev/App.tsx index 41e44d807..81dfdd533 100644 --- a/sample/another-remote-module/src/dev/App.tsx +++ b/sample/another-remote-module/src/dev/App.tsx @@ -1,6 +1,12 @@ +import { LoggerTelemetryService } from "@sample/shared"; import { useAppRouter } from "@sample/shell"; +import { useLogger } from "@squide/react-router"; import { sessionManager } from "./session.ts"; export function App() { - return useAppRouter(process.env.USE_MSW as unknown as boolean, sessionManager); + const logger = useLogger(); + + const telemetryService = new LoggerTelemetryService(logger); + + return useAppRouter(process.env.USE_MSW as unknown as boolean, sessionManager, telemetryService); } diff --git a/sample/host/src/App.tsx b/sample/host/src/App.tsx index 24ca34efc..15d4755a9 100644 --- a/sample/host/src/App.tsx +++ b/sample/host/src/App.tsx @@ -1,9 +1,14 @@ -import { BackgroundColorContext } from "@sample/shared"; +import { BackgroundColorContext, LoggerTelemetryService } from "@sample/shared"; import { useAppRouter } from "@sample/shell"; +import { useLogger } from "@squide/react-router"; import { sessionManager } from "./session.ts"; export function App() { - const appRouter = useAppRouter(process.env.USE_MSW as unknown as boolean, sessionManager); + const logger = useLogger(); + + const telemetryService = new LoggerTelemetryService(logger); + + const appRouter = useAppRouter(process.env.USE_MSW as unknown as boolean, sessionManager, telemetryService); return ( diff --git a/sample/local-module/src/dev/App.tsx b/sample/local-module/src/dev/App.tsx index 41e44d807..81dfdd533 100644 --- a/sample/local-module/src/dev/App.tsx +++ b/sample/local-module/src/dev/App.tsx @@ -1,6 +1,12 @@ +import { LoggerTelemetryService } from "@sample/shared"; import { useAppRouter } from "@sample/shell"; +import { useLogger } from "@squide/react-router"; import { sessionManager } from "./session.ts"; export function App() { - return useAppRouter(process.env.USE_MSW as unknown as boolean, sessionManager); + const logger = useLogger(); + + const telemetryService = new LoggerTelemetryService(logger); + + return useAppRouter(process.env.USE_MSW as unknown as boolean, sessionManager, telemetryService); } diff --git a/sample/remote-module/src/dev/App.tsx b/sample/remote-module/src/dev/App.tsx index fabbb2ec8..a42157cc6 100644 --- a/sample/remote-module/src/dev/App.tsx +++ b/sample/remote-module/src/dev/App.tsx @@ -1,9 +1,14 @@ -import { BackgroundColorContext } from "@sample/shared"; +import { BackgroundColorContext, LoggerTelemetryService } from "@sample/shared"; import { useAppRouter } from "@sample/shell"; +import { useLogger } from "@squide/react-router"; import { sessionManager } from "./session.ts"; export function App() { - const appRouter = useAppRouter(process.env.USE_MSW as unknown as boolean, sessionManager); + const logger = useLogger(); + + const telemetryService = new LoggerTelemetryService(logger); + + const appRouter = useAppRouter(process.env.USE_MSW as unknown as boolean, sessionManager, telemetryService); return ( diff --git a/sample/shared/src/index.ts b/sample/shared/src/index.ts index 65bf5bc34..7db82352d 100644 --- a/sample/shared/src/index.ts +++ b/sample/shared/src/index.ts @@ -4,4 +4,5 @@ export * from "./eventBus.ts"; export * from "./isNetlify.ts"; export * from "./session.ts"; export * from "./subscription.ts"; +export * from "./telemetryService.ts"; diff --git a/sample/shared/src/telemetryService.ts b/sample/shared/src/telemetryService.ts new file mode 100644 index 000000000..335b2487b --- /dev/null +++ b/sample/shared/src/telemetryService.ts @@ -0,0 +1,24 @@ +import type { Logger } from "@squide/core"; +import { createContext, useContext } from "react"; + +export interface TelemetryService { + track: (value: string) => void; +} + +export class LoggerTelemetryService implements TelemetryService { + #logger: Logger; + + constructor(logger: Logger) { + this.#logger = logger; + } + + track(value: string) { + this.#logger.information(`[telemetry] ${value}`); + } +} + +export const TelemetryServiceContext = createContext(undefined); + +export function useTelemetryService() { + return useContext(TelemetryServiceContext); +} diff --git a/sample/shell/src/useAppRouter.tsx b/sample/shell/src/useAppRouter.tsx index 81ed866c2..f3a7a62f3 100644 --- a/sample/shell/src/useAppRouter.tsx +++ b/sample/shell/src/useAppRouter.tsx @@ -1,16 +1,26 @@ -import type { Session, SessionManager, Subscription } from "@sample/shared"; -import { SubscriptionContext } from "@sample/shared"; +import { SubscriptionContext, TelemetryServiceContext, useTelemetryService, type Session, type SessionManager, type Subscription, type TelemetryService } from "@sample/shared"; import { useIsMswStarted } from "@squide/msw"; import { useIsMatchingRouteProtected, useLogger, useRoutes } from "@squide/react-router"; import { useAreModulesReady } from "@squide/webpack-module-federation"; import axios from "axios"; import { useEffect, useMemo, useState } from "react"; -import { RouterProvider, createBrowserRouter } from "react-router-dom"; +import { Outlet, RouterProvider, createBrowserRouter, useLocation } from "react-router-dom"; -export function useAppRouter(waitForMsw: boolean, sessionManager: SessionManager) { +export function RootRoute() { + const location = useLocation(); + const telemetryService = useTelemetryService(); + + useEffect(() => { + telemetryService?.track(`Navigated to the "${location.pathname}" page.`); + }, [location, telemetryService]); + + return ; +} + +export function useAppRouter(waitForMsw: boolean, sessionManager: SessionManager, telemetryService: TelemetryService) { const [isReady, setIsReady] = useState(false); - // Could have been done with a ref (https://react.dev/reference/react/useRef) to save a re-render but for this sample + // Could be done with a ref (https://react.dev/reference/react/useRef) to save a re-render but for this sample // it seemed unnecessary. If your app have multiple global data structures like this one thought, it should be considered. const [subscription, setSubscription] = useState(); @@ -80,7 +90,12 @@ export function useAppRouter(waitForMsw: boolean, sessionManager: SessionManager }, [areModulesReady, isMswStarted, isActiveRouteProtected, logger, sessionManager]); const router = useMemo(() => { - return createBrowserRouter(routes); + return createBrowserRouter([ + { + element: , + children: routes + } + ]); }, [routes]); if (!isReady) { @@ -88,11 +103,13 @@ export function useAppRouter(waitForMsw: boolean, sessionManager: SessionManager } return ( - - - + + + + + ); } From d4320fba14f957396efd77344e18a90c3739e29b Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Thu, 12 Oct 2023 13:57:08 -0400 Subject: [PATCH 19/51] Updated the docs to use the new $ props --- docs/getting-started/create-local-module.md | 4 +- docs/getting-started/create-remote-module.md | 4 +- docs/getting-started/learn-the-api.md | 6 +- docs/guides/federated-tabs.md | 22 ++-- docs/guides/override-the-host-layout.md | 2 +- .../registration/registerLocalModules.md | 4 +- .../registration/registerRemoteModules.md | 4 +- docs/reference/runtime/runtime-class.md | 104 +++++++++--------- 8 files changed, 75 insertions(+), 75 deletions(-) diff --git a/docs/getting-started/create-local-module.md b/docs/getting-started/create-local-module.md index 5dc5b9092..f4af953ad 100644 --- a/docs/getting-started/create-local-module.md +++ b/docs/getting-started/create-local-module.md @@ -97,8 +97,8 @@ export const register: ModuleRegisterFunction = (runtime, c }); runtime.registerNavigationItem({ - to: "/local/page", - content: "Local/Page" + $label: "Local/Page", + to: "/local/page" }); } ``` diff --git a/docs/getting-started/create-remote-module.md b/docs/getting-started/create-remote-module.md index 9224a083e..98a6be65a 100644 --- a/docs/getting-started/create-remote-module.md +++ b/docs/getting-started/create-remote-module.md @@ -74,8 +74,8 @@ export const register: ModuleRegisterFunction = (runtime, c }); runtime.registerNavigationItem({ - to: "/remote/page", - label: "Remote/Page" + $label: "Remote/Page", + to: "/remote/page" }); } ``` diff --git a/docs/getting-started/learn-the-api.md b/docs/getting-started/learn-the-api.md index c810b4aa9..00ae508ce 100644 --- a/docs/getting-started/learn-the-api.md +++ b/docs/getting-started/learn-the-api.md @@ -137,7 +137,7 @@ To keep Squide lightweight, not all functionalities should be integrated as a co First, define a plugin by implementing the [Plugin](../reference/plugins/plugin.md) interface: -```ts !#4 shared/src/mswPlugin.ts +```ts shared/src/mswPlugin.ts import { Plugin } from "@squide/react-router"; import type { RestHandler } from "msw"; @@ -154,7 +154,7 @@ export class MswPlugin extends Plugin { Then, make the plugin available to every part of the application by passing a service instance to the `Runtime` instance: -```ts !#5 host/src/boostrap.tsx +```ts host/src/boostrap.tsx import { Runtime } from "@squide/react-router"; import { MswPlugin } from "@squide/msw"; @@ -165,7 +165,7 @@ const runtime = new Runtime({ Then, access the plugin instance from the [Runtime](/reference/runtime/runtime-class.md) instance: -```ts !#4 +```ts import { MswPlugin } from "@sample/shared"; import { requetHandlers } from "../mocks/handlers.ts"; diff --git a/docs/guides/federated-tabs.md b/docs/guides/federated-tabs.md index 40bbee019..5dd030779 100644 --- a/docs/guides/federated-tabs.md +++ b/docs/guides/federated-tabs.md @@ -59,8 +59,8 @@ export const register: ModuleRegisterFunction = runtime => { }); runtime.registerNavigationItem({ - to: "/federated-tabs", - label: "Federated tabs" + $label: "Federated tabs", + to: "/federated-tabs" }); } ``` @@ -166,8 +166,8 @@ export const register: ModuleRegisterFunction = runtime => { }); runtime.registerNavigationItem({ - to: "/federated-tabs", - label: "Tab 1" + $label: "Tab 1", + to: "/federated-tabs" }, { // The menu id could be anything, in this example we are using the same path as the nested layout // path for convenience. @@ -190,8 +190,8 @@ export const register: ModuleRegisterFunction = (runtime: Runtime) => { }); runtime.registerNavigationItem({ - to: "/federated-tabs/tab-2", - label: "Tab 2" + $label: "Tab 2", + to: "/federated-tabs/tab-2" }, { // The menu id could be anything, in this example we are using the same path as the nested layout // path for convenience. @@ -214,8 +214,8 @@ export const register: ModuleRegisterFunction = runtime => { }); runtime.registerNavigationItem({ - to: "/federated-tabs/tab-3", - label: "Tab 3" + $label: "Tab 3", + to: "/federated-tabs/tab-3" }, { // The menu id could be anything, in this example we are using the same path as the nested layout // path for convenience. @@ -294,10 +294,10 @@ export const register: ModuleRegisterFunction = runtime => { }); runtime.registerNavigationItem({ - to: "/federated-tabs/tab-3", - label: "Tab 3", + $label: "Tab 3", // Highest priority goes first. - priority: 999 + $priority: 999, + to: "/federated-tabs/tab-3" }, { menuId: "/federated-tabs" }); diff --git a/docs/guides/override-the-host-layout.md b/docs/guides/override-the-host-layout.md index 51ac89d2b..671cbf7f6 100644 --- a/docs/guides/override-the-host-layout.md +++ b/docs/guides/override-the-host-layout.md @@ -168,7 +168,7 @@ export function App() { Then, mark the pages as hoisted and optionally use a new layout: -```tsx #11-12,15,18-19 local-module/src/register.tsx +```tsx #13,16-17,23 local-module/src/register.tsx import { lazy } from "react"; import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { LocalLayout } from "./LocalLayout.tsx"; diff --git a/docs/reference/registration/registerLocalModules.md b/docs/reference/registration/registerLocalModules.md index d2e0fa148..c985dcaed 100644 --- a/docs/reference/registration/registerLocalModules.md +++ b/docs/reference/registration/registerLocalModules.md @@ -54,8 +54,8 @@ export function register: ModuleRegisterFunction(runtime, c }); runtime.registerNavigationItem({ - to: "/about", - label: "About" + $label: "About", + to: "/about" }); } ``` diff --git a/docs/reference/registration/registerRemoteModules.md b/docs/reference/registration/registerRemoteModules.md index 97460fd05..98d96868f 100644 --- a/docs/reference/registration/registerRemoteModules.md +++ b/docs/reference/registration/registerRemoteModules.md @@ -64,8 +64,8 @@ export function register: ModuleRegisterFunction(runtime, c }); runtime.registerNavigationItem({ - to: "/about", - label: "About" + $label: "About", + to: "/about" }); } ``` diff --git a/docs/reference/runtime/runtime-class.md b/docs/reference/runtime/runtime-class.md index 365900f81..11fcc7dd6 100644 --- a/docs/reference/runtime/runtime-class.md +++ b/docs/reference/runtime/runtime-class.md @@ -60,8 +60,8 @@ runtime.registerRoute(route, options?: {}) ``` - `route`: accept any properties of a React Router [Route](https://reactrouter.com/en/main/components/route) component with the addition of: - - `name`: An optional name for the route. - - `visibility`: An optional visibility indicator for the route. Accepted values are `"public"` or `"protected"`. + - `$name`: An optional name for the route. + - `$visibility`: An optional visibility indicator for the route. Accepted values are `"public"` or `"protected"`. - `options`: An optional object literal of options: - `hoist`: An optional boolean value to register the route at the root of the router. The default value is `false`. - `parentPath`: An optional path of a parent route to register this new route under. @@ -102,7 +102,7 @@ When registering a route, a hint can be provided, indicating if the route is int import { Page } from "./Page.tsx"; runtime.registerRoute({ - visibility: "public" + $visibility: "public" path: "/page-1", element: }); @@ -115,12 +115,12 @@ import { Layout } from "./Layout.tsx"; import { Page } from "./Page.tsx"; runtime.registerRoute({ - visibility: "public" + $visibility: "public" path: "/layout", element: , children: [ { - visibility: "public", + $visibility: "public", path: "/page-1", element: , } @@ -134,15 +134,15 @@ When no visibility hint is provided, a route is considered as a `protected` rout ### Register a named route -The `registerRoute` function accepts a `parentName` property, allowing a route to be [nested under an existing parent route](#register-nested-routes-under-an-existing-route). When searching for the parent route matching the `parentName` property, the `parentName` will be matched against the `name` property of every route. +The `registerRoute` function accepts a `parentName` property, allowing a route to be [nested under an existing parent route](#register-nested-routes-under-an-existing-route). When searching for the parent route matching the `parentName` property, the `parentName` will be matched against the `$name` property of every route. -> A `name` property should usually only be defined for routes that doesn't have a path like an error boundary or an authentication boundary. +> A `$name` property should only be defined for routes that doesn't have a path like an error boundary or an authentication boundary. ```tsx !#4 import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; runtime.registerRoute({ - name: "error-boundary", + $name: "error-boundary", element: }); ``` @@ -154,10 +154,10 @@ import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; import { RootLayout } from "./RootLayout.tsx"; runtime.registerRoute({ - name: "error-boundary", + $name: "error-boundary", element: , children: [ - name: "root-layout", + $name: "root-layout", element: ] }); @@ -220,20 +220,20 @@ runtime.registerNavigationItem(item, options?: {}) A Squide navigation item can either be a `NavigationLink` or a `NavigationSection`. Both types can be intertwined to create a multi-level menu hierarchy. A `NavigationSection` item is used to setup a new level while a `NavigationLink` define a link. - `NavigationSection` accept the following properties: - - `label`: The section text. + - `$label`: The section text. + - `$priority`: An order priority affecting the position of the item in the menu (higher first) + - `$addiltionalProps`: Additional properties to be forwarded to the section renderer. - `children`: The section content. - - `priority`: An order priority affecting the position of the item in the menu (higher first) - - `addiltionalProps`: Additional properties to be forwarded to the section renderer. - `NavigationLink` accept any properties of a React Router [Link](https://reactrouter.com/en/main/components/link) component with the addition of: - - `label`: The link text. - - `priority`: An order priority affecting the position of the item in the menu (higher first) - - `additionalProps`: Additional properties to be forwarded to the link renderer. + - `$label`: The link text. + - `$priority`: An order priority affecting the position of the item in the menu (higher first) + - `$additionalProps`: Additional properties to be forwarded to the link renderer. ```ts // Register a new navigation item from a local or remote module. runtime.registerNavigationItem({ - to: "/page-1", - label: "Page 1" + $label: "Page 1", + to: "/page-1" }); ``` @@ -250,99 +250,99 @@ runtime.registerNavigationItem({ // --- Nested Link // Link runtime.registerNavigationItem({ - label: "Section", + $label: "Section", children: [ { label: "Nested Section", children: [ { - to: "#", - label: "Nested Nested Link", + $label: "Nested Nested Link", + to: "#" } ] }, { - to: "#", - label: "Nested Link" + $label: "Nested Link", + to: "#" } ] }, { - to: "#", - label: "Link" + $label: "Link", + to: "#" }); ``` ### Sort registered navigation items -A `priority` property can be added to a navigation item to affect it's position in the menu. The sorting algorithm is as follow: +A `$priority` property can be added to a navigation item to affect it's position in the menu. The sorting algorithm is as follow: - By default a navigation item have a priority of `0`. - If no navigation item have a priority, the items are positioned according to their registration order. - If an item have a priority `> 0`, the item will be positioned before any other items with a lower priority (or without an explicit priority value). - If an item have a priority `< 0`, the item will be positioned after any other items with a higher priority (or without an explicit priority value). -```ts !#4,12 +```ts !#3,11 runtime.registerNavigationItem({ - to: "/about", - label: "About", - priority: 10 + $label: "About", + $priority: 10, + to: "/about" }); runtime.registerNavigationItem({ - to: "/home", - label: "Home", + $label: "Home", // Because the "Home" navigation item has an higher priority, it will be rendered // before the "About" navigation item. - priority: 100 + $priority: 100, + to: "/home" }); ``` ### Use a React element as navigation item label -```tsx !#5-8 +```tsx !#4-7 import { QuestionMarkIcon } from "@sample/icons"; runtime.registerNavigationItem({ - to: "/about", - label: ( + $label: ( About - ) + ), + to: "/about" }); ``` ### Style a navigation item -```ts !#4-6 +```ts !#3-5 runtime.registerNavigationItem({ - to: "/about", - label: "About", + $label: "About", style: { backgroundColor: "#000" - } + }, + to: "/about" }); ``` ### Open a navigation link in a new tab -```ts !#4 +```ts !#3 runtime.registerNavigationItem({ - to: "/about", - label: "About", - target: "_blank" + $label: "About", + target: "_blank", + to: "/about" }); ``` ### Render additional props on a navigation item -```ts !#4-6 +```ts !#3-5 runtime.registerNavigationItem({ - to: "/about", - label: "About", - additionalProps: { + $label: "About", + $additionalProps: { highlight: true - } + }, + to: "/about" }); ``` @@ -352,8 +352,8 @@ By default, every navigation item registered with the `registerNavigationItem` f ```tsx !#5 runtime.registerNavigationItem({ - to: "/layout/page-1", - label: "Page 1" + $label: "Page 1", + to: "/layout/page-1" }, { menuId: "my-custom-layout" }); From 28cbee9889e1fffc091b76e364d497c9259430f8 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Thu, 12 Oct 2023 15:06:06 -0400 Subject: [PATCH 20/51] Updated the docs --- docs/getting-started/create-host.md | 12 +++--------- docs/guides/add-authentication.md | 5 +---- docs/guides/develop-a-module-in-isolation.md | 17 ++++------------- docs/guides/isolate-module-failures.md | 5 +---- docs/guides/override-a-react-context.md | 16 +++++----------- docs/guides/override-the-host-layout.md | 10 ++-------- sample/shell/src/useAppRouter.tsx | 9 ++++----- 7 files changed, 20 insertions(+), 54 deletions(-) diff --git a/docs/getting-started/create-host.md b/docs/getting-started/create-host.md index 72b86606a..30b5a7e6c 100644 --- a/docs/getting-started/create-host.md +++ b/docs/getting-started/create-host.md @@ -80,8 +80,7 @@ export {}; Next, to register the modules, instanciate the shell [Runtime](/reference/runtime/runtime-class.md) and register the remote module with the [registerRemoteModules](/reference/registration/registerRemoteModules.md) function (the configuration of the remote module will be covered in the [next section](create-remote-module.md)): -```tsx !#14-16,19-21,24 host/src/bootstrap.tsx -import { Suspense } from "react"; +```tsx !#13-15,18-20,23 host/src/bootstrap.tsx import { createRoot } from "react-dom/client"; import { ConsoleLogger, RuntimeContext, Runtime } from "@squide/react-router"; import { registerRemoteModules, type RemoteDefinition } from "@squide/webpack-module-federation"; @@ -110,9 +109,7 @@ const root = createRoot(document.getElementById("root")!); root.render( - Loading...
}> - - + ); ``` @@ -158,10 +155,7 @@ export function App() { // Render the router. return ( - Loading...
} - /> + ); } ``` diff --git a/docs/guides/add-authentication.md b/docs/guides/add-authentication.md index a360e1bac..ca6d8babc 100644 --- a/docs/guides/add-authentication.md +++ b/docs/guides/add-authentication.md @@ -317,10 +317,7 @@ export function App() { } return ( - Loading...
} - /> + ); } ``` diff --git a/docs/guides/develop-a-module-in-isolation.md b/docs/guides/develop-a-module-in-isolation.md index 58a2045e8..7f5992d3b 100644 --- a/docs/guides/develop-a-module-in-isolation.md +++ b/docs/guides/develop-a-module-in-isolation.md @@ -127,10 +127,7 @@ export function App() { } return ( - Loading...
} - /> + ); } ``` @@ -169,8 +166,7 @@ remote-module The `index.tsx` file is similar to the `bootstrap.tsx` file of an host application but, tailored for an isolated module. The key distinction is that, since the project is set up for local development, the module is registered with the [registerLocalModules](/reference/registration/registerLocalModules.md) function instead of the [registerRemoteModules](/reference/registration/registerRemoteModules.md) function: -```tsx !#9-11,15 remote-module/src/index.tsx -import { Suspense } from "react"; +```tsx !#8-10,14 remote-module/src/index.tsx import { createRoot } from "react-dom/client"; import { ConsoleLogger, RuntimeContext, Runtime, registerLocalModules } from "@squide/react-router"; import { App } from "./App.tsx"; @@ -190,9 +186,7 @@ const root = createRoot(document.getElementById("root")!); root.render( - Loading...
}> - - + ); ``` @@ -217,10 +211,7 @@ export function App() { }); return ( - Loading...
} - /> + ); } ``` diff --git a/docs/guides/isolate-module-failures.md b/docs/guides/isolate-module-failures.md index 8b4ec5962..c1aaf5611 100644 --- a/docs/guides/isolate-module-failures.md +++ b/docs/guides/isolate-module-failures.md @@ -48,10 +48,7 @@ export function App() { } return ( - Loading...
} - /> + ); } ``` diff --git a/docs/guides/override-a-react-context.md b/docs/guides/override-a-react-context.md index d01fa6467..4a9520ef4 100644 --- a/docs/guides/override-a-react-context.md +++ b/docs/guides/override-a-react-context.md @@ -9,7 +9,7 @@ In a federated application using [Module Federation](https://webpack.js.org/conc Let's take a simple example using a `BackgroundColorContext`: -```tsx !#16-21 host/src/App.tsx +```tsx !#15-19 host/src/App.tsx import { useAppRouter } from "@sample/shell"; import { BackgroundColorContext } from "@sample/shared"; import { useAreModulesReady } from "@squide/webpack-module-federation"; @@ -26,10 +26,7 @@ export function App() { return ( - Loading...
} - /> + ); } @@ -88,7 +85,7 @@ export const register: ModuleRegisterFunction = runtime => { Since there are multiple routes to setup with the new provider, an utility function can be extracted: -```tsx !#6-12,18 remote-module/src/register.tsx +```tsx !#6-12,17 remote-module/src/register.tsx import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { BackgroundColorContext } from "@sample/shared"; import { ColoredPage } from "./ColoredPage.tsx"; @@ -114,7 +111,7 @@ export const register: ModuleRegisterFunction = runtime => { Let's consider a more specific use case where the host application declares a `ThemeContext` from Workleap's new design system, Hopper: -```tsx !#16-21 host/src/App.tsx +```tsx !#15-19 host/src/App.tsx import { useAppRouter } from "@sample/shell"; import { ThemeContext } from "@hopper/components"; import { useAreModulesReady } from "@squide/webpack-module-federation"; @@ -131,10 +128,7 @@ export function App() { return ( - Loading...} - /> + ); } diff --git a/docs/guides/override-the-host-layout.md b/docs/guides/override-the-host-layout.md index 671cbf7f6..83f9c935c 100644 --- a/docs/guides/override-the-host-layout.md +++ b/docs/guides/override-the-host-layout.md @@ -52,10 +52,7 @@ export function App() { } return ( - Loading...} - /> + ); } ``` @@ -158,10 +155,7 @@ export function App() { } return ( - Loading...} - /> + ); } ``` diff --git a/sample/shell/src/useAppRouter.tsx b/sample/shell/src/useAppRouter.tsx index f3a7a62f3..055f865f4 100644 --- a/sample/shell/src/useAppRouter.tsx +++ b/sample/shell/src/useAppRouter.tsx @@ -14,7 +14,9 @@ export function RootRoute() { telemetryService?.track(`Navigated to the "${location.pathname}" page.`); }, [location, telemetryService]); - return ; + return ( + + ); } export function useAppRouter(waitForMsw: boolean, sessionManager: SessionManager, telemetryService: TelemetryService) { @@ -105,10 +107,7 @@ export function useAppRouter(waitForMsw: boolean, sessionManager: SessionManager return ( - + ); From b697735084ed5e79d0cb5d01b0c75e31724f7ab5 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Thu, 12 Oct 2023 15:08:18 -0400 Subject: [PATCH 21/51] Updated docs --- docs/guides/override-a-react-context.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/guides/override-a-react-context.md b/docs/guides/override-a-react-context.md index 4a9520ef4..b05fd6ec4 100644 --- a/docs/guides/override-a-react-context.md +++ b/docs/guides/override-a-react-context.md @@ -161,12 +161,12 @@ As `@hopper/components` expose the `ThemeContext`, the context must be re-declar import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { ThemeContext } from "@hopper/components"; import { Page } from "./Page.tsx"; -import type { ReactElement } from "react"; +import type { ReactNode } from "react"; -function withHopperTheme(page: ReactElement) { +function Providers({ children }: { children: ReactNode }) { return ( - {page} + {children} ) } @@ -174,7 +174,7 @@ function withHopperTheme(page: ReactElement) { export const register: ModuleRegisterFunction = runtime => { runtime.registerRoute({ path: "/page", - element: withHopperTheme() + element: }); } ``` From aa9dc02c80022658ed0b44f88bf2b3ec0b4908b8 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Thu, 12 Oct 2023 23:03:30 -0400 Subject: [PATCH 22/51] Split the samples into 2 distinct applications --- CONTRIBUTING.md | 107 +- package.json | 24 +- pnpm-lock.yaml | 1556 ++++++++++------- pnpm-workspace.yaml | 2 +- sample/another-remote-module/tsconfig.json | 15 - sample/host/src/App.tsx | 18 - sample/host/tsconfig.json | 17 - sample/local-module/tsconfig.json | 13 - sample/remote-module/src/dev/App.tsx | 19 - sample/remote-module/tsconfig.json | 16 - sample/shared/tsconfig.json | 10 - sample/shell/src/index.ts | 11 - sample/shell/tsconfig.json | 11 - .../another-remote-module/.browserslistrc | 0 .../another-remote-module/.eslintrc.json | 0 .../basic}/another-remote-module/package.json | 9 +- .../another-remote-module}/public/index.html | 1 - .../another-remote-module/src/dev/App.tsx | 5 + .../src/dev/DevHomePage.tsx | 8 + .../another-remote-module}/src/dev/index.tsx | 2 +- .../src/dev/register.tsx | 19 + .../another-remote-module}/src/dev/session.ts | 4 +- .../another-remote-module/src/register.tsx | 6 +- .../basic}/another-remote-module/swc.build.js | 0 .../basic}/another-remote-module/swc.dev.js | 0 .../basic/another-remote-module/tsconfig.json | 16 + .../another-remote-module/webpack.build.js | 2 +- .../another-remote-module/webpack.dev.js | 5 +- .../basic}/host/.browserslistrc | 0 {sample => samples/basic}/host/.eslintrc.json | 0 {sample => samples/basic}/host/_redirects | 0 {sample => samples/basic}/host/nodemon.json | 0 samples/basic/host/package.json | 47 + .../basic/host}/public/favicon.png | Bin samples/basic/host/public/index.html | 9 + samples/basic/host/src/App.tsx | 12 + .../basic/host/src/HomePage.tsx | 4 +- samples/basic/host/src/bootstrap.tsx | 44 + {sample => samples/basic}/host/src/index.ts | 0 .../basic}/host/src/register.tsx | 2 +- samples/basic/host/src/session.ts | 9 + {sample => samples/basic}/host/swc.build.js | 0 {sample => samples/basic}/host/swc.dev.js | 0 samples/basic/host/tsconfig.json | 17 + samples/basic/host/webpack.build.js | 20 + samples/basic/host/webpack.dev.js | 17 + .../basic}/local-module/.browserslistrc | 0 .../basic}/local-module/.eslintrc.json | 0 .../basic}/local-module/nodemon.json | 0 .../basic}/local-module/nodemon.local.json | 0 .../basic}/local-module/package.json | 13 +- samples/basic/local-module/public/index.html | 8 + .../basic/local-module/src/AboutPage.tsx | 4 +- .../basic/local-module/src/MessagePage.tsx | 7 +- .../basic}/local-module/src/WorkleapTab.tsx | 2 +- samples/basic/local-module/src/dev/App.tsx | 5 + .../basic/local-module}/src/dev/DevHome.tsx | 0 samples/basic/local-module/src/dev/index.tsx | 29 + .../basic}/local-module/src/dev/register.tsx | 2 +- samples/basic/local-module/src/dev/session.ts | 9 + .../basic}/local-module/src/register.tsx | 6 +- .../basic}/local-module/swc.config.js | 0 samples/basic/local-module/tsconfig.json | 16 + .../basic}/local-module/tsup.build.ts | 0 .../basic}/local-module/tsup.dev.ts | 0 .../basic}/local-module/webpack.config.js | 3 +- .../basic}/remote-module/.browserslistrc | 0 .../basic}/remote-module/.eslintrc.json | 0 .../basic}/remote-module/nodemon.json | 0 samples/basic/remote-module/package.json | 45 + samples/basic/remote-module/public/index.html | 8 + .../basic}/remote-module/src/ColoredPage.tsx | 2 +- .../basic}/remote-module/src/CustomLayout.tsx | 0 .../basic/remote-module/src/HoistedPage.tsx | 4 +- .../remote-module/src/OfficevibeTab.tsx | 2 +- .../basic/remote-module/src/RemotePage.tsx | 4 +- .../basic}/remote-module/src/SkillsTab.tsx | 2 +- samples/basic/remote-module/src/dev/App.tsx | 13 + .../basic/remote-module}/src/dev/DevHome.tsx | 0 .../basic/remote-module}/src/dev/index.tsx | 2 +- .../basic}/remote-module/src/dev/register.tsx | 2 +- .../basic/remote-module/src/dev/session.ts | 9 + .../basic}/remote-module/src/register.tsx | 54 +- .../basic}/remote-module/swc.build.js | 0 .../basic}/remote-module/swc.dev.js | 0 samples/basic/remote-module/tsconfig.json | 16 + samples/basic/remote-module/webpack.build.js | 18 + samples/basic/remote-module/webpack.dev.js | 28 + .../basic}/shared/.eslintrc.json | 0 {sample => samples/basic}/shared/nodemon.json | 0 {sample => samples/basic}/shared/package.json | 3 +- .../shared/src/BackgroundColorContext.ts | 0 .../basic}/shared/src/FederatedTabsLayout.tsx | 2 +- .../basic}/shared/src/appContext.ts | 2 +- .../basic}/shared/src/eventBus.ts | 0 {sample => samples/basic}/shared/src/index.ts | 2 - .../basic}/shared/src/isNetlify.ts | 0 samples/basic/shared/src/session.ts | 11 + samples/basic/shared/tsconfig.json | 13 + .../basic}/shared/tsup.build.ts | 0 {sample => samples/basic}/shared/tsup.dev.ts | 0 .../basic}/shell/.eslintrc.json | 0 {sample => samples/basic}/shell/nodemon.json | 0 samples/basic/shell/package.json | 46 + .../basic/shell/src/AuthenticatedLayout.tsx | 87 + .../shell/src/AuthenticationBoundary.tsx | 8 + samples/basic/shell/src/LoginPage.tsx | 65 + .../basic/shell/src/LogoutPage.tsx | 4 +- .../basic}/shell/src/ModuleErrorBoundary.tsx | 2 + .../basic/shell/src/NoMatchPage.tsx | 4 +- .../basic}/shell/src/RootErrorBoundary.tsx | 0 .../basic}/shell/src/RootLayout.tsx | 0 samples/basic/shell/src/index.ts | 3 + samples/basic/shell/src/register.tsx | 95 + samples/basic/shell/src/useAppRouter.tsx | 23 + samples/basic/shell/tsconfig.json | 15 + {sample => samples/basic}/shell/tsup.build.ts | 0 {sample => samples/basic}/shell/tsup.dev.ts | 0 samples/endpoints/host/.browserslistrc | 1 + samples/endpoints/host/.eslintrc.json | 5 + samples/endpoints/host/_redirects | 1 + .../endpoints}/host/mocks/browser.ts | 0 .../endpoints/host/mocks/characterHandlers.ts | 8 +- samples/endpoints/host/nodemon.json | 5 + .../endpoints}/host/package.json | 14 +- .../endpoints}/host/public/favicon.png | Bin .../endpoints/host}/public/index.html | 0 .../host/public/mockServiceWorker.js | 0 .../endpoints/host/src}/App.tsx | 4 +- .../endpoints/host/src/HomePage.tsx | 12 +- .../endpoints}/host/src/bootstrap.tsx | 18 +- samples/endpoints/host/src/index.ts | 10 + samples/endpoints/host/src/register.tsx | 54 + .../endpoints}/host/src/session.ts | 2 +- samples/endpoints/host/swc.build.js | 7 + samples/endpoints/host/swc.dev.js | 7 + samples/endpoints/host/tsconfig.json | 18 + .../endpoints}/host/webpack.build.js | 2 +- .../endpoints}/host/webpack.dev.js | 2 +- .../endpoints/local-module/.browserslistrc | 1 + samples/endpoints/local-module/.eslintrc.json | 5 + .../endpoints/local-module}/mocks/browser.ts | 0 .../local-module/mocks/characterHandlers.ts | 60 + samples/endpoints/local-module/nodemon.json | 5 + .../endpoints/local-module/nodemon.local.json | 5 + samples/endpoints/local-module/package.json | 71 + .../local-module/public/favicon.png | Bin .../endpoints/local-module}/public/index.html | 0 .../local-module/src/CharactersTab.tsx | 37 + .../local-module/src/SubscriptionPage.tsx | 21 + .../endpoints/local-module}/src/dev/App.tsx | 4 +- .../local-module/src/dev/DevHomePage.tsx | 2 +- .../endpoints/local-module/src/dev/index.tsx | 40 + .../local-module}/src/dev/register.tsx | 4 +- .../local-module}/src/dev/session.ts | 2 +- .../endpoints/local-module/src/register.tsx | 94 + samples/endpoints/local-module/swc.config.js | 7 + samples/endpoints/local-module/tsconfig.json | 17 + samples/endpoints/local-module/tsup.build.ts | 3 + samples/endpoints/local-module/tsup.dev.ts | 3 + .../endpoints/local-module/webpack.config.js | 15 + .../endpoints/remote-module/.browserslistrc | 1 + .../endpoints/remote-module/.eslintrc.json | 5 + .../endpoints/remote-module/mocks/browser.ts | 7 + .../remote-module/mocks/episodeHandlers.ts | 72 + .../remote-module/mocks/locationHandlers.ts | 42 + samples/endpoints/remote-module/nodemon.json | 5 + .../endpoints}/remote-module/package.json | 17 +- .../remote-module/public/favicon.png | Bin .../remote-module}/public/index.html | 0 .../remote-module/public/mockServiceWorker.js | 0 .../remote-module/src/EpisodesTab.tsx | 37 + .../remote-module/src/FailingTab.tsx | 16 + .../remote-module/src/LocationsTab.tsx | 37 + .../endpoints/remote-module/src/Providers.tsx | 25 + .../endpoints/remote-module/src/dev/App.tsx | 13 + .../remote-module/src/dev/DevHomePage.tsx | 8 + .../remote-module/src/dev/index.tsx | 2 +- .../remote-module/src/dev/register.tsx | 26 + .../remote-module}/src/dev/session.ts | 2 +- .../endpoints/remote-module/src/register.tsx | 81 + samples/endpoints/remote-module/swc.build.js | 7 + samples/endpoints/remote-module/swc.dev.js | 7 + samples/endpoints/remote-module/tsconfig.json | 17 + .../endpoints}/remote-module/webpack.build.js | 2 +- .../endpoints}/remote-module/webpack.dev.js | 3 +- samples/endpoints/shared/.eslintrc.json | 5 + samples/endpoints/shared/nodemon.json | 5 + samples/endpoints/shared/package.json | 52 + .../shared/src/FederatedTabsLayout.tsx | 74 + samples/endpoints/shared/src/index.ts | 5 + samples/endpoints/shared/src/isNetlify.ts | 3 + .../endpoints}/shared/src/session.ts | 0 .../endpoints}/shared/src/subscription.ts | 2 + .../endpoints}/shared/src/telemetryService.ts | 0 samples/endpoints/shared/tsconfig.json | 14 + samples/endpoints/shared/tsup.build.ts | 3 + samples/endpoints/shared/tsup.dev.ts | 3 + samples/endpoints/shell/.eslintrc.json | 5 + .../shell/mocks/authenticationHandlers.ts | 8 +- .../endpoints}/shell/mocks/session.ts | 0 .../shell/mocks/subscriptionHandlers.ts | 4 +- samples/endpoints/shell/nodemon.json | 5 + .../endpoints}/shell/package.json | 7 +- .../shell/src/AuthenticatedLayout.tsx | 11 +- .../shell/src/AuthenticationBoundary.tsx | 2 + .../endpoints/shell/src/LoginPage.tsx | 6 +- samples/endpoints/shell/src/LogoutPage.tsx | 13 + .../shell/src/ModuleErrorBoundary.tsx | 37 + samples/endpoints/shell/src/NoMatchPage.tsx | 15 + .../endpoints/shell/src/RootErrorBoundary.tsx | 35 + samples/endpoints/shell/src/RootLayout.tsx | 9 + samples/endpoints/shell/src/index.ts | 3 + .../endpoints}/shell/src/register.tsx | 38 +- .../endpoints}/shell/src/useAppRouter.tsx | 8 +- samples/endpoints/shell/tsconfig.json | 16 + samples/endpoints/shell/tsup.build.ts | 3 + samples/endpoints/shell/tsup.dev.ts | 3 + 218 files changed, 3149 insertions(+), 1018 deletions(-) delete mode 100644 sample/another-remote-module/tsconfig.json delete mode 100644 sample/host/src/App.tsx delete mode 100644 sample/host/tsconfig.json delete mode 100644 sample/local-module/tsconfig.json delete mode 100644 sample/remote-module/src/dev/App.tsx delete mode 100644 sample/remote-module/tsconfig.json delete mode 100644 sample/shared/tsconfig.json delete mode 100644 sample/shell/src/index.ts delete mode 100644 sample/shell/tsconfig.json rename {sample => samples/basic}/another-remote-module/.browserslistrc (100%) rename {sample => samples/basic}/another-remote-module/.eslintrc.json (100%) rename {sample => samples/basic}/another-remote-module/package.json (90%) rename {sample/remote-module => samples/basic/another-remote-module}/public/index.html (56%) create mode 100644 samples/basic/another-remote-module/src/dev/App.tsx create mode 100644 samples/basic/another-remote-module/src/dev/DevHomePage.tsx rename {sample/local-module => samples/basic/another-remote-module}/src/dev/index.tsx (95%) create mode 100644 samples/basic/another-remote-module/src/dev/register.tsx rename {sample/remote-module => samples/basic/another-remote-module}/src/dev/session.ts (77%) rename {sample => samples/basic}/another-remote-module/src/register.tsx (73%) rename {sample => samples/basic}/another-remote-module/swc.build.js (100%) rename {sample => samples/basic}/another-remote-module/swc.dev.js (100%) create mode 100644 samples/basic/another-remote-module/tsconfig.json rename {sample => samples/basic}/another-remote-module/webpack.build.js (95%) rename {sample => samples/basic}/another-remote-module/webpack.dev.js (85%) rename {sample => samples/basic}/host/.browserslistrc (100%) rename {sample => samples/basic}/host/.eslintrc.json (100%) rename {sample => samples/basic}/host/_redirects (100%) rename {sample => samples/basic}/host/nodemon.json (100%) create mode 100644 samples/basic/host/package.json rename {sample/another-remote-module => samples/basic/host}/public/favicon.png (100%) create mode 100644 samples/basic/host/public/index.html create mode 100644 samples/basic/host/src/App.tsx rename sample/host/src/Home.tsx => samples/basic/host/src/HomePage.tsx (62%) create mode 100644 samples/basic/host/src/bootstrap.tsx rename {sample => samples/basic}/host/src/index.ts (100%) rename {sample => samples/basic}/host/src/register.tsx (82%) create mode 100644 samples/basic/host/src/session.ts rename {sample => samples/basic}/host/swc.build.js (100%) rename {sample => samples/basic}/host/swc.dev.js (100%) create mode 100644 samples/basic/host/tsconfig.json create mode 100644 samples/basic/host/webpack.build.js create mode 100644 samples/basic/host/webpack.dev.js rename {sample => samples/basic}/local-module/.browserslistrc (100%) rename {sample => samples/basic}/local-module/.eslintrc.json (100%) rename {sample => samples/basic}/local-module/nodemon.json (100%) rename {sample => samples/basic}/local-module/nodemon.local.json (100%) rename {sample => samples/basic}/local-module/package.json (87%) create mode 100644 samples/basic/local-module/public/index.html rename sample/local-module/src/About.tsx => samples/basic/local-module/src/AboutPage.tsx (75%) rename sample/local-module/src/Message.tsx => samples/basic/local-module/src/MessagePage.tsx (87%) rename {sample => samples/basic}/local-module/src/WorkleapTab.tsx (66%) create mode 100644 samples/basic/local-module/src/dev/App.tsx rename {sample/another-remote-module => samples/basic/local-module}/src/dev/DevHome.tsx (100%) create mode 100644 samples/basic/local-module/src/dev/index.tsx rename {sample => samples/basic}/local-module/src/dev/register.tsx (86%) create mode 100644 samples/basic/local-module/src/dev/session.ts rename {sample => samples/basic}/local-module/src/register.tsx (89%) rename {sample => samples/basic}/local-module/swc.config.js (100%) create mode 100644 samples/basic/local-module/tsconfig.json rename {sample => samples/basic}/local-module/tsup.build.ts (100%) rename {sample => samples/basic}/local-module/tsup.dev.ts (100%) rename {sample => samples/basic}/local-module/webpack.config.js (74%) rename {sample => samples/basic}/remote-module/.browserslistrc (100%) rename {sample => samples/basic}/remote-module/.eslintrc.json (100%) rename {sample => samples/basic}/remote-module/nodemon.json (100%) create mode 100644 samples/basic/remote-module/package.json create mode 100644 samples/basic/remote-module/public/index.html rename {sample => samples/basic}/remote-module/src/ColoredPage.tsx (82%) rename {sample => samples/basic}/remote-module/src/CustomLayout.tsx (100%) rename sample/remote-module/src/Hoisted.tsx => samples/basic/remote-module/src/HoistedPage.tsx (83%) rename {sample => samples/basic}/remote-module/src/OfficevibeTab.tsx (88%) rename sample/remote-module/src/Remote.tsx => samples/basic/remote-module/src/RemotePage.tsx (76%) rename {sample => samples/basic}/remote-module/src/SkillsTab.tsx (65%) create mode 100644 samples/basic/remote-module/src/dev/App.tsx rename {sample/local-module => samples/basic/remote-module}/src/dev/DevHome.tsx (100%) rename {sample/another-remote-module => samples/basic/remote-module}/src/dev/index.tsx (95%) rename {sample => samples/basic}/remote-module/src/dev/register.tsx (86%) create mode 100644 samples/basic/remote-module/src/dev/session.ts rename {sample => samples/basic}/remote-module/src/register.tsx (63%) rename {sample => samples/basic}/remote-module/swc.build.js (100%) rename {sample => samples/basic}/remote-module/swc.dev.js (100%) create mode 100644 samples/basic/remote-module/tsconfig.json create mode 100644 samples/basic/remote-module/webpack.build.js create mode 100644 samples/basic/remote-module/webpack.dev.js rename {sample => samples/basic}/shared/.eslintrc.json (100%) rename {sample => samples/basic}/shared/nodemon.json (100%) rename {sample => samples/basic}/shared/package.json (95%) rename {sample => samples/basic}/shared/src/BackgroundColorContext.ts (100%) rename {sample => samples/basic}/shared/src/FederatedTabsLayout.tsx (96%) rename {sample => samples/basic}/shared/src/appContext.ts (62%) rename {sample => samples/basic}/shared/src/eventBus.ts (100%) rename {sample => samples/basic}/shared/src/index.ts (69%) rename {sample => samples/basic}/shared/src/isNetlify.ts (100%) create mode 100644 samples/basic/shared/src/session.ts create mode 100644 samples/basic/shared/tsconfig.json rename {sample => samples/basic}/shared/tsup.build.ts (100%) rename {sample => samples/basic}/shared/tsup.dev.ts (100%) rename {sample => samples/basic}/shell/.eslintrc.json (100%) rename {sample => samples/basic}/shell/nodemon.json (100%) create mode 100644 samples/basic/shell/package.json create mode 100644 samples/basic/shell/src/AuthenticatedLayout.tsx create mode 100644 samples/basic/shell/src/AuthenticationBoundary.tsx create mode 100644 samples/basic/shell/src/LoginPage.tsx rename sample/shell/src/Logout.tsx => samples/basic/shell/src/LogoutPage.tsx (77%) rename {sample => samples/basic}/shell/src/ModuleErrorBoundary.tsx (95%) rename sample/shell/src/NoMatch.tsx => samples/basic/shell/src/NoMatchPage.tsx (71%) rename {sample => samples/basic}/shell/src/RootErrorBoundary.tsx (100%) rename {sample => samples/basic}/shell/src/RootLayout.tsx (100%) create mode 100644 samples/basic/shell/src/index.ts create mode 100644 samples/basic/shell/src/register.tsx create mode 100644 samples/basic/shell/src/useAppRouter.tsx create mode 100644 samples/basic/shell/tsconfig.json rename {sample => samples/basic}/shell/tsup.build.ts (100%) rename {sample => samples/basic}/shell/tsup.dev.ts (100%) create mode 100644 samples/endpoints/host/.browserslistrc create mode 100644 samples/endpoints/host/.eslintrc.json create mode 100644 samples/endpoints/host/_redirects rename {sample => samples/endpoints}/host/mocks/browser.ts (100%) rename sample/remote-module/mocks/handlers.ts => samples/endpoints/host/mocks/characterHandlers.ts (99%) create mode 100644 samples/endpoints/host/nodemon.json rename {sample => samples/endpoints}/host/package.json (84%) rename {sample => samples/endpoints}/host/public/favicon.png (100%) rename {sample/another-remote-module => samples/endpoints/host}/public/index.html (100%) rename {sample => samples/endpoints}/host/public/mockServiceWorker.js (100%) rename {sample/local-module/src/dev => samples/endpoints/host/src}/App.tsx (74%) rename sample/remote-module/src/Fetch.tsx => samples/endpoints/host/src/HomePage.tsx (74%) rename {sample => samples/endpoints}/host/src/bootstrap.tsx (77%) create mode 100644 samples/endpoints/host/src/index.ts create mode 100644 samples/endpoints/host/src/register.tsx rename {sample => samples/endpoints}/host/src/session.ts (90%) create mode 100644 samples/endpoints/host/swc.build.js create mode 100644 samples/endpoints/host/swc.dev.js create mode 100644 samples/endpoints/host/tsconfig.json rename {sample => samples/endpoints}/host/webpack.build.js (95%) rename {sample => samples/endpoints}/host/webpack.dev.js (93%) create mode 100644 samples/endpoints/local-module/.browserslistrc create mode 100644 samples/endpoints/local-module/.eslintrc.json rename {sample/remote-module => samples/endpoints/local-module}/mocks/browser.ts (100%) create mode 100644 samples/endpoints/local-module/mocks/characterHandlers.ts create mode 100644 samples/endpoints/local-module/nodemon.json create mode 100644 samples/endpoints/local-module/nodemon.local.json create mode 100644 samples/endpoints/local-module/package.json rename {sample => samples/endpoints}/local-module/public/favicon.png (100%) rename {sample/host => samples/endpoints/local-module}/public/index.html (100%) create mode 100644 samples/endpoints/local-module/src/CharactersTab.tsx create mode 100644 samples/endpoints/local-module/src/SubscriptionPage.tsx rename {sample/another-remote-module => samples/endpoints/local-module}/src/dev/App.tsx (74%) rename sample/remote-module/src/dev/DevHome.tsx => samples/endpoints/local-module/src/dev/DevHomePage.tsx (80%) create mode 100644 samples/endpoints/local-module/src/dev/index.tsx rename {sample/another-remote-module => samples/endpoints/local-module}/src/dev/register.tsx (72%) rename {sample/another-remote-module => samples/endpoints/local-module}/src/dev/session.ts (86%) create mode 100644 samples/endpoints/local-module/src/register.tsx create mode 100644 samples/endpoints/local-module/swc.config.js create mode 100644 samples/endpoints/local-module/tsconfig.json create mode 100644 samples/endpoints/local-module/tsup.build.ts create mode 100644 samples/endpoints/local-module/tsup.dev.ts create mode 100644 samples/endpoints/local-module/webpack.config.js create mode 100644 samples/endpoints/remote-module/.browserslistrc create mode 100644 samples/endpoints/remote-module/.eslintrc.json create mode 100644 samples/endpoints/remote-module/mocks/browser.ts create mode 100644 samples/endpoints/remote-module/mocks/episodeHandlers.ts create mode 100644 samples/endpoints/remote-module/mocks/locationHandlers.ts create mode 100644 samples/endpoints/remote-module/nodemon.json rename {sample => samples/endpoints}/remote-module/package.json (86%) rename {sample => samples/endpoints}/remote-module/public/favicon.png (100%) rename {sample/local-module => samples/endpoints/remote-module}/public/index.html (100%) rename {sample => samples/endpoints}/remote-module/public/mockServiceWorker.js (100%) create mode 100644 samples/endpoints/remote-module/src/EpisodesTab.tsx create mode 100644 samples/endpoints/remote-module/src/FailingTab.tsx create mode 100644 samples/endpoints/remote-module/src/LocationsTab.tsx create mode 100644 samples/endpoints/remote-module/src/Providers.tsx create mode 100644 samples/endpoints/remote-module/src/dev/App.tsx create mode 100644 samples/endpoints/remote-module/src/dev/DevHomePage.tsx rename {sample => samples/endpoints}/remote-module/src/dev/index.tsx (96%) create mode 100644 samples/endpoints/remote-module/src/dev/register.tsx rename {sample/local-module => samples/endpoints/remote-module}/src/dev/session.ts (86%) create mode 100644 samples/endpoints/remote-module/src/register.tsx create mode 100644 samples/endpoints/remote-module/swc.build.js create mode 100644 samples/endpoints/remote-module/swc.dev.js create mode 100644 samples/endpoints/remote-module/tsconfig.json rename {sample => samples/endpoints}/remote-module/webpack.build.js (95%) rename {sample => samples/endpoints}/remote-module/webpack.dev.js (90%) create mode 100644 samples/endpoints/shared/.eslintrc.json create mode 100644 samples/endpoints/shared/nodemon.json create mode 100644 samples/endpoints/shared/package.json create mode 100644 samples/endpoints/shared/src/FederatedTabsLayout.tsx create mode 100644 samples/endpoints/shared/src/index.ts create mode 100644 samples/endpoints/shared/src/isNetlify.ts rename {sample => samples/endpoints}/shared/src/session.ts (100%) rename {sample => samples/endpoints}/shared/src/subscription.ts (94%) rename {sample => samples/endpoints}/shared/src/telemetryService.ts (100%) create mode 100644 samples/endpoints/shared/tsconfig.json create mode 100644 samples/endpoints/shared/tsup.build.ts create mode 100644 samples/endpoints/shared/tsup.dev.ts create mode 100644 samples/endpoints/shell/.eslintrc.json rename {sample => samples/endpoints}/shell/mocks/authenticationHandlers.ts (84%) rename {sample => samples/endpoints}/shell/mocks/session.ts (100%) rename {sample => samples/endpoints}/shell/mocks/subscriptionHandlers.ts (75%) create mode 100644 samples/endpoints/shell/nodemon.json rename {sample => samples/endpoints}/shell/package.json (90%) rename {sample => samples/endpoints}/shell/src/AuthenticatedLayout.tsx (90%) rename {sample => samples/endpoints}/shell/src/AuthenticationBoundary.tsx (88%) rename sample/shell/src/Login.tsx => samples/endpoints/shell/src/LoginPage.tsx (96%) create mode 100644 samples/endpoints/shell/src/LogoutPage.tsx create mode 100644 samples/endpoints/shell/src/ModuleErrorBoundary.tsx create mode 100644 samples/endpoints/shell/src/NoMatchPage.tsx create mode 100644 samples/endpoints/shell/src/RootErrorBoundary.tsx create mode 100644 samples/endpoints/shell/src/RootLayout.tsx create mode 100644 samples/endpoints/shell/src/index.ts rename {sample => samples/endpoints}/shell/src/register.tsx (72%) rename {sample => samples/endpoints}/shell/src/useAppRouter.tsx (94%) create mode 100644 samples/endpoints/shell/tsconfig.json create mode 100644 samples/endpoints/shell/tsup.build.ts create mode 100644 samples/endpoints/shell/tsup.dev.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 17ef48611..2105966b2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ The following documentation is only for the maintainers of this repository. - [Installation](#installation) - [Develop the shell packages](#develop-the-shell-packages) - [Release the packages](#release-the-packages) -- [Deploy the sample application](#deploy-the-sample-application) +- [Deploy the sample applications](#deploy-the-sample-applications) - [Available commands](#commands) - [CI](#ci) - [Add a new package to the monorepo](#add-a-new-package-to-the-monorepo) @@ -22,7 +22,7 @@ The main difference to account for is that the `devDependencies` must now be ins ## Project overview -This project is split into two major sections, [packages/](packages/) and [sample/](sample/). +This project is split into two major sections, [packages/](packages/) and [samples/](samples/). ### Packages @@ -34,17 +34,18 @@ Under [packages/](packages/) are the actual packages composing the federated app [@squide/webpack-module-federation](packages/webpack-module-federation/) is module federation implementation for [webpack](https://webpack.js.org/concepts/module-federation/). This implementation is offered as a standalone package because not all application configurations will require module federation and the shell could eventually support alternative module federation application like [Rspack](https://www.rspack.dev/). +[@squide/msw](packages/msw/) is a package including helpers to configure [Mock Service Worker](https://mswjs.io/) for a federated application. + [@squide/fakes](packages/fakes/) is a collection of fake implementations to facilitate the development of federated modules in isolation. -### Sample +### Samples -Under [sample/](sample/) is a sample application to test the federation application shell functionalities while developing. +Under [samples/](samples/) are samples application to test the Squide functionalities while developing. -In this sample application would find: +You'll find two samples: -- [An host application](sample/host/): http://locahost:8080 -- [A static module](sample/static-module/) -- [A remote module](sample/remote-module/): http://localhost:8081 +- `basic`: A sample application showcasing the basic features or Squide. +- `endpoints`: A more complexe sample application showcasing the different usecases related to data fetching and management. ## Installation @@ -78,13 +79,21 @@ With the first terminal, execute the following script: pnpm dev ``` -With the second terminal, execute the following script: +With the second terminal, execute either the following script: + +```bash +pnpm dev-basic +``` + +or ```bash -pnpm dev-sample +pnpm dev-endpoints ``` -You can then open your favorite browser and navigate to `http://localhost:8080/` to get a live preview of your code. To test that the remote module is working correctly, navigate to `http://localhost:8081/remoteEntry.js` +You can then open your favorite browser and navigate to `http://localhost:8080/` to get a live preview of your code. + +> To test that the remote module is working correctly, navigate to `http://localhost:8081/remoteEntry.js` ## Release the packages @@ -118,21 +127,43 @@ By default, packages compilation output will be in their respective *dist* direc If you got linting error, most of the time, they can be fixed automatically using `eslint . --fix`, if not, follow the report provided by `pnpm lint`. -## Deploy the sample application +## Deploy the sample applications + +The sample applications . For each application multiples sites are available. + +### The "basic" sample + +The sites for this sample application are hosted on [Netlify](https://www.netlify.com/): + +- [host](https://squide-host.netlify.app/) +- [remote-module](https://squide-remote-module.netlify.app) +- [another-remote-module](https://squide-another-remote-module.netlify.app) + +To deploy the sample application, open a terminal at the root of the repository and execute the following script: + +```bash +deploy-basic +``` + +A prompt with a few questions will appear and then site will automatically be deployed to production. + +### The sample with "endpoints" -The sample application is hosted on [Netlify](https://www.netlify.com/). 2 sites are available, one for the host application (https://squide-host.netlify.app/) and one for the remote module application (https://squide-remote-module.netlify.app). +The sites for this sample application are hosted on [Netlify](https://www.netlify.com/): -To deploy both websites, open a terminal at the root of the repository and execute the following script: +- TBD + +To deploy the sample application, open a terminal at the root of the repository and execute the following script: ```bash -deploy-sample +deploy-endpoints ``` -It will automatically deploy both sites to production. +A prompt with a few questions will appear and then site will automatically be deployed to production. ## Commands -From the project root, you have access to many commands the main ones are: +From the project root, you have access to many commands. The most important ones are: ### dev @@ -142,12 +173,20 @@ Build the shell packages for development and start the watch processes. pnpm dev ``` -### dev-sample +### dev-basic + +Build the sample "basic" application for development and start the dev servers. + +```bash +pnpm dev-basic +``` + +### dev-endpoints -Build the sample application for development and start the dev servers. +Build the sample "application with "endpoints" for development and start the dev servers. ```bash -pnpm dev-sample +pnpm dev-endpoints ``` ### dev-docs @@ -166,20 +205,36 @@ Build the packages for release. pnpm build ``` -### build-sample +### build-basic + +Build the sample "basic" application for release. + +```bash +pnpm build-basic +``` + +### build-endpoints + +Build the sample application with "endpoints" for release. + +```bash +pnpm build-endpoints +``` + +### serve-basic -Build the sample application for release. +Build the sample "basic" application for deployment and start a local web server to serve the application. ```bash -pnpm build-sample +pnpm serve-basic ``` -### serve-sample +### serve-endpoints -Build the sample application for deployment and start a local web server to serve the application. +Build the sample application with "endpoints" for deployment and start a local web server to serve the application. ```bash -pnpm serve-sample +pnpm serve-endpoints ``` ### dev-docs diff --git a/package.json b/package.json index 343f547bf..cbe252d98 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ }, "scripts": { "postinstall": "pnpm -r --parallel --include-workspace-root exec pnpm dlx rimraf node_modules/.cache && pnpm temporary-script-until-build-order-topology-is-fixed", - "temporary-script-until-build-order-topology-is-fixed": "pnpm build && pnpm build-sample", + "temporary-script-until-build-order-topology-is-fixed": "pnpm build && pnpm build-basic && pnpm build-endpoints", "dev": "pnpm --filter \"./packages/*\" -r --parallel dev", "build": "pnpm --filter \"./packages/*\" -r --parallel build", "test": "jest", @@ -22,13 +22,19 @@ "clean": "pnpm -r --parallel --include-workspace-root exec pnpm dlx rimraf dist node_modules/.cache", "reset": "pnpm clean && pnpm reset:modules", "reset:modules": "pnpm -r --parallel --include-workspace-root exec pnpm dlx rimraf node_modules pnpm-lock.yaml", - "dev-sample": "pnpm --filter \"./sample/*\" -r --parallel dev", - "build-sample": "pnpm --filter \"./sample/*\" -r --parallel build", - "serve-sample": "pnpm --filter \"./sample/*\" -r --parallel serve-build", - "deploy-sample": "cross-env NETLIFY=true pnpm build-sample && pnpm run deploy-sample:host && pnpm run deploy-sample:remote-module && pnpm run deploy-sample:another-remote-module", - "deploy-sample:host": "netlify deploy --dir=sample/host/dist --site=ae684cea-e6b1-4293-95d6-fc82462654c8 --prod", - "deploy-sample:remote-module": "netlify deploy --dir=sample/remote-module/dist --site=43234f6e-a884-410e-9b4d-f290459f841f --prod", - "deploy-sample:another-remote-module": "netlify deploy --dir=sample/another-remote-module/dist --site=2673b626-74aa-4c03-8a8f-8c794e90fd07 --prod", + "dev-basic": "pnpm --filter \"./samples/basic/*\" -r --parallel dev", + "build-basic": "pnpm --filter \"./samples/basic/*\" -r --parallel build", + "serve-basic": "pnpm --filter \"./samples/basic/*\" -r --parallel serve-build", + "deploy-basic": "cross-env NETLIFY=true pnpm build-basic && pnpm run deploy-basic:host && pnpm run deploy-basic:remote-module && pnpm run deploy-basic:another-remote-module", + "deploy-basic:host": "netlify deploy --dir=samples/basic/host/dist --site=ae684cea-e6b1-4293-95d6-fc82462654c8 --prod", + "deploy-basic:remote-module": "netlify deploy --dir=samples/basic/remote-module/dist --site=43234f6e-a884-410e-9b4d-f290459f841f --prod", + "deploy-basic:another-remote-module": "netlify deploy --dir=samples/basic/another-remote-module/dist --site=2673b626-74aa-4c03-8a8f-8c794e90fd07 --prod", + "dev-endpoints": "pnpm --filter \"./samples/endpoints/*\" -r --parallel dev", + "build-endpoints": "pnpm --filter \"./samples/endpoints/*\" -r --parallel build", + "serve-endpoints": "pnpm --filter \"./samples/endpoints/*\" -r --parallel serve-build", + "deploy-endpoints": "cross-env NETLIFY=true pnpm build-endpoints && pnpm run deploy-endpoints:host && pnpm run deploy-endpoints:remote-module", + "deploy-endpoints:host": "netlify deploy --dir=samples/endpoints/host/dist --site=TBD --prod", + "deploy-endpoints:remote-module": "netlify deploy --dir=samples/endpoints/remote-module/dist --site=TBD --prod", "dev-docs": "retype start", "list-outdated-deps": "pnpm outdated -r --format list", "update-outdated-deps": "pnpm update -r --latest" @@ -42,7 +48,7 @@ "cross-env": "7.0.3", "eslint": "8.51.0", "jest": "29.7.0", - "netlify-cli": "16.6.1", + "netlify-cli": "16.7.0", "retypeapp": "3.5.0", "ts-node": "10.9.1", "typescript": "5.2.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 11e492350..789f9258d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,8 +33,8 @@ importers: specifier: 29.7.0 version: 29.7.0(@types/node@20.8.4)(ts-node@10.9.1) netlify-cli: - specifier: 16.6.1 - version: 16.6.1(@types/node@20.8.4) + specifier: 16.7.0 + version: 16.7.0(@types/node@20.8.4) retypeapp: specifier: 3.5.0 version: 3.5.0 @@ -212,7 +212,7 @@ importers: version: 18.2.0(react@18.2.0) ts-jest: specifier: 29.1.1 - version: 29.1.1(@babel/core@7.23.0)(esbuild@0.18.20)(jest@29.7.0)(typescript@5.2.2) + version: 29.1.1(@babel/core@7.23.2)(esbuild@0.18.20)(jest@29.7.0)(typescript@5.2.2) tsup: specifier: 7.2.0 version: 7.2.0(@swc/core@1.3.92)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) @@ -279,7 +279,7 @@ importers: version: 18.2.0(react@18.2.0) ts-jest: specifier: 29.1.1 - version: 29.1.1(@babel/core@7.23.0)(esbuild@0.18.20)(jest@29.7.0)(typescript@5.2.2) + version: 29.1.1(@babel/core@7.23.2)(esbuild@0.18.20)(jest@29.7.0)(typescript@5.2.2) tsup: specifier: 7.2.0 version: 7.2.0(@swc/core@1.3.92)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) @@ -290,20 +290,23 @@ importers: specifier: 5.88.2 version: 5.88.2(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4) - sample/another-remote-module: + samples/basic/another-remote-module: dependencies: - '@sample/shared': + '@basic/shared': specifier: workspace:* version: link:../shared - '@sample/shell': + '@basic/shell': specifier: workspace:* version: link:../shell + '@squide/fakes': + specifier: workspace:* + version: link:../../../packages/fakes '@squide/react-router': specifier: workspace:* - version: link:../../packages/react-router + version: link:../../../packages/react-router '@squide/webpack-module-federation': specifier: workspace:* - version: link:../../packages/webpack-module-federation + version: link:../../../packages/webpack-module-federation react: specifier: 18.2.0 version: 18.2.0 @@ -314,9 +317,252 @@ importers: specifier: 6.16.0 version: 6.16.0(react-dom@18.2.0)(react@18.2.0) devDependencies: + '@swc/core': + specifier: 1.3.92 + version: 1.3.92(@swc/helpers@0.5.3) + '@swc/helpers': + specifier: 0.5.3 + version: 0.5.3 + '@types/react': + specifier: 18.2.28 + version: 18.2.28 + '@types/react-dom': + specifier: 18.2.13 + version: 18.2.13 + '@types/webpack': + specifier: 5.28.3 + version: 5.28.3(@swc/core@1.3.92)(webpack-cli@5.1.4) + '@workleap/browserslist-config': + specifier: 2.0.0 + version: 2.0.0 + '@workleap/eslint-plugin': + specifier: 3.0.0 + version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) + '@workleap/swc-configs': + specifier: 2.1.2 + version: 2.1.2(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@swc/jest@0.2.29)(browserslist@4.22.1) + '@workleap/typescript-configs': + specifier: 3.0.2 + version: 3.0.2(typescript@5.2.2) + '@workleap/webpack-configs': + specifier: 1.1.0 + version: 1.1.0(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@types/webpack@5.28.3)(browserslist@4.22.1)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) + browserslist: + specifier: 4.22.1 + version: 4.22.1 + cross-env: + specifier: 7.0.3 + version: 7.0.3 + http-server: + specifier: 14.1.1 + version: 14.1.1 + typescript: + specifier: 5.2.2 + version: 5.2.2 + webpack: + specifier: 5.88.2 + version: 5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4) + webpack-cli: + specifier: 5.1.4 + version: 5.1.4(webpack-dev-server@4.15.1)(webpack@5.88.2) + webpack-dev-server: + specifier: 4.15.1 + version: 4.15.1(webpack-cli@5.1.4)(webpack@5.88.2) + + samples/basic/host: + dependencies: + '@basic/local-module': + specifier: workspace:* + version: link:../local-module + '@basic/shared': + specifier: workspace:* + version: link:../shared + '@basic/shell': + specifier: workspace:* + version: link:../shell + '@squide/react-router': + specifier: workspace:* + version: link:../../../packages/react-router + '@squide/webpack-module-federation': + specifier: workspace:* + version: link:../../../packages/webpack-module-federation + react: + specifier: 18.2.0 + version: 18.2.0 + react-dom: + specifier: 18.2.0 + version: 18.2.0(react@18.2.0) + react-router-dom: + specifier: 6.16.0 + version: 6.16.0(react-dom@18.2.0)(react@18.2.0) + devDependencies: + '@squide/fakes': + specifier: workspace:* + version: link:../../../packages/fakes + '@swc/core': + specifier: 1.3.92 + version: 1.3.92(@swc/helpers@0.5.3) + '@swc/helpers': + specifier: 0.5.3 + version: 0.5.3 + '@types/react': + specifier: 18.2.28 + version: 18.2.28 + '@types/react-dom': + specifier: 18.2.13 + version: 18.2.13 + '@types/webpack': + specifier: 5.28.3 + version: 5.28.3(@swc/core@1.3.92)(webpack-cli@5.1.4) + '@workleap/browserslist-config': + specifier: 2.0.0 + version: 2.0.0 + '@workleap/eslint-plugin': + specifier: 3.0.0 + version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) + '@workleap/swc-configs': + specifier: 2.1.2 + version: 2.1.2(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@swc/jest@0.2.29)(browserslist@4.22.1) + '@workleap/typescript-configs': + specifier: 3.0.2 + version: 3.0.2(typescript@5.2.2) + '@workleap/webpack-configs': + specifier: 1.1.0 + version: 1.1.0(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@types/webpack@5.28.3)(browserslist@4.22.1)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) + browserslist: + specifier: 4.22.1 + version: 4.22.1 + copyfiles: + specifier: 2.4.1 + version: 2.4.1 + http-server: + specifier: 14.1.1 + version: 14.1.1 + nodemon: + specifier: 3.0.1 + version: 3.0.1 + typescript: + specifier: 5.2.2 + version: 5.2.2 + webpack: + specifier: 5.88.2 + version: 5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4) + webpack-cli: + specifier: 5.1.4 + version: 5.1.4(webpack-dev-server@4.15.1)(webpack@5.88.2) + webpack-dev-server: + specifier: 4.15.1 + version: 4.15.1(webpack-cli@5.1.4)(webpack@5.88.2) + + samples/basic/local-module: + dependencies: + '@basic/shared': + specifier: workspace:* + version: link:../shared + '@basic/shell': + specifier: workspace:* + version: link:../shell + '@squide/fakes': + specifier: workspace:* + version: link:../../../packages/fakes + '@squide/react-router': + specifier: workspace:* + version: link:../../../packages/react-router + react: + specifier: 18.2.0 + version: 18.2.0 + react-dom: + specifier: 18.2.0 + version: 18.2.0(react@18.2.0) + react-router-dom: + specifier: 6.16.0 + version: 6.16.0(react-dom@18.2.0)(react@18.2.0) + devDependencies: + '@swc/core': + specifier: 1.3.92 + version: 1.3.92(@swc/helpers@0.5.3) + '@swc/helpers': + specifier: 0.5.3 + version: 0.5.3 + '@types/react': + specifier: 18.2.28 + version: 18.2.28 + '@types/react-dom': + specifier: 18.2.13 + version: 18.2.13 + '@types/webpack': + specifier: 5.28.3 + version: 5.28.3(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4) + '@workleap/browserslist-config': + specifier: 2.0.0 + version: 2.0.0 + '@workleap/eslint-plugin': + specifier: 3.0.0 + version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) + '@workleap/swc-configs': + specifier: 2.1.2 + version: 2.1.2(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@swc/jest@0.2.29)(browserslist@4.22.1) + '@workleap/tsup-configs': + specifier: 3.0.1 + version: 3.0.1(tsup@7.2.0)(typescript@5.2.2) + '@workleap/typescript-configs': + specifier: 3.0.2 + version: 3.0.2(typescript@5.2.2) + '@workleap/webpack-configs': + specifier: 1.1.0 + version: 1.1.0(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@types/webpack@5.28.3)(browserslist@4.22.1)(esbuild@0.18.20)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) + browserslist: + specifier: 4.22.1 + version: 4.22.1 + cross-env: + specifier: 7.0.3 + version: 7.0.3 + nodemon: + specifier: 3.0.1 + version: 3.0.1 + tsup: + specifier: 7.2.0 + version: 7.2.0(@swc/core@1.3.92)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) + typescript: + specifier: 5.2.2 + version: 5.2.2 + webpack: + specifier: 5.88.2 + version: 5.88.2(@swc/core@1.3.92)(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-cli: + specifier: 5.1.4 + version: 5.1.4(webpack-dev-server@4.15.1)(webpack@5.88.2) + webpack-dev-server: + specifier: 4.15.1 + version: 4.15.1(webpack-cli@5.1.4)(webpack@5.88.2) + + samples/basic/remote-module: + dependencies: + '@basic/shared': + specifier: workspace:* + version: link:../shared + '@basic/shell': + specifier: workspace:* + version: link:../shell '@squide/fakes': specifier: workspace:* - version: link:../../packages/fakes + version: link:../../../packages/fakes + '@squide/react-router': + specifier: workspace:* + version: link:../../../packages/react-router + '@squide/webpack-module-federation': + specifier: workspace:* + version: link:../../../packages/webpack-module-federation + react: + specifier: 18.2.0 + version: 18.2.0 + react-dom: + specifier: 18.2.0 + version: 18.2.0(react@18.2.0) + react-router-dom: + specifier: 6.16.0 + version: 6.16.0(react-dom@18.2.0)(react@18.2.0) + devDependencies: '@swc/core': specifier: 1.3.92 version: 1.3.92(@swc/helpers@0.5.3) @@ -329,69 +575,159 @@ importers: '@types/react-dom': specifier: 18.2.13 version: 18.2.13 - '@types/webpack': - specifier: 5.28.3 - version: 5.28.3(@swc/core@1.3.92)(webpack-cli@5.1.4) - '@workleap/browserslist-config': - specifier: 2.0.0 - version: 2.0.0 + '@types/webpack': + specifier: 5.28.3 + version: 5.28.3(@swc/core@1.3.92)(webpack-cli@5.1.4) + '@workleap/browserslist-config': + specifier: 2.0.0 + version: 2.0.0 + '@workleap/eslint-plugin': + specifier: 3.0.0 + version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) + '@workleap/swc-configs': + specifier: 2.1.2 + version: 2.1.2(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@swc/jest@0.2.29)(browserslist@4.22.1) + '@workleap/typescript-configs': + specifier: 3.0.2 + version: 3.0.2(typescript@5.2.2) + '@workleap/webpack-configs': + specifier: 1.1.0 + version: 1.1.0(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@types/webpack@5.28.3)(browserslist@4.22.1)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) + browserslist: + specifier: 4.22.1 + version: 4.22.1 + cross-env: + specifier: 7.0.3 + version: 7.0.3 + http-server: + specifier: 14.1.1 + version: 14.1.1 + nodemon: + specifier: 3.0.1 + version: 3.0.1 + typescript: + specifier: 5.2.2 + version: 5.2.2 + webpack: + specifier: 5.88.2 + version: 5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4) + webpack-cli: + specifier: 5.1.4 + version: 5.1.4(webpack-dev-server@4.15.1)(webpack@5.88.2) + webpack-dev-server: + specifier: 4.15.1 + version: 4.15.1(webpack-cli@5.1.4)(webpack@5.88.2) + + samples/basic/shared: + devDependencies: + '@remix-run/router': + specifier: 1.9.0 + version: 1.9.0 + '@squide/react-router': + specifier: workspace:* + version: link:../../../packages/react-router + '@types/react': + specifier: 18.2.28 + version: 18.2.28 + '@types/react-dom': + specifier: 18.2.13 + version: 18.2.13 + '@workleap/eslint-plugin': + specifier: 3.0.0 + version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) + '@workleap/tsup-configs': + specifier: 3.0.1 + version: 3.0.1(tsup@7.2.0)(typescript@5.2.2) + '@workleap/typescript-configs': + specifier: 3.0.2 + version: 3.0.2(typescript@5.2.2) + nodemon: + specifier: 3.0.1 + version: 3.0.1 + react: + specifier: 18.2.0 + version: 18.2.0 + react-dom: + specifier: 18.2.0 + version: 18.2.0(react@18.2.0) + react-router-dom: + specifier: 6.16.0 + version: 6.16.0(react-dom@18.2.0)(react@18.2.0) + tsup: + specifier: 7.2.0 + version: 7.2.0(@swc/core@1.3.92)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) + typescript: + specifier: 5.2.2 + version: 5.2.2 + + samples/basic/shell: + devDependencies: + '@basic/shared': + specifier: workspace:* + version: link:../shared + '@remix-run/router': + specifier: 1.9.0 + version: 1.9.0 + '@squide/react-router': + specifier: workspace:* + version: link:../../../packages/react-router + '@squide/webpack-module-federation': + specifier: 1.0.5 + version: link:../../../packages/webpack-module-federation + '@types/react': + specifier: 18.2.28 + version: 18.2.28 + '@types/react-dom': + specifier: 18.2.13 + version: 18.2.13 '@workleap/eslint-plugin': specifier: 3.0.0 version: 3.0.0(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(jest@29.7.0)(typescript@5.2.2) - '@workleap/swc-configs': - specifier: 2.1.2 - version: 2.1.2(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@swc/jest@0.2.29)(browserslist@4.22.1) + '@workleap/tsup-configs': + specifier: 3.0.1 + version: 3.0.1(tsup@7.2.0)(typescript@5.2.2) '@workleap/typescript-configs': specifier: 3.0.2 version: 3.0.2(typescript@5.2.2) - '@workleap/webpack-configs': - specifier: 1.1.0 - version: 1.1.0(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@types/webpack@5.28.3)(browserslist@4.22.1)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) - axios: - specifier: 1.5.1 - version: 1.5.1(debug@4.3.4) - browserslist: - specifier: 4.22.1 - version: 4.22.1 - cross-env: - specifier: 7.0.3 - version: 7.0.3 - http-server: - specifier: 14.1.1 - version: 14.1.1 + nodemon: + specifier: 3.0.1 + version: 3.0.1 + react: + specifier: 18.2.0 + version: 18.2.0 + react-dom: + specifier: 18.2.0 + version: 18.2.0(react@18.2.0) + react-router-dom: + specifier: 6.16.0 + version: 6.16.0(react-dom@18.2.0)(react@18.2.0) + tsup: + specifier: 7.2.0 + version: 7.2.0(@swc/core@1.3.92)(postcss@8.4.31)(ts-node@10.9.1)(typescript@5.2.2) typescript: specifier: 5.2.2 version: 5.2.2 - webpack: - specifier: 5.88.2 - version: 5.88.2(@swc/core@1.3.92)(webpack-cli@5.1.4) - webpack-cli: - specifier: 5.1.4 - version: 5.1.4(webpack-dev-server@4.15.1)(webpack@5.88.2) - webpack-dev-server: - specifier: 4.15.1 - version: 4.15.1(webpack-cli@5.1.4)(webpack@5.88.2) - sample/host: + samples/endpoints/host: dependencies: - '@sample/local-module': + '@endpoints/local-module': specifier: workspace:* version: link:../local-module - '@sample/shared': + '@endpoints/shared': specifier: workspace:* version: link:../shared - '@sample/shell': + '@endpoints/shell': specifier: workspace:* version: link:../shell '@squide/msw': specifier: workspace:* - version: link:../../packages/msw + version: link:../../../packages/msw '@squide/react-router': specifier: workspace:* - version: link:../../packages/react-router + version: link:../../../packages/react-router '@squide/webpack-module-federation': specifier: workspace:* - version: link:../../packages/webpack-module-federation + version: link:../../../packages/webpack-module-federation axios: specifier: 1.5.1 version: 1.5.1(debug@4.3.4) @@ -401,19 +737,25 @@ importers: react-dom: specifier: 18.2.0 version: 18.2.0(react@18.2.0) + react-error-boundary: + specifier: 4.0.11 + version: 4.0.11(react@18.2.0) react-router-dom: specifier: 6.16.0 version: 6.16.0(react-dom@18.2.0)(react@18.2.0) devDependencies: '@squide/fakes': specifier: workspace:* - version: link:../../packages/fakes + version: link:../../../packages/fakes '@swc/core': specifier: 1.3.92 version: 1.3.92(@swc/helpers@0.5.3) '@swc/helpers': specifier: 0.5.3 version: 0.5.3 + '@tanstack/react-query': + specifier: rc + version: 5.0.0-rc.12(react-dom@18.2.0)(react@18.2.0) '@types/react': specifier: 18.2.28 version: 18.2.28 @@ -469,36 +811,54 @@ importers: specifier: 4.15.1 version: 4.15.1(webpack-cli@5.1.4)(webpack@5.88.2) - sample/local-module: + samples/endpoints/local-module: dependencies: - '@sample/shared': + '@endpoints/shared': specifier: workspace:* version: link:../shared - '@sample/shell': + '@endpoints/shell': specifier: workspace:* version: link:../shell + '@squide/fakes': + specifier: workspace:* + version: link:../../../packages/fakes + '@squide/msw': + specifier: workspace:* + version: link:../../../packages/msw '@squide/react-router': specifier: workspace:* - version: link:../../packages/react-router + version: link:../../../packages/react-router + '@tanstack/react-query': + specifier: rc + version: 5.0.0-rc.12(react-dom@18.2.0)(react@18.2.0) + axios: + specifier: 1.5.1 + version: 1.5.1(debug@4.3.4) + msw: + specifier: 1.3.2 + version: 1.3.2(typescript@5.2.2) react: specifier: 18.2.0 version: 18.2.0 react-dom: specifier: 18.2.0 version: 18.2.0(react@18.2.0) + react-error-boundary: + specifier: 4.0.11 + version: 4.0.11(react@18.2.0) react-router-dom: specifier: 6.16.0 version: 6.16.0(react-dom@18.2.0)(react@18.2.0) devDependencies: - '@squide/fakes': - specifier: workspace:* - version: link:../../packages/fakes '@swc/core': specifier: 1.3.92 version: 1.3.92(@swc/helpers@0.5.3) '@swc/helpers': specifier: 0.5.3 version: 0.5.3 + '@tanstack/react-query-devtools': + specifier: rc + version: 5.0.0-rc.12(@tanstack/react-query@5.0.0-rc.12)(react-dom@18.2.0)(react@18.2.0) '@types/react': specifier: 18.2.28 version: 18.2.28 @@ -526,9 +886,6 @@ importers: '@workleap/webpack-configs': specifier: 1.1.0 version: 1.1.0(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@types/webpack@5.28.3)(browserslist@4.22.1)(esbuild@0.18.20)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) - axios: - specifier: 1.5.1 - version: 1.5.1(debug@4.3.4) browserslist: specifier: 4.22.1 version: 4.22.1 @@ -554,39 +911,48 @@ importers: specifier: 4.15.1 version: 4.15.1(webpack-cli@5.1.4)(webpack@5.88.2) - sample/remote-module: + samples/endpoints/remote-module: dependencies: - '@sample/shared': + '@endpoints/shared': specifier: workspace:* version: link:../shared - '@sample/shell': + '@endpoints/shell': specifier: workspace:* version: link:../shell + '@squide/fakes': + specifier: workspace:* + version: link:../../../packages/fakes '@squide/msw': specifier: workspace:* - version: link:../../packages/msw + version: link:../../../packages/msw '@squide/react-router': specifier: workspace:* - version: link:../../packages/react-router + version: link:../../../packages/react-router '@squide/webpack-module-federation': specifier: workspace:* - version: link:../../packages/webpack-module-federation + version: link:../../../packages/webpack-module-federation '@tanstack/react-query': specifier: rc - version: 5.0.0-rc.7(react-dom@18.2.0)(react@18.2.0) + version: 5.0.0-rc.12(react-dom@18.2.0)(react@18.2.0) + axios: + specifier: 1.5.1 + version: 1.5.1(debug@4.3.4) + msw: + specifier: 1.3.2 + version: 1.3.2(typescript@5.2.2) react: specifier: 18.2.0 version: 18.2.0 react-dom: specifier: 18.2.0 version: 18.2.0(react@18.2.0) + react-error-boundary: + specifier: 4.0.11 + version: 4.0.11(react@18.2.0) react-router-dom: specifier: 6.16.0 version: 6.16.0(react-dom@18.2.0)(react@18.2.0) devDependencies: - '@squide/fakes': - specifier: workspace:* - version: link:../../packages/fakes '@swc/core': specifier: 1.3.92 version: 1.3.92(@swc/helpers@0.5.3) @@ -595,7 +961,7 @@ importers: version: 0.5.3 '@tanstack/react-query-devtools': specifier: rc - version: 5.0.0-rc.10(@tanstack/react-query@5.0.0-rc.7)(react-dom@18.2.0)(react@18.2.0) + version: 5.0.0-rc.12(@tanstack/react-query@5.0.0-rc.12)(react-dom@18.2.0)(react@18.2.0) '@types/react': specifier: 18.2.28 version: 18.2.28 @@ -620,9 +986,6 @@ importers: '@workleap/webpack-configs': specifier: 1.1.0 version: 1.1.0(@swc/core@1.3.92)(@swc/helpers@0.5.3)(@types/webpack@5.28.3)(browserslist@4.22.1)(postcss@8.4.31)(react-refresh@0.14.0)(typescript@5.2.2)(webpack-dev-server@4.15.1)(webpack@5.88.2) - axios: - specifier: 1.5.1 - version: 1.5.1(debug@4.3.4) browserslist: specifier: 4.22.1 version: 4.22.1 @@ -632,9 +995,6 @@ importers: http-server: specifier: 14.1.1 version: 14.1.1 - msw: - specifier: 1.3.2 - version: 1.3.2(typescript@5.2.2) nodemon: specifier: 3.0.1 version: 3.0.1 @@ -651,14 +1011,17 @@ importers: specifier: 4.15.1 version: 4.15.1(webpack-cli@5.1.4)(webpack@5.88.2) - sample/shared: + samples/endpoints/shared: devDependencies: '@remix-run/router': specifier: 1.9.0 version: 1.9.0 '@squide/react-router': specifier: workspace:* - version: link:../../packages/react-router + version: link:../../../packages/react-router + '@tanstack/react-query': + specifier: 4.36.1 + version: 4.36.1(react-dom@18.2.0)(react@18.2.0) '@types/react': specifier: 18.2.28 version: 18.2.28 @@ -683,6 +1046,9 @@ importers: react-dom: specifier: 18.2.0 version: 18.2.0(react@18.2.0) + react-error-boundary: + specifier: 4.0.11 + version: 4.0.11(react@18.2.0) react-router-dom: specifier: 6.16.0 version: 6.16.0(react-dom@18.2.0)(react@18.2.0) @@ -693,26 +1059,26 @@ importers: specifier: 5.2.2 version: 5.2.2 - sample/shell: + samples/endpoints/shell: devDependencies: + '@endpoints/shared': + specifier: workspace:* + version: link:../shared '@remix-run/router': specifier: 1.9.0 version: 1.9.0 - '@sample/shared': - specifier: workspace:* - version: link:../shared '@squide/fakes': specifier: workspace:* - version: link:../../packages/fakes + version: link:../../../packages/fakes '@squide/msw': specifier: workspace:* - version: link:../../packages/msw + version: link:../../../packages/msw '@squide/react-router': specifier: workspace:* - version: link:../../packages/react-router + version: link:../../../packages/react-router '@squide/webpack-module-federation': specifier: workspace:* - version: link:../../packages/webpack-module-federation + version: link:../../../packages/webpack-module-federation '@types/react': specifier: 18.2.28 version: 18.2.28 @@ -743,6 +1109,9 @@ importers: react-dom: specifier: 18.2.0 version: 18.2.0(react@18.2.0) + react-error-boundary: + specifier: 4.0.11 + version: 4.0.11(react@18.2.0) react-router-dom: specifier: 6.16.0 version: 6.16.0(react-dom@18.2.0)(react@18.2.0) @@ -781,15 +1150,15 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/core@7.23.0: - resolution: {integrity: sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==} + /@babel/core@7.23.2: + resolution: {integrity: sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==} engines: {node: '>=6.9.0'} dependencies: '@ampproject/remapping': 2.2.1 '@babel/code-frame': 7.22.13 '@babel/generator': 7.23.0 '@babel/helper-compilation-targets': 7.22.15 - '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) '@babel/helpers': 7.23.2 '@babel/parser': 7.23.0 '@babel/template': 7.22.15 @@ -839,42 +1208,42 @@ packages: semver: 6.3.1 dev: true - /@babel/helper-create-class-features-plugin@7.22.15(@babel/core@7.23.0): + /@babel/helper-create-class-features-plugin@7.22.15(@babel/core@7.23.2): resolution: {integrity: sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.0) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.2) '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 semver: 6.3.1 dev: true - /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.23.0): + /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.23.2): resolution: {integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-annotate-as-pure': 7.22.5 regexpu-core: 5.3.2 semver: 6.3.1 dev: true - /@babel/helper-define-polyfill-provider@0.4.3(@babel/core@7.23.0): + /@babel/helper-define-polyfill-provider@0.4.3(@babel/core@7.23.2): resolution: {integrity: sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 debug: 4.3.4(supports-color@9.4.0) @@ -918,13 +1287,13 @@ packages: '@babel/types': 7.23.0 dev: true - /@babel/helper-module-transforms@7.23.0(@babel/core@7.23.0): + /@babel/helper-module-transforms@7.23.0(@babel/core@7.23.2): resolution: {integrity: sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-module-imports': 7.22.15 '@babel/helper-simple-access': 7.22.5 @@ -944,25 +1313,25 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.23.0): + /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.23.2): resolution: {integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-wrap-function': 7.22.20 dev: true - /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.0): + /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.2): resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 @@ -1038,967 +1407,967 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.22.19 dev: true - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.22.15(@babel/core@7.23.0): + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.22.15(@babel/core@7.23.2): resolution: {integrity: sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.22.15(@babel/core@7.23.0): + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.22.15(@babel/core@7.23.2): resolution: {integrity: sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-transform-optional-chaining': 7.23.0(@babel/core@7.23.0) + '@babel/plugin-transform-optional-chaining': 7.23.0(@babel/core@7.23.2) dev: true - /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.0): + /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.2): resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 dev: true - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.0): + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.2): resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.0): + /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.2): resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.0): + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.2): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.23.0): + /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.23.2): resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.23.0): + /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.23.2): resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.23.0): + /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.23.2): resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-import-assertions@7.22.5(@babel/core@7.23.0): + /@babel/plugin-syntax-import-assertions@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-import-attributes@7.22.5(@babel/core@7.23.0): + /@babel/plugin-syntax-import-attributes@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.0): + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.2): resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.0): + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.2): resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.23.0): + /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.0): + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.2): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.0): + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.2): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.0): + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.2): resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.0): + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.2): resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.0): + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.2): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.0): + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.2): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.23.0): + /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.23.2): resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.0): + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.2): resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.23.0): + /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.23.0): + /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.23.2): resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.0 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-arrow-functions@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-arrow-functions@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-async-generator-functions@7.23.2(@babel/core@7.23.0): + /@babel/plugin-transform-async-generator-functions@7.23.2(@babel/core@7.23.2): resolution: {integrity: sha512-BBYVGxbDVHfoeXbOwcagAkOQAm9NxoTdMGfTqghu1GrvadSaw6iW3Je6IcL5PNOw8VwjxqBECXy50/iCQSY/lQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.0) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.0) + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.2) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-async-to-generator@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-async-to-generator@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-module-imports': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.0) + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-block-scoped-functions@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-block-scoped-functions@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-block-scoping@7.23.0(@babel/core@7.23.0): + /@babel/plugin-transform-block-scoping@7.23.0(@babel/core@7.23.2): resolution: {integrity: sha512-cOsrbmIOXmf+5YbL99/S49Y3j46k/T16b9ml8bm9lP6N9US5iQ2yBK7gpui1pg0V/WMcXdkfKbTb7HXq9u+v4g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-class-properties@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-class-properties@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-class-static-block@7.22.11(@babel/core@7.23.0): + /@babel/plugin-transform-class-static-block@7.22.11(@babel/core@7.23.2): resolution: {integrity: sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 dependencies: - '@babel/core': 7.23.0 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.0) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-classes@7.22.15(@babel/core@7.23.0): + /@babel/plugin-transform-classes@7.22.15(@babel/core@7.23.2): resolution: {integrity: sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.0) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.2) '@babel/helper-split-export-declaration': 7.22.6 globals: 11.12.0 dev: true - /@babel/plugin-transform-computed-properties@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-computed-properties@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 '@babel/template': 7.22.15 dev: true - /@babel/plugin-transform-destructuring@7.23.0(@babel/core@7.23.0): + /@babel/plugin-transform-destructuring@7.23.0(@babel/core@7.23.2): resolution: {integrity: sha512-vaMdgNXFkYrB+8lbgniSYWHsgqK5gjaMNcc84bMIOMRLH0L9AqYq3hwMdvnyqj1OPqea8UtjPEuS/DCenah1wg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-dotall-regex@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-dotall-regex@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-duplicate-keys@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-duplicate-keys@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-dynamic-import@7.22.11(@babel/core@7.23.0): + /@babel/plugin-transform-dynamic-import@7.22.11(@babel/core@7.23.2): resolution: {integrity: sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-exponentiation-operator@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-exponentiation-operator@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-builder-binary-assignment-operator-visitor': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-export-namespace-from@7.22.11(@babel/core@7.23.0): + /@babel/plugin-transform-export-namespace-from@7.22.11(@babel/core@7.23.2): resolution: {integrity: sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-for-of@7.22.15(@babel/core@7.23.0): + /@babel/plugin-transform-for-of@7.22.15(@babel/core@7.23.2): resolution: {integrity: sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-function-name@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-function-name@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-function-name': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-json-strings@7.22.11(@babel/core@7.23.0): + /@babel/plugin-transform-json-strings@7.22.11(@babel/core@7.23.2): resolution: {integrity: sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-literals@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-literals@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-logical-assignment-operators@7.22.11(@babel/core@7.23.0): + /@babel/plugin-transform-logical-assignment-operators@7.22.11(@babel/core@7.23.2): resolution: {integrity: sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-member-expression-literals@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-member-expression-literals@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-modules-amd@7.23.0(@babel/core@7.23.0): + /@babel/plugin-transform-modules-amd@7.23.0(@babel/core@7.23.2): resolution: {integrity: sha512-xWT5gefv2HGSm4QHtgc1sYPbseOyf+FFDo2JbpE25GWl5BqTGO9IMwTYJRoIdjsF85GE+VegHxSCUt5EvoYTAw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 - '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-modules-commonjs@7.23.0(@babel/core@7.23.0): + /@babel/plugin-transform-modules-commonjs@7.23.0(@babel/core@7.23.2): resolution: {integrity: sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 - '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-simple-access': 7.22.5 dev: true - /@babel/plugin-transform-modules-systemjs@7.23.0(@babel/core@7.23.0): + /@babel/plugin-transform-modules-systemjs@7.23.0(@babel/core@7.23.2): resolution: {integrity: sha512-qBej6ctXZD2f+DhlOC9yO47yEYgUh5CZNz/aBoH4j/3NOlRfJXJbY7xDQCqQVf9KbrqGzIWER1f23doHGrIHFg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-identifier': 7.22.20 dev: true - /@babel/plugin-transform-modules-umd@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-modules-umd@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 - '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.0 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-new-target@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-new-target@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-nullish-coalescing-operator@7.22.11(@babel/core@7.23.0): + /@babel/plugin-transform-nullish-coalescing-operator@7.22.11(@babel/core@7.23.2): resolution: {integrity: sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-numeric-separator@7.22.11(@babel/core@7.23.0): + /@babel/plugin-transform-numeric-separator@7.22.11(@babel/core@7.23.2): resolution: {integrity: sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-object-rest-spread@7.22.15(@babel/core@7.23.0): + /@babel/plugin-transform-object-rest-spread@7.22.15(@babel/core@7.23.2): resolution: {integrity: sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/compat-data': 7.23.2 - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-transform-parameters': 7.22.15(@babel/core@7.23.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-transform-parameters': 7.22.15(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-object-super@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-object-super@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.0) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-optional-catch-binding@7.22.11(@babel/core@7.23.0): + /@babel/plugin-transform-optional-catch-binding@7.22.11(@babel/core@7.23.2): resolution: {integrity: sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-optional-chaining@7.23.0(@babel/core@7.23.0): + /@babel/plugin-transform-optional-chaining@7.23.0(@babel/core@7.23.2): resolution: {integrity: sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-parameters@7.22.15(@babel/core@7.23.0): + /@babel/plugin-transform-parameters@7.22.15(@babel/core@7.23.2): resolution: {integrity: sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-private-methods@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-private-methods@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-private-property-in-object@7.22.11(@babel/core@7.23.0): + /@babel/plugin-transform-private-property-in-object@7.22.11(@babel/core@7.23.2): resolution: {integrity: sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.0) + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.0) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-property-literals@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-property-literals@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-react-constant-elements@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-react-constant-elements@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-BF5SXoO+nX3h5OhlN78XbbDrBOffv+AxPP2ENaJOVqjWCgBDeOY3WcaUcddutGSfoap+5NEQ/q/4I3WZIvgkXA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-react-display-name@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-react-display-name@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 - '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-react-jsx@7.22.15(@babel/core@7.23.0): + /@babel/plugin-transform-react-jsx@7.22.15(@babel/core@7.23.2): resolution: {integrity: sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-module-imports': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.2) '@babel/types': 7.23.0 dev: true - /@babel/plugin-transform-react-pure-annotations@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-react-pure-annotations@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-gP4k85wx09q+brArVinTXhWiyzLl9UpmGva0+mWyKxk6JZequ05x3eUcIUE+FyttPKJFRRVtAvQaJ6YF9h1ZpA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-regenerator@7.22.10(@babel/core@7.23.0): + /@babel/plugin-transform-regenerator@7.22.10(@babel/core@7.23.2): resolution: {integrity: sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 regenerator-transform: 0.15.2 dev: true - /@babel/plugin-transform-reserved-words@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-reserved-words@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-shorthand-properties@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-shorthand-properties@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-spread@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-spread@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 dev: true - /@babel/plugin-transform-sticky-regex@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-sticky-regex@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-template-literals@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-template-literals@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-typeof-symbol@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-typeof-symbol@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-typescript@7.22.15(@babel/core@7.23.0): + /@babel/plugin-transform-typescript@7.22.15(@babel/core@7.23.2): resolution: {integrity: sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.0) + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.23.2) dev: true - /@babel/plugin-transform-unicode-escapes@7.22.10(@babel/core@7.23.0): + /@babel/plugin-transform-unicode-escapes@7.22.10(@babel/core@7.23.2): resolution: {integrity: sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-unicode-property-regex@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-unicode-property-regex@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-unicode-regex@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-unicode-regex@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-unicode-sets-regex@7.22.5(@babel/core@7.23.0): + /@babel/plugin-transform-unicode-sets-regex@7.22.5(@babel/core@7.23.2): resolution: {integrity: sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.0 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/preset-env@7.23.2(@babel/core@7.23.0): + /@babel/preset-env@7.23.2(@babel/core@7.23.2): resolution: {integrity: sha512-BW3gsuDD+rvHL2VO2SjAUNTBe5YrjsTiDyqamPDWY723na3/yPQ65X5oQkFVJZ0o50/2d+svm1rkPoJeR1KxVQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/compat-data': 7.23.2 - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-option': 7.22.15 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.22.15(@babel/core@7.23.0) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.22.15(@babel/core@7.23.0) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.0) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.0) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.0) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.0) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-import-assertions': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-syntax-import-attributes': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.0) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.0) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.0) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.0) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.23.0) - '@babel/plugin-transform-arrow-functions': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-async-generator-functions': 7.23.2(@babel/core@7.23.0) - '@babel/plugin-transform-async-to-generator': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-block-scoped-functions': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-block-scoping': 7.23.0(@babel/core@7.23.0) - '@babel/plugin-transform-class-properties': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-class-static-block': 7.22.11(@babel/core@7.23.0) - '@babel/plugin-transform-classes': 7.22.15(@babel/core@7.23.0) - '@babel/plugin-transform-computed-properties': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-destructuring': 7.23.0(@babel/core@7.23.0) - '@babel/plugin-transform-dotall-regex': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-duplicate-keys': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-dynamic-import': 7.22.11(@babel/core@7.23.0) - '@babel/plugin-transform-exponentiation-operator': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-export-namespace-from': 7.22.11(@babel/core@7.23.0) - '@babel/plugin-transform-for-of': 7.22.15(@babel/core@7.23.0) - '@babel/plugin-transform-function-name': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-json-strings': 7.22.11(@babel/core@7.23.0) - '@babel/plugin-transform-literals': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-logical-assignment-operators': 7.22.11(@babel/core@7.23.0) - '@babel/plugin-transform-member-expression-literals': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-modules-amd': 7.23.0(@babel/core@7.23.0) - '@babel/plugin-transform-modules-commonjs': 7.23.0(@babel/core@7.23.0) - '@babel/plugin-transform-modules-systemjs': 7.23.0(@babel/core@7.23.0) - '@babel/plugin-transform-modules-umd': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-new-target': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.22.11(@babel/core@7.23.0) - '@babel/plugin-transform-numeric-separator': 7.22.11(@babel/core@7.23.0) - '@babel/plugin-transform-object-rest-spread': 7.22.15(@babel/core@7.23.0) - '@babel/plugin-transform-object-super': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-optional-catch-binding': 7.22.11(@babel/core@7.23.0) - '@babel/plugin-transform-optional-chaining': 7.23.0(@babel/core@7.23.0) - '@babel/plugin-transform-parameters': 7.22.15(@babel/core@7.23.0) - '@babel/plugin-transform-private-methods': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-private-property-in-object': 7.22.11(@babel/core@7.23.0) - '@babel/plugin-transform-property-literals': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-regenerator': 7.22.10(@babel/core@7.23.0) - '@babel/plugin-transform-reserved-words': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-shorthand-properties': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-spread': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-sticky-regex': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-template-literals': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-typeof-symbol': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-unicode-escapes': 7.22.10(@babel/core@7.23.0) - '@babel/plugin-transform-unicode-property-regex': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-unicode-regex': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-unicode-sets-regex': 7.22.5(@babel/core@7.23.0) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.23.0) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.22.15(@babel/core@7.23.2) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.22.15(@babel/core@7.23.2) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.2) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.2) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.2) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.2) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-import-assertions': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-syntax-import-attributes': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.2) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.2) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.2) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.2) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.2) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.23.2) + '@babel/plugin-transform-arrow-functions': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-async-generator-functions': 7.23.2(@babel/core@7.23.2) + '@babel/plugin-transform-async-to-generator': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-block-scoped-functions': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-block-scoping': 7.23.0(@babel/core@7.23.2) + '@babel/plugin-transform-class-properties': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-class-static-block': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-classes': 7.22.15(@babel/core@7.23.2) + '@babel/plugin-transform-computed-properties': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-destructuring': 7.23.0(@babel/core@7.23.2) + '@babel/plugin-transform-dotall-regex': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-duplicate-keys': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-dynamic-import': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-exponentiation-operator': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-export-namespace-from': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-for-of': 7.22.15(@babel/core@7.23.2) + '@babel/plugin-transform-function-name': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-json-strings': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-literals': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-logical-assignment-operators': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-member-expression-literals': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-modules-amd': 7.23.0(@babel/core@7.23.2) + '@babel/plugin-transform-modules-commonjs': 7.23.0(@babel/core@7.23.2) + '@babel/plugin-transform-modules-systemjs': 7.23.0(@babel/core@7.23.2) + '@babel/plugin-transform-modules-umd': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-new-target': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-nullish-coalescing-operator': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-numeric-separator': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-object-rest-spread': 7.22.15(@babel/core@7.23.2) + '@babel/plugin-transform-object-super': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-optional-catch-binding': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-optional-chaining': 7.23.0(@babel/core@7.23.2) + '@babel/plugin-transform-parameters': 7.22.15(@babel/core@7.23.2) + '@babel/plugin-transform-private-methods': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-private-property-in-object': 7.22.11(@babel/core@7.23.2) + '@babel/plugin-transform-property-literals': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-regenerator': 7.22.10(@babel/core@7.23.2) + '@babel/plugin-transform-reserved-words': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-shorthand-properties': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-spread': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-sticky-regex': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-template-literals': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-typeof-symbol': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-unicode-escapes': 7.22.10(@babel/core@7.23.2) + '@babel/plugin-transform-unicode-property-regex': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-unicode-regex': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-unicode-sets-regex': 7.22.5(@babel/core@7.23.2) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.23.2) '@babel/types': 7.23.0 - babel-plugin-polyfill-corejs2: 0.4.6(@babel/core@7.23.0) - babel-plugin-polyfill-corejs3: 0.8.5(@babel/core@7.23.0) - babel-plugin-polyfill-regenerator: 0.5.3(@babel/core@7.23.0) + babel-plugin-polyfill-corejs2: 0.4.6(@babel/core@7.23.2) + babel-plugin-polyfill-corejs3: 0.8.5(@babel/core@7.23.2) + babel-plugin-polyfill-regenerator: 0.5.3(@babel/core@7.23.2) core-js-compat: 3.33.0 semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.23.0): + /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.23.2): resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 '@babel/types': 7.23.0 esutils: 2.0.3 dev: true - /@babel/preset-react@7.22.15(@babel/core@7.23.0): + /@babel/preset-react@7.22.15(@babel/core@7.23.2): resolution: {integrity: sha512-Csy1IJ2uEh/PecCBXXoZGAZBeCATTuePzCSB7dLYWS0vOEj6CNpjxIhW4duWwZodBNueH7QO14WbGn8YyeuN9w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-option': 7.22.15 - '@babel/plugin-transform-react-display-name': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.23.0) - '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-react-pure-annotations': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-transform-react-display-name': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.23.2) + '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-react-pure-annotations': 7.22.5(@babel/core@7.23.2) dev: true - /@babel/preset-typescript@7.23.2(@babel/core@7.23.0): + /@babel/preset-typescript@7.23.2(@babel/core@7.23.2): resolution: {integrity: sha512-u4UJc1XsS1GhIGteM8rnGiIvf9rJpiVgMEeCnwlLA7WJPC+jcXWJAGxYmeqs5hOZD8BbAfnV5ezBOxQbb4OUxA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-option': 7.22.15 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-transform-modules-commonjs': 7.23.0(@babel/core@7.23.0) - '@babel/plugin-transform-typescript': 7.22.15(@babel/core@7.23.0) + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-modules-commonjs': 7.23.0(@babel/core@7.23.2) + '@babel/plugin-transform-typescript': 7.22.15(@babel/core@7.23.2) dev: true /@babel/regjsgen@0.8.0: @@ -2010,7 +2379,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.0 - dev: true /@babel/template@7.22.15: resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} @@ -3288,7 +3656,7 @@ packages: resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.19 babel-plugin-istanbul: 6.1.1 @@ -3403,7 +3771,7 @@ packages: detect-libc: 2.0.2 https-proxy-agent: 5.0.1(supports-color@9.4.0) make-dir: 3.1.0 - node-fetch: 2.6.12 + node-fetch: 2.7.0 nopt: 5.0.0 npmlog: 5.0.1 rimraf: 3.0.2 @@ -3420,7 +3788,6 @@ packages: dependencies: '@types/set-cookie-parser': 2.4.4 set-cookie-parser: 2.6.0 - dev: true /@mswjs/interceptors@0.17.10: resolution: {integrity: sha512-N8x7eSLGcmUFNWZRxT1vsHvypzIRgQYdG0rJey/rZCy6zT/30qDt8Joj7FxzGNLSwXbeZqJOMqDurp7ra4hgbw==} @@ -3436,7 +3803,6 @@ packages: web-encoding: 1.1.5 transitivePeerDependencies: - supports-color - dev: true /@netlify/binary-info@1.0.0: resolution: {integrity: sha512-4wMPu9iN3/HL97QblBsBay3E1etIciR84izI3U+4iALY+JHCrI+a2jO0qbAZ/nxKoegypYEaiiqWXylm+/zfrw==} @@ -3458,8 +3824,8 @@ packages: yargs: 17.7.2 dev: true - /@netlify/build@29.22.2(@types/node@20.8.4)(debug@4.3.4): - resolution: {integrity: sha512-cSz8X6UhNJdWY79No6Jxm0XBLtRE2lhoPc1OrXLS6afMspB3V9TsFNWRWSWvCL2Ppzkbu82rTdVA9YzVmkMiDw==} + /@netlify/build@29.22.5(@types/node@20.8.4)(debug@4.3.4): + resolution: {integrity: sha512-DlSj0muL1iSIQjImiMsgBXhO7ei4/JDo8TqkV5/9sneO340JGlYg3DRABxZh5wpXzQLgMQENbXZLKbUPjlR1PQ==} engines: {node: ^14.16.0 || >=16.0.0} hasBin: true dependencies: @@ -3467,13 +3833,13 @@ packages: '@honeycombio/opentelemetry-node': 0.5.0(debug@4.3.4)(supports-color@9.4.0) '@netlify/cache-utils': 5.1.5 '@netlify/config': 20.9.0 - '@netlify/edge-bundler': 9.2.0 + '@netlify/edge-bundler': 9.2.1 '@netlify/framework-info': 9.8.10 - '@netlify/functions-utils': 5.2.33(supports-color@9.4.0) + '@netlify/functions-utils': 5.2.34(supports-color@9.4.0) '@netlify/git-utils': 5.1.1 '@netlify/plugins-list': 6.71.0 '@netlify/run-utils': 5.1.1 - '@netlify/zip-it-and-ship-it': 9.23.0(supports-color@9.4.0) + '@netlify/zip-it-and-ship-it': 9.24.3(supports-color@9.4.0) '@opentelemetry/api': 1.6.0 '@sindresorhus/slugify': 2.2.1 ansi-escapes: 6.2.0 @@ -3600,8 +3966,8 @@ packages: uuid: 9.0.0 dev: true - /@netlify/edge-bundler@9.2.0: - resolution: {integrity: sha512-31YyaCOEvbhOwaTF34l4gAOjyD5eDAqIvpe8/VojhxBdDCrFKAVaQ6tiXaZOCGVwN3bzl49ai5Y/ekrDfIXKtw==} + /@netlify/edge-bundler@9.2.1: + resolution: {integrity: sha512-GThjKlfjL4yyoKDBsKnPQUevi2zA0XJdibpPzkT53/ys3fx8W+w4SUpdrFyrpNCBItNvdKj+rcT+s6cbhk046Q==} engines: {node: ^14.16.0 || >=16.0.0} dependencies: '@import-maps/resolve': 1.0.1 @@ -3644,11 +4010,11 @@ packages: semver: 7.5.4 dev: true - /@netlify/functions-utils@5.2.33(supports-color@9.4.0): - resolution: {integrity: sha512-YQBgglpH8ilYw0fS7HlWb5NdlzpEHgT/+/1zOqPlvZoKu/6kKE4iPvuQpXtVciXJMTW095pIbC/I4CZHmIv0PQ==} + /@netlify/functions-utils@5.2.34(supports-color@9.4.0): + resolution: {integrity: sha512-emaLcOfooXmoUWtsF+L7xhT28KipyJsU3i/h2w0JqoT1BUOkVs4vuWVeTZUVeHSJCe1DsiwyLe0NY4UkgNu6cQ==} engines: {node: ^14.16.0 || >=16.0.0} dependencies: - '@netlify/zip-it-and-ship-it': 9.24.2(supports-color@9.4.0) + '@netlify/zip-it-and-ship-it': 9.24.3(supports-color@9.4.0) cpy: 9.0.1 path-exists: 5.0.0 transitivePeerDependencies: @@ -3813,72 +4179,23 @@ packages: execa: 6.1.0 dev: true - /@netlify/serverless-functions-api@1.8.0: - resolution: {integrity: sha512-+dsowkoEA+LF4wS9kKafToHNSace7MxD2q3pgBik3N8UjAXBZo7J9t/E7rpkcm5w2ZXklvrDM815bOrzfDE5Jg==} + /@netlify/serverless-functions-api@1.9.1: + resolution: {integrity: sha512-SxsaTczNwV/gjQbGZLKVGHWVVtITV6V42nZ2UKcWQot0PCEZ/2JUQ8m6o+fIMn74m5FmCKowlKOqLkAE43nV5Q==} engines: {node: ^14.18.0 || >=16.0.0} dependencies: '@netlify/node-cookies': 0.1.0 urlpattern-polyfill: 8.0.2 dev: true - /@netlify/serverless-functions-api@1.9.0: - resolution: {integrity: sha512-Jq4uk1Mwa5vyxImupJYXPP+I5yYcp3PtguvXtJRutKdm9DPALXfZVtCQzBWMNdZiqVWCM3La9hvaBsPjSMfeug==} - engines: {node: ^14.18.0 || >=16.0.0} - dependencies: - '@netlify/node-cookies': 0.1.0 - urlpattern-polyfill: 8.0.2 - dev: true - - /@netlify/zip-it-and-ship-it@9.23.0(supports-color@9.4.0): - resolution: {integrity: sha512-4Khr9U3VKCEpK13zykbP8g2EgK58c7okRAyC0C+7lU5oopwp5K4lkKvGmZCaNyls3hhjXaX/wLv6K+t1NOIGSw==} - engines: {node: ^14.18.0 || >=16.0.0} - hasBin: true - dependencies: - '@babel/parser': 7.23.0 - '@netlify/binary-info': 1.0.0 - '@netlify/serverless-functions-api': 1.8.0 - '@vercel/nft': 0.23.1(supports-color@9.4.0) - archiver: 6.0.1 - common-path-prefix: 3.0.0 - cp-file: 10.0.0 - es-module-lexer: 1.3.1 - esbuild: 0.19.2 - execa: 6.1.0 - filter-obj: 5.1.0 - find-up: 6.3.0 - glob: 8.1.0 - is-builtin-module: 3.2.1 - is-path-inside: 4.0.0 - junk: 4.0.1 - locate-path: 7.2.0 - merge-options: 3.0.4 - minimatch: 9.0.3 - normalize-path: 3.0.0 - p-map: 5.5.0 - path-exists: 5.0.0 - precinct: 11.0.5(supports-color@9.4.0) - require-package-name: 2.0.1 - resolve: 2.0.0-next.5 - semver: 7.5.4 - tmp-promise: 3.0.3 - toml: 3.0.0 - unixify: 1.0.0 - urlpattern-polyfill: 8.0.2 - yargs: 17.7.2 - transitivePeerDependencies: - - encoding - - supports-color - dev: true - - /@netlify/zip-it-and-ship-it@9.24.2(supports-color@9.4.0): - resolution: {integrity: sha512-JENcQgG42n7z7DFIpQhOxnQliVZJ943VamjShkrYD6DZyw2jAEqY9llugDF6mQU/gX83ylaDpJJpfudeprg2KA==} + /@netlify/zip-it-and-ship-it@9.24.3(supports-color@9.4.0): + resolution: {integrity: sha512-lPgazcS+IvKX6Ov/K7dCk/SDU5d9M5TuV8LmMtJAsrX8VsASzcG3Q3QjNIXuVskZX2mitVLX9MpdmEIv/UXMtA==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true dependencies: '@babel/parser': 7.23.0 '@babel/types': 7.22.19 '@netlify/binary-info': 1.0.0 - '@netlify/serverless-functions-api': 1.9.0 + '@netlify/serverless-functions-api': 1.9.1 '@vercel/nft': 0.23.1(supports-color@9.4.0) archiver: 6.0.1 common-path-prefix: 3.0.0 @@ -4052,7 +4369,7 @@ packages: '@octokit/request-error': 3.0.3 '@octokit/types': 9.3.2 is-plain-object: 5.0.0 - node-fetch: 2.6.12 + node-fetch: 2.7.0 universal-user-agent: 6.0.0 transitivePeerDependencies: - encoding @@ -4088,7 +4405,6 @@ packages: /@open-draft/until@1.0.3: resolution: {integrity: sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==} - dev: true /@opentelemetry/api-logs@0.39.1: resolution: {integrity: sha512-9BJ8lMcOzEN0lu+Qji801y707oFO4xT3db6cosPvl+k7ItUHKN5ofWqtSbM9gbt1H4JJ/4/2TVrqI9Rq7hNv6Q==} @@ -4861,101 +5177,101 @@ packages: lodash: 4.17.21 dev: true - /@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.23.0): + /@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.23.2): resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.23.0): + /@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.23.2): resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.23.0): + /@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.23.2): resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.23.0): + /@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.23.2): resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.23.0): + /@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.23.2): resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.23.0): + /@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.23.2): resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.23.0): + /@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.23.2): resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 dev: true - /@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.23.0): + /@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.23.2): resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==} engines: {node: '>=12'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 dev: true - /@svgr/babel-preset@8.1.0(@babel/core@7.23.0): + /@svgr/babel-preset@8.1.0(@babel/core@7.23.2): resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==} engines: {node: '>=14'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.23.0 - '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.23.0) - '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.23.0) - '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.23.0) - '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.23.0) - '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.23.0) - '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.23.0) - '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.23.0) - '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.23.2) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.23.2) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.23.2) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.23.2) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.23.2) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.23.2) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.23.2) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.23.2) dev: true /@svgr/core@8.1.0(typescript@5.2.2): resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==} engines: {node: '>=14'} dependencies: - '@babel/core': 7.23.0 - '@svgr/babel-preset': 8.1.0(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@svgr/babel-preset': 8.1.0(@babel/core@7.23.2) camelcase: 6.3.0 cosmiconfig: 8.3.6(typescript@5.2.2) snake-case: 3.0.4 @@ -4978,8 +5294,8 @@ packages: peerDependencies: '@svgr/core': '*' dependencies: - '@babel/core': 7.23.0 - '@svgr/babel-preset': 8.1.0(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@svgr/babel-preset': 8.1.0(@babel/core@7.23.2) '@svgr/core': 8.1.0(typescript@5.2.2) '@svgr/hast-util-to-babel-ast': 8.0.0 svg-parser: 2.0.4 @@ -5005,11 +5321,11 @@ packages: resolution: {integrity: sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==} engines: {node: '>=14'} dependencies: - '@babel/core': 7.23.0 - '@babel/plugin-transform-react-constant-elements': 7.22.5(@babel/core@7.23.0) - '@babel/preset-env': 7.23.2(@babel/core@7.23.0) - '@babel/preset-react': 7.22.15(@babel/core@7.23.0) - '@babel/preset-typescript': 7.23.2(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@babel/plugin-transform-react-constant-elements': 7.22.5(@babel/core@7.23.2) + '@babel/preset-env': 7.23.2(@babel/core@7.23.2) + '@babel/preset-react': 7.22.15(@babel/core@7.23.2) + '@babel/preset-typescript': 7.23.2(@babel/core@7.23.2) '@svgr/core': 8.1.0(typescript@5.2.2) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0) '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0)(typescript@5.2.2) @@ -5152,28 +5468,50 @@ packages: defer-to-connect: 2.0.1 dev: true - /@tanstack/query-core@5.0.0-rc.6: - resolution: {integrity: sha512-aBGionFwNoPsxnOZPVDIWOStRFWh0/jJgLgC238XyjoML/gN+pwcGNvhp995Pt+uOZPhw5XemzoKCVX9Vpq9KA==} + /@tanstack/query-core@4.36.1: + resolution: {integrity: sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==} + dev: true + + /@tanstack/query-core@5.0.0-rc.12: + resolution: {integrity: sha512-gEgRIkFMjxhu+rbomSxAYTA+aPaPrLffJwFCpfd/AqJ8PTlnredJnFfFskCbR6oZo/1XEuDORFW9c6b4gOnEKQ==} /@tanstack/query-devtools@5.0.0-rc.10: resolution: {integrity: sha512-wtxlnd/j6sTUGX3l5xH3WB2NKptsLelVYuRO49NMV79Bm1aNVgGh21rJmWDmsig+4MNRA22CfpouuTMh8+3AbA==} dev: true - /@tanstack/react-query-devtools@5.0.0-rc.10(@tanstack/react-query@5.0.0-rc.7)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-NSQrNPoRW5MY1g1r9Q4coEOf1BO87a5GxYTOeZvHFJGvVPIDToOo4NsKqaWQ8hMLR9QBoLqzoUx1MUg8awuRQA==} + /@tanstack/react-query-devtools@5.0.0-rc.12(@tanstack/react-query@5.0.0-rc.12)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-LgNy9jwynSiEMGSr3fUaAQEmnSN4y/D+jaP25dh1fCqtBF1fAS1nog7rDfBwQ2XjW/Ur2HXGw8OcA1mnU4tpcg==} peerDependencies: - '@tanstack/react-query': ^5.0.0-rc.7 + '@tanstack/react-query': ^5.0.0-rc.12 react: ^18.0.0 react-dom: ^18.0.0 dependencies: '@tanstack/query-devtools': 5.0.0-rc.10 - '@tanstack/react-query': 5.0.0-rc.7(react-dom@18.2.0)(react@18.2.0) + '@tanstack/react-query': 5.0.0-rc.12(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + + /@tanstack/react-query@4.36.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + '@tanstack/query-core': 4.36.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) + use-sync-external-store: 1.2.0(react@18.2.0) dev: true - /@tanstack/react-query@5.0.0-rc.7(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-LajFlMMj4CYGgO1KgSVaF1voznOqXUzhDNc2weRlS3Ua9vncTcl3JMh4HO7fReeOGogn6X/1JGILXh7CyrXwOQ==} + /@tanstack/react-query@5.0.0-rc.12(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-pUb6xkHs2YFb9N6/34ZMut4LG0yO+uqjDaoWmxOsmIZVwPYCNrsRjI8kaF/6F+5gyxJp6hzFHH4Jlukbx/tReA==} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 @@ -5184,7 +5522,7 @@ packages: react-native: optional: true dependencies: - '@tanstack/query-core': 5.0.0-rc.6 + '@tanstack/query-core': 5.0.0-rc.12 client-only: 0.0.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -5300,7 +5638,7 @@ packages: /@types/concat-stream@2.0.0: resolution: {integrity: sha512-t3YCerNM7NTVjLuICZo5gYAXYoDvpuuTceCcFQWcDQz26kxUR5uIWolxbIR5jRNIXpMqhOpW/b8imCR1LEmuJw==} dependencies: - '@types/node': 18.18.4 + '@types/node': 20.8.4 dev: true /@types/connect-history-api-fallback@1.5.1: @@ -5316,13 +5654,11 @@ packages: /@types/cookie@0.4.1: resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} - dev: true /@types/debug@4.1.9: resolution: {integrity: sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow==} dependencies: '@types/ms': 0.7.32 - dev: true /@types/eslint-scope@3.7.5: resolution: {integrity: sha512-JNvhIEyxVW6EoMIFIvj93ZOywYFatlpu9deeH6eSx6PE3WHYvHaQtmHmQeNw7aA81bYGBPPQqdtBm6b1SsQMmA==} @@ -5423,7 +5759,6 @@ packages: /@types/js-levenshtein@1.1.1: resolution: {integrity: sha512-qC4bCqYGy1y/NP7dDVr7KJarn+PbX1nSpwA7JXdu0HxT3QYjO8MJ+cntENtHFVy2dRAyBV23OZ6MxsW1AM1L8g==} - dev: true /@types/jsdom@20.0.1: resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} @@ -5458,14 +5793,13 @@ packages: /@types/ms@0.7.32: resolution: {integrity: sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g==} - dev: true /@types/node@12.20.55: resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} dev: true - /@types/node@18.18.4: - resolution: {integrity: sha512-t3rNFBgJRugIhackit2mVcLfF6IRc0JE4oeizPQL8Zrm8n2WY/0wOdpOPhdtG0V9Q2TlW/axbF1MJ6z+Yj/kKQ==} + /@types/node@18.18.5: + resolution: {integrity: sha512-4slmbtwV59ZxitY4ixUZdy1uRLf9eSIvBWPQxNjhHYWEtn0FryfKpyS2cvADYXTayWdKEIsJengncrVvkI4I6A==} dev: true /@types/node@20.8.4: @@ -5544,7 +5878,6 @@ packages: resolution: {integrity: sha512-xCfTC/eL/GmvMC24b42qJpYSTdCIBwWcfskDF80ztXtnU6pKXyvuZP2EConb2K9ps0s7gMhCa0P1foy7wiItMA==} dependencies: '@types/node': 20.8.4 - dev: true /@types/sockjs@0.3.34: resolution: {integrity: sha512-R+n7qBFnm/6jinlteC9DBL5dGiDGjWAvjo4viUanpnc/dG1y7uDoacXPIQ/PQEg1fI912SMHIa014ZjRpvDw4g==} @@ -5994,7 +6327,7 @@ packages: eslint-plugin-react: 7.33.2(eslint@8.51.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.51.0) eslint-plugin-storybook: 0.6.15(eslint@8.51.0)(typescript@5.2.2) - eslint-plugin-testing-library: 6.0.2(eslint@8.51.0)(typescript@5.2.2) + eslint-plugin-testing-library: 6.1.0(eslint@8.51.0)(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - eslint-import-resolver-typescript @@ -6239,7 +6572,6 @@ packages: /@xmldom/xmldom@0.8.10: resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} engines: {node: '>=10.0.0'} - dev: true /@xtuc/ieee754@1.2.0: resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -6250,7 +6582,6 @@ packages: /@zxing/text-encoding@0.9.0: resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==} requiresBuild: true - dev: true optional: true /abab@2.0.6: @@ -6421,7 +6752,6 @@ packages: engines: {node: '>=8'} dependencies: type-fest: 0.21.3 - dev: true /ansi-escapes@5.0.0: resolution: {integrity: sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==} @@ -6483,7 +6813,6 @@ packages: engines: {node: '>=8'} dependencies: color-convert: 2.0.1 - dev: true /ansi-styles@5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} @@ -6772,7 +7101,6 @@ packages: /available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} - dev: true /avvio@8.2.1: resolution: {integrity: sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==} @@ -6808,17 +7136,17 @@ packages: resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} dev: true - /babel-jest@29.7.0(@babel/core@7.23.0): + /babel-jest@29.7.0(@babel/core@7.23.2): resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.8.0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@jest/transform': 29.7.0 '@types/babel__core': 7.20.2 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.23.0) + babel-preset-jest: 29.6.3(@babel/core@7.23.2) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -6849,71 +7177,71 @@ packages: '@types/babel__traverse': 7.20.2 dev: true - /babel-plugin-polyfill-corejs2@0.4.6(@babel/core@7.23.0): + /babel-plugin-polyfill-corejs2@0.4.6(@babel/core@7.23.2): resolution: {integrity: sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: '@babel/compat-data': 7.23.2 - '@babel/core': 7.23.0 - '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.2) semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-corejs3@0.8.5(@babel/core@7.23.0): + /babel-plugin-polyfill-corejs3@0.8.5(@babel/core@7.23.2): resolution: {integrity: sha512-Q6CdATeAvbScWPNLB8lzSO7fgUVBkQt6zLgNlfyeCr/EQaEQR+bWiBYYPYAFyE528BMjRhL+1QBMOI4jc/c5TA==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.23.0 - '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.2) core-js-compat: 3.33.0 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-regenerator@0.5.3(@babel/core@7.23.0): + /babel-plugin-polyfill-regenerator@0.5.3(@babel/core@7.23.2): resolution: {integrity: sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.23.0 - '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.0) + '@babel/core': 7.23.2 + '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.2) transitivePeerDependencies: - supports-color dev: true - /babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.0): + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.2): resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.0 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.0) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.0) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.0) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.0) - dev: true - - /babel-preset-jest@29.6.3(@babel/core@7.23.0): + '@babel/core': 7.23.2 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.2) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.2) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.2) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.2) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.2) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.2) + dev: true + + /babel-preset-jest@29.6.3(@babel/core@7.23.2): resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.0) + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.2) dev: true /backoff@2.5.0: @@ -6932,7 +7260,6 @@ packages: /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - dev: true /base@0.11.2: resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==} @@ -7014,7 +7341,6 @@ packages: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 - dev: true /bl@5.1.0: resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} @@ -7127,7 +7453,7 @@ packages: hasBin: true dependencies: caniuse-lite: 1.0.30001547 - electron-to-chromium: 1.4.551 + electron-to-chromium: 1.4.552 node-releases: 2.0.13 update-browserslist-db: 1.0.13(browserslist@4.22.1) @@ -7160,7 +7486,6 @@ packages: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - dev: true /buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -7266,7 +7591,7 @@ packages: /call-bind@1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} dependencies: - function-bind: 1.1.1 + function-bind: 1.1.2 get-intrinsic: 1.2.1 /callsite@1.0.0: @@ -7341,7 +7666,6 @@ packages: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - dev: true /chalk@5.2.0: resolution: {integrity: sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==} @@ -7383,7 +7707,6 @@ packages: /chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - dev: true /chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} @@ -7471,7 +7794,6 @@ packages: engines: {node: '>=8'} dependencies: restore-cursor: 3.1.0 - dev: true /cli-cursor@4.0.0: resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} @@ -7490,7 +7812,6 @@ packages: /cli-spinners@2.9.1: resolution: {integrity: sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==} engines: {node: '>=6'} - dev: true /cli-truncate@0.2.1: resolution: {integrity: sha512-f4r4yJnbT++qUPI9NR4XLDLq41gQ+uqnPItWG0F5ZkehuNiTTa3EY0S4AqTSUOeJ7/zU41oWPQSNkW5BqPL9bg==} @@ -7507,7 +7828,6 @@ packages: /cli-width@3.0.0: resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} engines: {node: '>= 10'} - dev: true /client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} @@ -7535,7 +7855,6 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true /clone-deep@4.0.1: resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} @@ -7548,7 +7867,6 @@ packages: /clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} - dev: true /co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} @@ -7583,7 +7901,6 @@ packages: engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 - dev: true /color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} @@ -7591,7 +7908,6 @@ packages: /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - dev: true /color-string@1.9.1: resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} @@ -7799,7 +8115,6 @@ packages: /cookie@0.4.2: resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} engines: {node: '>= 0.6'} - dev: true /cookie@0.5.0: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} @@ -8277,7 +8592,6 @@ packages: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} dependencies: clone: 1.0.4 - dev: true /defer-to-connect@2.0.1: resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} @@ -8603,8 +8917,8 @@ packages: /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - /electron-to-chromium@1.4.551: - resolution: {integrity: sha512-/Ng/W/kFv7wdEHYzxdK7Cv0BHEGSkSB3M0Ssl8Ndr1eMiYeas/+Mv4cNaDqamqWx6nd2uQZfPz6g25z25M/sdw==} + /electron-to-chromium@1.4.552: + resolution: {integrity: sha512-qMPzA5TEuOAbLFmbpNvO4qkBRe2B5dAxl6H4KxqRNy9cvBeHT2EyzecX0bumBfRhHN8cQJrx6NPd0AAoCCPKQw==} /elegant-spinner@1.0.1: resolution: {integrity: sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ==} @@ -8618,7 +8932,6 @@ packages: /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true /emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} @@ -8768,7 +9081,7 @@ packages: define-properties: 1.2.1 es-abstract: 1.22.2 es-set-tostringtag: 2.0.1 - function-bind: 1.1.1 + function-bind: 1.1.2 get-intrinsic: 1.2.1 globalthis: 1.0.3 has-property-descriptors: 1.0.0 @@ -8914,7 +9227,6 @@ packages: /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - dev: true /escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} @@ -9170,8 +9482,8 @@ packages: - typescript dev: true - /eslint-plugin-testing-library@6.0.2(eslint@8.51.0)(typescript@5.2.2): - resolution: {integrity: sha512-3BV6FWtLbpKFb4Y1obSdt8PC9xSqz6T+7EHB/6pSCXqVjKPoS67ck903feKMKQphd5VhrX+N51yHuVaPa7elsw==} + /eslint-plugin-testing-library@6.1.0(eslint@8.51.0)(typescript@5.2.2): + resolution: {integrity: sha512-r7kE+az3tbp8vyRwfyAGZ6V/xw+XvdWFPicIo6jbOPZoossOFDeHizARqPGV6gEkyF8hyCFhhH3mlQOGS3N5Sg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6'} peerDependencies: eslint: ^7.5.0 || ^8.0.0 @@ -9490,7 +9802,6 @@ packages: chardet: 0.7.0 iconv-lite: 0.4.24 tmp: 0.0.33 - dev: true /extglob@2.0.4: resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==} @@ -9706,7 +10017,6 @@ packages: engines: {node: '>=8'} dependencies: escape-string-regexp: 1.0.5 - dev: true /figures@4.0.1: resolution: {integrity: sha512-rElJwkA/xS04Vfg+CaZodpso7VqBknOYbzi6I76hI4X80RUjkSxO2oAyPmGbuXUppywjqndOrQDl817hDnI++w==} @@ -9882,7 +10192,6 @@ packages: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} dependencies: is-callable: 1.2.7 - dev: true /for-in@1.0.2: resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} @@ -9988,8 +10297,8 @@ packages: requiresBuild: true optional: true - /function-bind@1.1.1: - resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} /function.prototype.name@1.1.6: resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} @@ -10041,12 +10350,11 @@ packages: /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - dev: true /get-intrinsic@1.2.1: resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} dependencies: - function-bind: 1.1.1 + function-bind: 1.1.2 has: 1.0.4 has-proto: 1.0.1 has-symbols: 1.0.3 @@ -10239,7 +10547,6 @@ packages: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: get-intrinsic: 1.2.1 - dev: true /got@12.6.1: resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} @@ -10276,7 +10583,6 @@ packages: /graphql@16.8.1: resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - dev: true /handle-thing@2.0.1: resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} @@ -10330,7 +10636,6 @@ packages: engines: {node: '>= 0.4'} dependencies: has-symbols: 1.0.3 - dev: true /has-unicode@2.0.1: resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} @@ -10397,7 +10702,6 @@ packages: /headers-polyfill@3.2.5: resolution: {integrity: sha512-tUCGvt191vNSQgttSyJoibR+VO+I6+iCHIUdhzEMJKE+EAL8BwCN7fUOZlY4ofOelNHsK+gEjxB/B+9N3EWtdA==} - dev: true /hexer@1.5.0: resolution: {integrity: sha512-dyrPC8KzBzUJ19QTIo1gXNqIISRXQ0NwteW6OeQHRN4ZuZeHkdODfj0zHBdOlHbRY8GqbqK57C9oWSvQZizFsg==} @@ -10664,7 +10968,6 @@ packages: /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - dev: true /ignore-by-default@1.0.1: resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} @@ -10798,7 +11101,6 @@ packages: strip-ansi: 6.0.1 through: 2.3.8 wrap-ansi: 6.2.0 - dev: true /inspect-with-kind@1.0.5: resolution: {integrity: sha512-MAQUJuIo7Xqk8EVNP+6d3CKq9c80hi4tjIbIAT6lmGW9W6WzlHiu9PS8uSuUYU+Do+j1baiFp3H25XEVxDIG2g==} @@ -10869,7 +11171,6 @@ packages: dependencies: call-bind: 1.0.2 has-tostringtag: 1.0.0 - dev: true /is-array-buffer@3.0.2: resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} @@ -10933,7 +11234,6 @@ packages: /is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - dev: true /is-ci@3.0.1: resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} @@ -11046,7 +11346,6 @@ packages: /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - dev: true /is-fullwidth-code-point@4.0.0: resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} @@ -11063,7 +11362,6 @@ packages: engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 - dev: true /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} @@ -11098,7 +11396,6 @@ packages: /is-interactive@1.0.0: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} - dev: true /is-interactive@2.0.0: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} @@ -11116,7 +11413,6 @@ packages: /is-node-process@1.2.0: resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} - dev: true /is-npm@6.0.0: resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} @@ -11259,7 +11555,6 @@ packages: engines: {node: '>= 0.4'} dependencies: which-typed-array: 1.1.11 - dev: true /is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} @@ -11268,7 +11563,6 @@ packages: /is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} - dev: true /is-unicode-supported@1.3.0: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} @@ -11355,7 +11649,7 @@ packages: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/parser': 7.23.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 @@ -11368,7 +11662,7 @@ packages: resolution: {integrity: sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==} engines: {node: '>=10'} dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/parser': 7.23.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 @@ -11513,11 +11807,11 @@ packages: ts-node: optional: true dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 '@types/node': 20.8.4 - babel-jest: 29.7.0(@babel/core@7.23.0) + babel-jest: 29.7.0(@babel/core@7.23.2) chalk: 4.1.2 ci-info: 3.9.0 deepmerge: 4.3.1 @@ -11781,15 +12075,15 @@ packages: resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 '@babel/generator': 7.23.0 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.0) - '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.23.0) + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.23.2) '@babel/types': 7.23.0 '@jest/expect-utils': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.0) + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.2) chalk: 4.1.2 expect: 29.7.0 graceful-fs: 4.2.11 @@ -11907,7 +12201,6 @@ packages: /js-levenshtein@1.1.6: resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} engines: {node: '>=0.10.0'} - dev: true /js-string-escape@1.0.1: resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==} @@ -12377,7 +12670,6 @@ packages: dependencies: chalk: 4.1.2 is-unicode-supported: 0.1.0 - dev: true /log-symbols@5.1.0: resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} @@ -13263,7 +13555,6 @@ packages: transitivePeerDependencies: - encoding - supports-color - dev: true /multicast-dns@7.2.5: resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} @@ -13287,7 +13578,6 @@ packages: /mute-stream@0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} - dev: true /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -13343,21 +13633,21 @@ packages: resolution: {integrity: sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==} dev: true - /netlify-cli@16.6.1(@types/node@20.8.4): - resolution: {integrity: sha512-XaJpLuEItcnSp90FWU1mbC1HThc1No+4crfE8xtNcKWP1x/JfjIJFcCug+7J5ouy5a/twYLAlPuKctB2Fyg4/g==} + /netlify-cli@16.7.0(@types/node@20.8.4): + resolution: {integrity: sha512-RxxC68uZI58Z2uGTUloieHrrb0+BoqUdATTWbxKA5OPwpEP3cjDu+WaM45wdEnocy7Eq2ZG0mjLsAByunY2m7A==} engines: {node: '>=16.16.0'} hasBin: true requiresBuild: true dependencies: '@bugsnag/js': 7.20.2 '@fastify/static': 6.10.2 - '@netlify/build': 29.22.2(@types/node@20.8.4)(debug@4.3.4) + '@netlify/build': 29.22.5(@types/node@20.8.4)(debug@4.3.4) '@netlify/build-info': 7.10.1 '@netlify/config': 20.9.0 '@netlify/edge-bundler': 9.1.0 '@netlify/local-functions-proxy': 1.1.1 - '@netlify/serverless-functions-api': 1.8.0 - '@netlify/zip-it-and-ship-it': 9.23.0(supports-color@9.4.0) + '@netlify/serverless-functions-api': 1.9.1 + '@netlify/zip-it-and-ship-it': 9.24.3(supports-color@9.4.0) '@octokit/rest': 19.0.13 ansi-escapes: 6.2.0 ansi-styles: 6.2.1 @@ -13541,7 +13831,6 @@ packages: optional: true dependencies: whatwg-url: 5.0.0 - dev: true /node-fetch@3.3.2: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} @@ -13931,7 +14220,6 @@ packages: log-symbols: 4.1.0 strip-ansi: 6.0.1 wcwidth: 1.0.1 - dev: true /ora@6.3.1: resolution: {integrity: sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==} @@ -13959,7 +14247,6 @@ packages: /os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} - dev: true /outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} @@ -13967,7 +14254,6 @@ packages: /outvariant@1.4.0: resolution: {integrity: sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==} - dev: true /p-cancelable@3.0.0: resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} @@ -14271,7 +14557,6 @@ packages: /path-to-regexp@6.2.1: resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==} - dev: true /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} @@ -14772,6 +15057,14 @@ packages: react: 18.2.0 scheduler: 0.23.0 + /react-error-boundary@4.0.11(react@18.2.0): + resolution: {integrity: sha512-U13ul67aP5DOSPNSCWQ/eO0AQEYzEFkVljULQIjMV0KlffTAhxuDoBKdO0pb/JZ8mDhMKFZ9NZi0BmLGUiNphw==} + peerDependencies: + react: '>=16.13.1' + dependencies: + '@babel/runtime': 7.23.2 + react: 18.2.0 + /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} dev: true @@ -15006,7 +15299,6 @@ packages: /regenerator-runtime@0.14.0: resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} - dev: true /regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} @@ -15126,7 +15418,6 @@ packages: /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - dev: true /require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} @@ -15226,7 +15517,6 @@ packages: dependencies: onetime: 5.1.2 signal-exit: 3.0.7 - dev: true /restore-cursor@4.0.0: resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} @@ -15288,7 +15578,6 @@ packages: /run-async@2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} - dev: true /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -15307,7 +15596,6 @@ packages: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: tslib: 2.6.2 - dev: true /sade@1.8.1: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} @@ -15498,7 +15786,6 @@ packages: /set-cookie-parser@2.6.0: resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} - dev: true /set-function-name@2.0.1: resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} @@ -15892,11 +16179,9 @@ packages: resolution: {integrity: sha512-KDf/ujU8Zud3YaLtMCcTI4xkZlZVIYxTLr+XIULexP+77EEVWixeXroLUXQXiVtH4XH2W7jr/3PT1v3zBuvc3A==} dependencies: events: 3.3.0 - dev: true /strict-event-emitter@0.4.6: resolution: {integrity: sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==} - dev: true /string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} @@ -15934,7 +16219,6 @@ packages: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - dev: true /string-width@5.1.2: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} @@ -16139,7 +16423,6 @@ packages: engines: {node: '>=8'} dependencies: has-flag: 4.0.0 - dev: true /supports-color@8.1.1: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} @@ -16400,7 +16683,6 @@ packages: /through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - dev: true /thunky@1.1.0: resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} @@ -16431,7 +16713,6 @@ packages: engines: {node: '>=0.6.0'} dependencies: os-tmpdir: 1.0.2 - dev: true /tmp@0.2.1: resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==} @@ -16531,7 +16812,6 @@ packages: /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: true /tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} @@ -16590,7 +16870,7 @@ packages: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} dev: true - /ts-jest@29.1.1(@babel/core@7.23.0)(esbuild@0.18.20)(jest@29.7.0)(typescript@5.2.2): + /ts-jest@29.1.1(@babel/core@7.23.2)(esbuild@0.18.20)(jest@29.7.0)(typescript@5.2.2): resolution: {integrity: sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -16611,7 +16891,7 @@ packages: esbuild: optional: true dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.23.2 bs-logger: 0.2.6 esbuild: 0.18.20 fast-json-stable-stringify: 2.1.0 @@ -16759,7 +17039,6 @@ packages: /type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} - dev: true /type-fest@0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} @@ -16779,7 +17058,6 @@ packages: /type-fest@2.19.0: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} - dev: true /type-fest@3.13.1: resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} @@ -16845,7 +17123,6 @@ packages: resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} engines: {node: '>=14.17'} hasBin: true - dev: true /uid-safe@2.1.5: resolution: {integrity: sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==} @@ -16911,7 +17188,7 @@ packages: '@types/concat-stream': 2.0.0 '@types/debug': 4.1.9 '@types/is-empty': 1.2.1 - '@types/node': 18.18.4 + '@types/node': 18.18.5 '@types/unist': 2.0.8 concat-stream: 2.0.0 debug: 4.3.4(supports-color@9.4.0) @@ -17130,6 +17407,14 @@ packages: resolution: {integrity: sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==} dev: true + /use-sync-external-store@1.2.0(react@18.2.0): + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: true + /use@3.1.1: resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==} engines: {node: '>=0.10.0'} @@ -17146,7 +17431,6 @@ packages: is-generator-function: 1.0.10 is-typed-array: 1.1.12 which-typed-array: 1.1.11 - dev: true /utila@0.4.0: resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} @@ -17294,7 +17578,6 @@ packages: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: defaults: 1.0.4 - dev: true /web-encoding@1.1.5: resolution: {integrity: sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==} @@ -17302,7 +17585,6 @@ packages: util: 0.12.5 optionalDependencies: '@zxing/text-encoding': 0.9.0 - dev: true /web-streams-polyfill@3.2.1: resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} @@ -17311,7 +17593,6 @@ packages: /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: true /webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} @@ -17552,7 +17833,6 @@ packages: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - dev: true /whatwg-url@7.1.0: resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} @@ -17620,7 +17900,6 @@ packages: for-each: 0.3.3 gopd: 1.0.1 has-tostringtag: 1.0.0 - dev: true /which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} @@ -17717,7 +17996,6 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} @@ -17726,7 +18004,6 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true /wrap-ansi@8.1.0: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} @@ -17807,7 +18084,6 @@ packages: /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - dev: true /yallist@2.1.2: resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} @@ -17842,7 +18118,6 @@ packages: /yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} - dev: true /yargs@15.4.1: resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} @@ -17885,7 +18160,6 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - dev: true /yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f4525e8e8..4145d02cd 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,3 @@ packages: - "packages/*" - - "sample/*" + - "samples/**" diff --git a/sample/another-remote-module/tsconfig.json b/sample/another-remote-module/tsconfig.json deleted file mode 100644 index 7d155fc83..000000000 --- a/sample/another-remote-module/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "extends": "@workleap/typescript-configs/web-application.json", - "compilerOptions": { - "paths": { - "@squide/core": ["../../packages/core/src/index.ts"], - "@squide/react-router": ["../../packages/react-router/src/index.ts"], - "@squide/webpack-module-federation": ["../../packages/webpack-module-federation/src/index.ts"], - "@squide/webpack-module-federation/defineConfig.js": ["../../packages/webpack-module-federation/src/defineConfig.js"], - "@sample/shell": ["../shell/src/index.ts"], - "@sample/shared": ["../shared/src/index.ts"], - "@sample/shared/FederatedTabsLayout.tsx": ["../shared/src/FederatedTabsLayout.tsx"] - } - }, - "exclude": ["dist", "node_modules"] -} diff --git a/sample/host/src/App.tsx b/sample/host/src/App.tsx deleted file mode 100644 index 15d4755a9..000000000 --- a/sample/host/src/App.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { BackgroundColorContext, LoggerTelemetryService } from "@sample/shared"; -import { useAppRouter } from "@sample/shell"; -import { useLogger } from "@squide/react-router"; -import { sessionManager } from "./session.ts"; - -export function App() { - const logger = useLogger(); - - const telemetryService = new LoggerTelemetryService(logger); - - const appRouter = useAppRouter(process.env.USE_MSW as unknown as boolean, sessionManager, telemetryService); - - return ( - - {appRouter} - - ); -} diff --git a/sample/host/tsconfig.json b/sample/host/tsconfig.json deleted file mode 100644 index 71d7e36e9..000000000 --- a/sample/host/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "@workleap/typescript-configs/web-application.json", - "compilerOptions": { - "paths": { - "@squide/core": ["../../packages/core/src/index.ts"], - "@squide/react-router": ["../../packages/react-router/src/index.ts"], - "@squide/webpack-module-federation": ["../../packages/webpack-module-federation/src/index.ts"], - "@squide/webpack-module-federation/defineConfig.js": ["../../packages/webpack-module-federation/src/defineConfig.js"], - "@squide/msw": ["../../packages/msw/src/index.ts"], - "@squide/fakes": ["../../packages/fakes/src/index.ts"], - "@sample/shell": ["../shell/src/index.ts"], - "@sample/shared": ["../shared/src/index.ts"], - "@sample/local-module": ["../local-module/src/register.ts"] - } - }, - "exclude": ["dist", "node_modules"] -} diff --git a/sample/local-module/tsconfig.json b/sample/local-module/tsconfig.json deleted file mode 100644 index 1b3ab1298..000000000 --- a/sample/local-module/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "@workleap/typescript-configs/library.json", - "compilerOptions": { - "paths": { - "@squide/core": ["../../packages/core/src/index.ts"], - "@squide/react-router": ["../../packages/react-router/src/index.ts"], - "@sample/shell": ["../shell/src/index.ts"], - "@sample/shared": ["../shared/src/index.ts"], - "@sample/shared/FederatedTabsLayout.tsx": ["../shared/src/FederatedTabsLayout.tsx"] - } - }, - "exclude": ["dist", "node_modules"] -} diff --git a/sample/remote-module/src/dev/App.tsx b/sample/remote-module/src/dev/App.tsx deleted file mode 100644 index a42157cc6..000000000 --- a/sample/remote-module/src/dev/App.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { BackgroundColorContext, LoggerTelemetryService } from "@sample/shared"; -import { useAppRouter } from "@sample/shell"; -import { useLogger } from "@squide/react-router"; -import { sessionManager } from "./session.ts"; - -export function App() { - const logger = useLogger(); - - const telemetryService = new LoggerTelemetryService(logger); - - const appRouter = useAppRouter(process.env.USE_MSW as unknown as boolean, sessionManager, telemetryService); - - return ( - - {appRouter} - - ); -} - diff --git a/sample/remote-module/tsconfig.json b/sample/remote-module/tsconfig.json deleted file mode 100644 index a396d74cb..000000000 --- a/sample/remote-module/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": "@workleap/typescript-configs/web-application.json", - "compilerOptions": { - "paths": { - "@squide/core": ["../../packages/core/src/index.ts"], - "@squide/react-router": ["../../packages/react-router/src/index.ts"], - "@squide/webpack-module-federation": ["../../packages/webpack-module-federation/src/index.ts"], - "@squide/webpack-module-federation/defineConfig.js": ["../../packages/webpack-module-federation/src/defineConfig.js"], - "@squide/msw": ["../../packages/msw/src/index.ts"], - "@sample/shell": ["../shell/src/index.ts"], - "@sample/shared": ["../shared/src/index.ts"], - "@sample/shared/FederatedTabsLayout.tsx": ["../shared/src/FederatedTabsLayout.tsx"] - } - }, - "exclude": ["dist", "node_modules"] -} diff --git a/sample/shared/tsconfig.json b/sample/shared/tsconfig.json deleted file mode 100644 index d52f7ed18..000000000 --- a/sample/shared/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@workleap/typescript-configs/library.json", - "compilerOptions": { - "paths": { - "@squide/core": ["../../packages/core/src/index.ts"], - "@squide/react-router": ["../../packages/react-router/src/index.ts"] - } - }, - "exclude": ["dist", "node_modules"] -} diff --git a/sample/shell/src/index.ts b/sample/shell/src/index.ts deleted file mode 100644 index 3af16ad19..000000000 --- a/sample/shell/src/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export * from "./AuthenticatedLayout.tsx"; -export * from "./AuthenticationBoundary.tsx"; -export * from "./Login.tsx"; -export * from "./Logout.tsx"; -export * from "./ModuleErrorBoundary.tsx"; -export * from "./NoMatch.tsx"; -export * from "./RootErrorBoundary.tsx"; -export * from "./RootLayout.tsx"; -export * from "./register.tsx"; -export * from "./useAppRouter.tsx"; - diff --git a/sample/shell/tsconfig.json b/sample/shell/tsconfig.json deleted file mode 100644 index ed6188feb..000000000 --- a/sample/shell/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "@workleap/typescript-configs/library.json", - "compilerOptions": { - "paths": { - "@squide/core": ["../../packages/core/src/index.ts"], - "@squide/react-router": ["../../packages/react-router/src/index.ts"], - "@sample/shared": ["../shared/src/index.ts"] - } - }, - "exclude": ["dist", "node_modules"] -} diff --git a/sample/another-remote-module/.browserslistrc b/samples/basic/another-remote-module/.browserslistrc similarity index 100% rename from sample/another-remote-module/.browserslistrc rename to samples/basic/another-remote-module/.browserslistrc diff --git a/sample/another-remote-module/.eslintrc.json b/samples/basic/another-remote-module/.eslintrc.json similarity index 100% rename from sample/another-remote-module/.eslintrc.json rename to samples/basic/another-remote-module/.eslintrc.json diff --git a/sample/another-remote-module/package.json b/samples/basic/another-remote-module/package.json similarity index 90% rename from sample/another-remote-module/package.json rename to samples/basic/another-remote-module/package.json index 73b67345e..f8886c395 100644 --- a/sample/another-remote-module/package.json +++ b/samples/basic/another-remote-module/package.json @@ -1,5 +1,5 @@ { - "name": "@sample/another-remote-module", + "name": "@basic/another-remote-module", "author": "Workleap", "version": "0.0.0", "description": "Another remote module to showcase @squide.", @@ -15,7 +15,6 @@ "devDependencies": { "@swc/core": "1.3.92", "@swc/helpers": "0.5.3", - "@squide/fakes": "workspace:*", "@types/react": "18.2.28", "@types/react-dom": "18.2.13", "@types/webpack": "5.28.3", @@ -24,7 +23,6 @@ "@workleap/swc-configs": "2.1.2", "@workleap/typescript-configs": "3.0.2", "@workleap/webpack-configs": "1.1.0", - "axios": "1.5.1", "browserslist": "4.22.1", "cross-env": "7.0.3", "http-server": "14.1.1", @@ -34,8 +32,9 @@ "webpack-dev-server": "4.15.1" }, "dependencies": { - "@sample/shared": "workspace:*", - "@sample/shell": "workspace:*", + "@basic/shared": "workspace:*", + "@basic/shell": "workspace:*", + "@squide/fakes": "workspace:*", "@squide/react-router": "workspace:*", "@squide/webpack-module-federation": "workspace:*", "react": "18.2.0", diff --git a/sample/remote-module/public/index.html b/samples/basic/another-remote-module/public/index.html similarity index 56% rename from sample/remote-module/public/index.html rename to samples/basic/another-remote-module/public/index.html index 98accb408..9b3f674a4 100644 --- a/sample/remote-module/public/index.html +++ b/samples/basic/another-remote-module/public/index.html @@ -1,7 +1,6 @@ -
diff --git a/samples/basic/another-remote-module/src/dev/App.tsx b/samples/basic/another-remote-module/src/dev/App.tsx new file mode 100644 index 000000000..0a831159f --- /dev/null +++ b/samples/basic/another-remote-module/src/dev/App.tsx @@ -0,0 +1,5 @@ +import { useAppRouter } from "@basic/shell"; + +export function App() { + return useAppRouter(); +} diff --git a/samples/basic/another-remote-module/src/dev/DevHomePage.tsx b/samples/basic/another-remote-module/src/dev/DevHomePage.tsx new file mode 100644 index 000000000..d3a4e66a0 --- /dev/null +++ b/samples/basic/another-remote-module/src/dev/DevHomePage.tsx @@ -0,0 +1,8 @@ +export function DevHomePage() { + return ( +
+

Another remote module development home page

+

Hey!

+
+ ); +} diff --git a/sample/local-module/src/dev/index.tsx b/samples/basic/another-remote-module/src/dev/index.tsx similarity index 95% rename from sample/local-module/src/dev/index.tsx rename to samples/basic/another-remote-module/src/dev/index.tsx index 11ef74b6c..42d730435 100644 --- a/sample/local-module/src/dev/index.tsx +++ b/samples/basic/another-remote-module/src/dev/index.tsx @@ -1,4 +1,4 @@ -import { registerShell } from "@sample/shell"; +import { registerShell } from "@basic/shell"; import { ConsoleLogger, Runtime, RuntimeContext, registerLocalModules } from "@squide/react-router"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; diff --git a/samples/basic/another-remote-module/src/dev/register.tsx b/samples/basic/another-remote-module/src/dev/register.tsx new file mode 100644 index 000000000..1072f8664 --- /dev/null +++ b/samples/basic/another-remote-module/src/dev/register.tsx @@ -0,0 +1,19 @@ +import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; +import { DevHomePage } from "./DevHomePage.tsx"; + +export const registerDev: ModuleRegisterFunction = runtime => { + runtime.registerRoute({ + index: true, + element: + }); + + runtime.registerRoute({ + path: "/federated-tabs", + lazy: () => import("@basic/shared/FederatedTabsLayout.tsx") + }); + + runtime.registerNavigationItem({ + $label: "Tabs", + to: "/federated-tabs" + }); +}; diff --git a/sample/remote-module/src/dev/session.ts b/samples/basic/another-remote-module/src/dev/session.ts similarity index 77% rename from sample/remote-module/src/dev/session.ts rename to samples/basic/another-remote-module/src/dev/session.ts index 6f20a918b..3432fc0a9 100644 --- a/sample/remote-module/src/dev/session.ts +++ b/samples/basic/another-remote-module/src/dev/session.ts @@ -1,8 +1,8 @@ -import type { Session } from "@sample/shared"; +import type { Session, SessionManager } from "@basic/shared"; import { LocalStorageSessionManager } from "@squide/fakes"; import type { SessionAccessorFunction } from "@squide/react-router"; -export const sessionManager = new LocalStorageSessionManager(); +export const sessionManager = new LocalStorageSessionManager() as SessionManager; export const sessionAccessor: SessionAccessorFunction = () => { return sessionManager.getSession(); diff --git a/sample/another-remote-module/src/register.tsx b/samples/basic/another-remote-module/src/register.tsx similarity index 73% rename from sample/another-remote-module/src/register.tsx rename to samples/basic/another-remote-module/src/register.tsx index 5714b8b20..039b15e7f 100644 --- a/sample/another-remote-module/src/register.tsx +++ b/samples/basic/another-remote-module/src/register.tsx @@ -3,12 +3,12 @@ import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; function registerRoutes(runtime: Runtime) { runtime.registerRoute({ path: "/federated-tabs", - lazy: () => import("@sample/shared/FederatedTabsLayout.tsx") + lazy: () => import("@basic/shared/FederatedTabsLayout.tsx") }); runtime.registerNavigationItem({ - to: "/federated-tabs", - $label: "Tabs" + $label: "Tabs", + to: "/federated-tabs" }); } diff --git a/sample/another-remote-module/swc.build.js b/samples/basic/another-remote-module/swc.build.js similarity index 100% rename from sample/another-remote-module/swc.build.js rename to samples/basic/another-remote-module/swc.build.js diff --git a/sample/another-remote-module/swc.dev.js b/samples/basic/another-remote-module/swc.dev.js similarity index 100% rename from sample/another-remote-module/swc.dev.js rename to samples/basic/another-remote-module/swc.dev.js diff --git a/samples/basic/another-remote-module/tsconfig.json b/samples/basic/another-remote-module/tsconfig.json new file mode 100644 index 000000000..6df5c8822 --- /dev/null +++ b/samples/basic/another-remote-module/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "@workleap/typescript-configs/web-application.json", + "compilerOptions": { + "paths": { + "@squide/core": ["../../../packages/core/src/index.ts"], + "@squide/react-router": ["../../../packages/react-router/src/index.ts"], + "@squide/webpack-module-federation": ["../../../packages/webpack-module-federation/src/index.ts"], + "@squide/webpack-module-federation/defineConfig.js": ["../../../packages/webpack-module-federation/src/defineConfig.js"], + "@squide/fakes": ["../../../packages/fakes/src/index.ts"], + "@basic/shell": ["../shell/src/index.ts"], + "@basic/shared": ["../shared/src/index.ts"], + "@basic/shared/FederatedTabsLayout.tsx": ["../shared/src/FederatedTabsLayout.tsx"] + } + }, + "exclude": ["dist", "node_modules"] +} diff --git a/sample/another-remote-module/webpack.build.js b/samples/basic/another-remote-module/webpack.build.js similarity index 95% rename from sample/another-remote-module/webpack.build.js rename to samples/basic/another-remote-module/webpack.build.js index 901112abf..6f0f124bc 100644 --- a/sample/another-remote-module/webpack.build.js +++ b/samples/basic/another-remote-module/webpack.build.js @@ -8,7 +8,7 @@ const publicPath = process.env.NETLIFY === "true" ? "https://squide-another-remo export default defineBuildRemoteModuleConfig(swcConfig, "remote2", publicPath, { sharedDependencies: { - "@sample/shared": { + "@basic/shared": { singleton: true } }, diff --git a/sample/another-remote-module/webpack.dev.js b/samples/basic/another-remote-module/webpack.dev.js similarity index 85% rename from sample/another-remote-module/webpack.dev.js rename to samples/basic/another-remote-module/webpack.dev.js index 418be45e6..11e10e376 100644 --- a/sample/another-remote-module/webpack.dev.js +++ b/samples/basic/another-remote-module/webpack.dev.js @@ -10,7 +10,7 @@ let config; if (!process.env.LOCAL) { config = defineDevRemoteModuleConfig(swcConfig, "remote2", 8082, { sharedDependencies: { - "@sample/shared": { + "@basic/shared": { singleton: true } }, @@ -20,8 +20,7 @@ if (!process.env.LOCAL) { }); } else { config = defineDevConfig(swcConfig, { - entry: path.resolve("./src/dev/index.tsx"), - overlay: false + entry: path.resolve("./src/dev/index.tsx") }); } diff --git a/sample/host/.browserslistrc b/samples/basic/host/.browserslistrc similarity index 100% rename from sample/host/.browserslistrc rename to samples/basic/host/.browserslistrc diff --git a/sample/host/.eslintrc.json b/samples/basic/host/.eslintrc.json similarity index 100% rename from sample/host/.eslintrc.json rename to samples/basic/host/.eslintrc.json diff --git a/sample/host/_redirects b/samples/basic/host/_redirects similarity index 100% rename from sample/host/_redirects rename to samples/basic/host/_redirects diff --git a/sample/host/nodemon.json b/samples/basic/host/nodemon.json similarity index 100% rename from sample/host/nodemon.json rename to samples/basic/host/nodemon.json diff --git a/samples/basic/host/package.json b/samples/basic/host/package.json new file mode 100644 index 000000000..f8ef43f6a --- /dev/null +++ b/samples/basic/host/package.json @@ -0,0 +1,47 @@ +{ + "name": "@basic/host", + "author": "Workleap", + "version": "0.0.0", + "description": "Host application to showcase @squide.", + "private": true, + "license": "Apache-2.0", + "type": "module", + "scripts": { + "dev": "nodemon", + "build": "pnpm build:webpack && pnpm build:copy-redirects", + "build:webpack": "webpack --config webpack.build.js", + "build:copy-redirects": "copyfiles _redirects dist", + "serve-build": "pnpm build && pnpm http-server dist -p 8080 -P http://localhost:8080? -c-1" + }, + "devDependencies": { + "@squide/fakes": "workspace:*", + "@swc/core": "1.3.92", + "@swc/helpers": "0.5.3", + "@types/react": "18.2.28", + "@types/react-dom": "18.2.13", + "@types/webpack": "5.28.3", + "@workleap/browserslist-config": "2.0.0", + "@workleap/eslint-plugin": "3.0.0", + "@workleap/swc-configs": "2.1.2", + "@workleap/typescript-configs": "3.0.2", + "@workleap/webpack-configs": "1.1.0", + "browserslist": "4.22.1", + "copyfiles": "2.4.1", + "http-server": "14.1.1", + "nodemon": "3.0.1", + "typescript": "5.2.2", + "webpack": "5.88.2", + "webpack-cli": "5.1.4", + "webpack-dev-server": "4.15.1" + }, + "dependencies": { + "@basic/local-module": "workspace:*", + "@basic/shared": "workspace:*", + "@basic/shell": "workspace:*", + "@squide/react-router": "workspace:*", + "@squide/webpack-module-federation": "workspace:*", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-router-dom": "6.16.0" + } +} diff --git a/sample/another-remote-module/public/favicon.png b/samples/basic/host/public/favicon.png similarity index 100% rename from sample/another-remote-module/public/favicon.png rename to samples/basic/host/public/favicon.png diff --git a/samples/basic/host/public/index.html b/samples/basic/host/public/index.html new file mode 100644 index 000000000..74bd0a31d --- /dev/null +++ b/samples/basic/host/public/index.html @@ -0,0 +1,9 @@ + + + + + + +
+ + diff --git a/samples/basic/host/src/App.tsx b/samples/basic/host/src/App.tsx new file mode 100644 index 000000000..8c2abe0a9 --- /dev/null +++ b/samples/basic/host/src/App.tsx @@ -0,0 +1,12 @@ +import { BackgroundColorContext } from "@basic/shared"; +import { useAppRouter } from "@basic/shell"; + +export function App() { + const appRouter = useAppRouter(); + + return ( + + {appRouter} + + ); +} diff --git a/sample/host/src/Home.tsx b/samples/basic/host/src/HomePage.tsx similarity index 62% rename from sample/host/src/Home.tsx rename to samples/basic/host/src/HomePage.tsx index 49317b3af..cb6e827dc 100644 --- a/sample/host/src/Home.tsx +++ b/samples/basic/host/src/HomePage.tsx @@ -1,4 +1,4 @@ -export function Home() { +export function HomePage() { return (

Home Page

@@ -7,4 +7,4 @@ export function Home() { ); } -export const Component = Home; +export const Component = HomePage; diff --git a/samples/basic/host/src/bootstrap.tsx b/samples/basic/host/src/bootstrap.tsx new file mode 100644 index 000000000..e33377760 --- /dev/null +++ b/samples/basic/host/src/bootstrap.tsx @@ -0,0 +1,44 @@ +import { registerLocalModule } from "@basic/local-module"; +import { isNetlify, type AppContext } from "@basic/shared"; +import { registerShell } from "@basic/shell"; +import { ConsoleLogger, Runtime, RuntimeContext, registerLocalModules } from "@squide/react-router"; +import { registerRemoteModules, type RemoteDefinition } from "@squide/webpack-module-federation"; +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import { App } from "./App.tsx"; +import { registerHost } from "./register.tsx"; +import { sessionAccessor, sessionManager } from "./session.ts"; + +const Remotes: RemoteDefinition[] = [ + { + name: "remote1", + url: isNetlify ? "https://squide-remote-module.netlify.app" : "http://localhost:8081" + }, + { + name: "remote2", + url: isNetlify ? "https://squide-another-remote-module.netlify.app" : "http://localhost:8082" + } +]; + +const runtime = new Runtime({ + loggers: [new ConsoleLogger()], + sessionAccessor +}); + +const context: AppContext = { + name: "Test app" +}; + +registerLocalModules([registerShell(sessionManager), registerHost, registerLocalModule], runtime, { context }); + +registerRemoteModules(Remotes, runtime, { context }); + +const root = createRoot(document.getElementById("root")!); + +root.render( + + + + + +); diff --git a/sample/host/src/index.ts b/samples/basic/host/src/index.ts similarity index 100% rename from sample/host/src/index.ts rename to samples/basic/host/src/index.ts diff --git a/sample/host/src/register.tsx b/samples/basic/host/src/register.tsx similarity index 82% rename from sample/host/src/register.tsx rename to samples/basic/host/src/register.tsx index a1cdf61c9..5c268efe9 100644 --- a/sample/host/src/register.tsx +++ b/samples/basic/host/src/register.tsx @@ -3,6 +3,6 @@ import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; export const registerHost: ModuleRegisterFunction = runtime => { runtime.registerRoute({ index: true, - lazy: () => import("./Home.tsx") + lazy: () => import("./HomePage.tsx") }); }; diff --git a/samples/basic/host/src/session.ts b/samples/basic/host/src/session.ts new file mode 100644 index 000000000..3432fc0a9 --- /dev/null +++ b/samples/basic/host/src/session.ts @@ -0,0 +1,9 @@ +import type { Session, SessionManager } from "@basic/shared"; +import { LocalStorageSessionManager } from "@squide/fakes"; +import type { SessionAccessorFunction } from "@squide/react-router"; + +export const sessionManager = new LocalStorageSessionManager() as SessionManager; + +export const sessionAccessor: SessionAccessorFunction = () => { + return sessionManager.getSession(); +}; diff --git a/sample/host/swc.build.js b/samples/basic/host/swc.build.js similarity index 100% rename from sample/host/swc.build.js rename to samples/basic/host/swc.build.js diff --git a/sample/host/swc.dev.js b/samples/basic/host/swc.dev.js similarity index 100% rename from sample/host/swc.dev.js rename to samples/basic/host/swc.dev.js diff --git a/samples/basic/host/tsconfig.json b/samples/basic/host/tsconfig.json new file mode 100644 index 000000000..677fbc79d --- /dev/null +++ b/samples/basic/host/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "@workleap/typescript-configs/web-application.json", + "compilerOptions": { + "paths": { + "@squide/core": ["../../../packages/core/src/index.ts"], + "@squide/react-router": ["../../../packages/react-router/src/index.ts"], + "@squide/webpack-module-federation": ["../../../packages/webpack-module-federation/src/index.ts"], + "@squide/webpack-module-federation/defineConfig.js": ["../../../packages/webpack-module-federation/src/defineConfig.js"], + "@squide/fakes": ["../../../packages/fakes/src/index.ts"], + "@basic/shell": ["../shell/src/index.ts"], + "@basic/shared": ["../shared/src/index.ts"], + "@basic/shared/FederatedTabsLayout.tsx": ["../shared/src/FederatedTabsLayout.tsx"], + "@basic/local-module": ["../local-module/src/register.ts"] + } + }, + "exclude": ["dist", "node_modules"] +} diff --git a/samples/basic/host/webpack.build.js b/samples/basic/host/webpack.build.js new file mode 100644 index 000000000..5e827aef3 --- /dev/null +++ b/samples/basic/host/webpack.build.js @@ -0,0 +1,20 @@ +// @ts-check + +import { defineBuildHostConfig } from "@squide/webpack-module-federation/defineConfig.js"; +import { swcConfig } from "./swc.build.js"; + +// The trailing / is very important, otherwise paths will not be resolved correctly. +const publicPath = process.env.NETLIFY === "true" ? "https://squide-host.netlify.app/" : "http://localhost:8080/"; + +export default defineBuildHostConfig(swcConfig, "host", publicPath, { + sharedDependencies: { + "@basic/shared": { + singleton: true, + eager: true + } + }, + environmentVariables: { + "NETLIFY": process.env.NETLIFY === "true" + } +}); + diff --git a/samples/basic/host/webpack.dev.js b/samples/basic/host/webpack.dev.js new file mode 100644 index 000000000..442f63890 --- /dev/null +++ b/samples/basic/host/webpack.dev.js @@ -0,0 +1,17 @@ +// @ts-check + +import { defineDevHostConfig } from "@squide/webpack-module-federation/defineConfig.js"; +import { swcConfig } from "./swc.dev.js"; + +export default defineDevHostConfig(swcConfig, "host", 8080, { + overlay: false, + sharedDependencies: { + "@basic/shared": { + singleton: true, + eager: true + } + }, + environmentVariables: { + "NETLIFY": process.env.NETLIFY === "true" + } +}); diff --git a/sample/local-module/.browserslistrc b/samples/basic/local-module/.browserslistrc similarity index 100% rename from sample/local-module/.browserslistrc rename to samples/basic/local-module/.browserslistrc diff --git a/sample/local-module/.eslintrc.json b/samples/basic/local-module/.eslintrc.json similarity index 100% rename from sample/local-module/.eslintrc.json rename to samples/basic/local-module/.eslintrc.json diff --git a/sample/local-module/nodemon.json b/samples/basic/local-module/nodemon.json similarity index 100% rename from sample/local-module/nodemon.json rename to samples/basic/local-module/nodemon.json diff --git a/sample/local-module/nodemon.local.json b/samples/basic/local-module/nodemon.local.json similarity index 100% rename from sample/local-module/nodemon.local.json rename to samples/basic/local-module/nodemon.local.json diff --git a/sample/local-module/package.json b/samples/basic/local-module/package.json similarity index 87% rename from sample/local-module/package.json rename to samples/basic/local-module/package.json index a83b05632..001f54372 100644 --- a/sample/local-module/package.json +++ b/samples/basic/local-module/package.json @@ -1,5 +1,5 @@ { - "name": "@sample/local-module", + "name": "@basic/local-module", "author": "Workleap", "version": "0.0.0", "description": "Local module to showcase @squide.", @@ -20,6 +20,10 @@ "serve-build": "pnpm build" }, "peerDependencies": { + "@basic/shared": "*", + "@basic/shell": "*", + "@squide/fakes": "*", + "@squide/react-router": "*", "react": "*", "react-dom": "*", "react-router-dom": "*" @@ -27,7 +31,6 @@ "devDependencies": { "@swc/core": "1.3.92", "@swc/helpers": "0.5.3", - "@squide/fakes": "workspace:*", "@types/react": "18.2.28", "@types/react-dom": "18.2.13", "@types/webpack": "5.28.3", @@ -37,7 +40,6 @@ "@workleap/tsup-configs": "3.0.1", "@workleap/typescript-configs": "3.0.2", "@workleap/webpack-configs": "1.1.0", - "axios": "1.5.1", "browserslist": "4.22.1", "cross-env": "7.0.3", "nodemon": "3.0.1", @@ -48,8 +50,9 @@ "webpack-dev-server": "4.15.1" }, "dependencies": { - "@sample/shared": "workspace:*", - "@sample/shell": "workspace:*", + "@basic/shared": "workspace:*", + "@basic/shell": "workspace:*", + "@squide/fakes": "workspace:*", "@squide/react-router": "workspace:*", "react": "18.2.0", "react-dom": "18.2.0", diff --git a/samples/basic/local-module/public/index.html b/samples/basic/local-module/public/index.html new file mode 100644 index 000000000..a667bb440 --- /dev/null +++ b/samples/basic/local-module/public/index.html @@ -0,0 +1,8 @@ + + + + + +
+ + diff --git a/sample/local-module/src/About.tsx b/samples/basic/local-module/src/AboutPage.tsx similarity index 75% rename from sample/local-module/src/About.tsx rename to samples/basic/local-module/src/AboutPage.tsx index 580696b49..46998bace 100644 --- a/sample/local-module/src/About.tsx +++ b/samples/basic/local-module/src/AboutPage.tsx @@ -1,6 +1,6 @@ import { Link } from "react-router-dom"; -export function About() { +export function AboutPage() { return (

About

@@ -10,4 +10,4 @@ export function About() { ); } -export const Component = About; +export const Component = AboutPage; diff --git a/sample/local-module/src/Message.tsx b/samples/basic/local-module/src/MessagePage.tsx similarity index 87% rename from sample/local-module/src/Message.tsx rename to samples/basic/local-module/src/MessagePage.tsx index ce72df570..81d53dc8a 100644 --- a/sample/local-module/src/Message.tsx +++ b/samples/basic/local-module/src/MessagePage.tsx @@ -1,9 +1,8 @@ +import { useApplicationEventBusDispatcher } from "@basic/shared"; import { useCallback, useState, type ChangeEvent } from "react"; - -import { useApplicationEventBusDispatcher } from "@sample/shared"; import { Link } from "react-router-dom"; -export function Message() { +export function MessagePage() { const [message, setMessage] = useState(""); const handleTextChange = useCallback((event: ChangeEvent) => { @@ -30,4 +29,4 @@ export function Message() { ); } -export const Component = Message; +export const Component = MessagePage; diff --git a/sample/local-module/src/WorkleapTab.tsx b/samples/basic/local-module/src/WorkleapTab.tsx similarity index 66% rename from sample/local-module/src/WorkleapTab.tsx rename to samples/basic/local-module/src/WorkleapTab.tsx index 6b2e9ec12..8702c07f1 100644 --- a/sample/local-module/src/WorkleapTab.tsx +++ b/samples/basic/local-module/src/WorkleapTab.tsx @@ -1,7 +1,7 @@ export function WorkleapTab() { return (
-

This is the Workleap tab from @sample/local-module!

+

This is the Workleap tab from @basic/local-module!

); } diff --git a/samples/basic/local-module/src/dev/App.tsx b/samples/basic/local-module/src/dev/App.tsx new file mode 100644 index 000000000..0a831159f --- /dev/null +++ b/samples/basic/local-module/src/dev/App.tsx @@ -0,0 +1,5 @@ +import { useAppRouter } from "@basic/shell"; + +export function App() { + return useAppRouter(); +} diff --git a/sample/another-remote-module/src/dev/DevHome.tsx b/samples/basic/local-module/src/dev/DevHome.tsx similarity index 100% rename from sample/another-remote-module/src/dev/DevHome.tsx rename to samples/basic/local-module/src/dev/DevHome.tsx diff --git a/samples/basic/local-module/src/dev/index.tsx b/samples/basic/local-module/src/dev/index.tsx new file mode 100644 index 000000000..42d730435 --- /dev/null +++ b/samples/basic/local-module/src/dev/index.tsx @@ -0,0 +1,29 @@ +import { registerShell } from "@basic/shell"; +import { ConsoleLogger, Runtime, RuntimeContext, registerLocalModules } from "@squide/react-router"; +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import { registerLocalModule } from "../register.tsx"; +import { App } from "./App.tsx"; +import { registerDev } from "./register.tsx"; +import { sessionAccessor, sessionManager } from "./session.ts"; + +// Create the shell runtime. +// Services, loggers and sessionAccessor could be reuse through a shared packages or faked when in isolation. +const runtime = new Runtime({ + loggers: [new ConsoleLogger()], + sessionAccessor +}); + +registerLocalModules([registerShell(sessionManager), registerDev, registerLocalModule], runtime); + +const root = createRoot(document.getElementById("root")!); + +root.render( + + + + + +); + + diff --git a/sample/local-module/src/dev/register.tsx b/samples/basic/local-module/src/dev/register.tsx similarity index 86% rename from sample/local-module/src/dev/register.tsx rename to samples/basic/local-module/src/dev/register.tsx index 607f60cd5..126befcb1 100644 --- a/sample/local-module/src/dev/register.tsx +++ b/samples/basic/local-module/src/dev/register.tsx @@ -9,7 +9,7 @@ export const registerDev: ModuleRegisterFunction = runtime => { runtime.registerRoute({ path: "/federated-tabs", - lazy: () => import("@sample/shared/FederatedTabsLayout.tsx") + lazy: () => import("@basic/shared/FederatedTabsLayout.tsx") }); runtime.registerNavigationItem({ diff --git a/samples/basic/local-module/src/dev/session.ts b/samples/basic/local-module/src/dev/session.ts new file mode 100644 index 000000000..3432fc0a9 --- /dev/null +++ b/samples/basic/local-module/src/dev/session.ts @@ -0,0 +1,9 @@ +import type { Session, SessionManager } from "@basic/shared"; +import { LocalStorageSessionManager } from "@squide/fakes"; +import type { SessionAccessorFunction } from "@squide/react-router"; + +export const sessionManager = new LocalStorageSessionManager() as SessionManager; + +export const sessionAccessor: SessionAccessorFunction = () => { + return sessionManager.getSession(); +}; diff --git a/sample/local-module/src/register.tsx b/samples/basic/local-module/src/register.tsx similarity index 89% rename from sample/local-module/src/register.tsx rename to samples/basic/local-module/src/register.tsx index ad8688c8a..9e2f69f0a 100644 --- a/sample/local-module/src/register.tsx +++ b/samples/basic/local-module/src/register.tsx @@ -1,15 +1,15 @@ -import type { AppContext } from "@sample/shared"; +import type { AppContext } from "@basic/shared"; import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; function registerRoutes(runtime: Runtime) { runtime.registerRoute({ path: "/about", - lazy: () => import("./About.tsx") + lazy: () => import("./AboutPage.tsx") }); runtime.registerRoute({ path: "/message", - lazy: () => import("./Message.tsx") + lazy: () => import("./MessagePage.tsx") }); runtime.registerNavigationItem({ diff --git a/sample/local-module/swc.config.js b/samples/basic/local-module/swc.config.js similarity index 100% rename from sample/local-module/swc.config.js rename to samples/basic/local-module/swc.config.js diff --git a/samples/basic/local-module/tsconfig.json b/samples/basic/local-module/tsconfig.json new file mode 100644 index 000000000..78cd9986f --- /dev/null +++ b/samples/basic/local-module/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "@workleap/typescript-configs/library.json", + "compilerOptions": { + "paths": { + "@squide/core": ["../../../packages/core/src/index.ts"], + "@squide/react-router": ["../../../packages/react-router/src/index.ts"], + "@squide/webpack-module-federation": ["../../../packages/webpack-module-federation/src/index.ts"], + "@squide/webpack-module-federation/defineConfig.js": ["../../../packages/webpack-module-federation/src/defineConfig.js"], + "@squide/fakes": ["../../../packages/fakes/src/index.ts"], + "@basic/shell": ["../shell/src/index.ts"], + "@basic/shared": ["../shared/src/index.ts"], + "@basic/shared/FederatedTabsLayout.tsx": ["../shared/src/FederatedTabsLayout.tsx"] + } + }, + "exclude": ["dist", "node_modules"] +} diff --git a/sample/local-module/tsup.build.ts b/samples/basic/local-module/tsup.build.ts similarity index 100% rename from sample/local-module/tsup.build.ts rename to samples/basic/local-module/tsup.build.ts diff --git a/sample/local-module/tsup.dev.ts b/samples/basic/local-module/tsup.dev.ts similarity index 100% rename from sample/local-module/tsup.dev.ts rename to samples/basic/local-module/tsup.dev.ts diff --git a/sample/local-module/webpack.config.js b/samples/basic/local-module/webpack.config.js similarity index 74% rename from sample/local-module/webpack.config.js rename to samples/basic/local-module/webpack.config.js index d830cda47..442f96b30 100644 --- a/sample/local-module/webpack.config.js +++ b/samples/basic/local-module/webpack.config.js @@ -5,7 +5,6 @@ import path from "node:path"; import { swcConfig } from "./swc.config.js"; export default defineDevConfig(swcConfig, { - entry: path.resolve("./src/dev/index.tsx"), - overlay: false + entry: path.resolve("./src/dev/index.tsx") }); diff --git a/sample/remote-module/.browserslistrc b/samples/basic/remote-module/.browserslistrc similarity index 100% rename from sample/remote-module/.browserslistrc rename to samples/basic/remote-module/.browserslistrc diff --git a/sample/remote-module/.eslintrc.json b/samples/basic/remote-module/.eslintrc.json similarity index 100% rename from sample/remote-module/.eslintrc.json rename to samples/basic/remote-module/.eslintrc.json diff --git a/sample/remote-module/nodemon.json b/samples/basic/remote-module/nodemon.json similarity index 100% rename from sample/remote-module/nodemon.json rename to samples/basic/remote-module/nodemon.json diff --git a/samples/basic/remote-module/package.json b/samples/basic/remote-module/package.json new file mode 100644 index 000000000..d1ca21f0d --- /dev/null +++ b/samples/basic/remote-module/package.json @@ -0,0 +1,45 @@ +{ + "name": "@basic/remote-module", + "author": "Workleap", + "version": "0.0.0", + "description": "Remote module to showcase @squide.", + "private": true, + "license": "Apache-2.0", + "type": "module", + "scripts": { + "dev": "nodemon", + "dev-local": "cross-env LOCAL=true nodemon", + "build": "webpack --config webpack.build.js", + "serve-build": "pnpm build && pnpm http-server dist -p 8081 -P http://localhost:8081? -c-1" + }, + "devDependencies": { + "@swc/core": "1.3.92", + "@swc/helpers": "0.5.3", + "@types/react": "18.2.28", + "@types/react-dom": "18.2.13", + "@types/webpack": "5.28.3", + "@workleap/browserslist-config": "2.0.0", + "@workleap/eslint-plugin": "3.0.0", + "@workleap/swc-configs": "2.1.2", + "@workleap/typescript-configs": "3.0.2", + "@workleap/webpack-configs": "1.1.0", + "browserslist": "4.22.1", + "cross-env": "7.0.3", + "http-server": "14.1.1", + "nodemon": "3.0.1", + "typescript": "5.2.2", + "webpack": "5.88.2", + "webpack-cli": "5.1.4", + "webpack-dev-server": "4.15.1" + }, + "dependencies": { + "@basic/shared": "workspace:*", + "@basic/shell": "workspace:*", + "@squide/fakes": "workspace:*", + "@squide/react-router": "workspace:*", + "@squide/webpack-module-federation": "workspace:*", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-router-dom": "6.16.0" + } +} diff --git a/samples/basic/remote-module/public/index.html b/samples/basic/remote-module/public/index.html new file mode 100644 index 000000000..9b3f674a4 --- /dev/null +++ b/samples/basic/remote-module/public/index.html @@ -0,0 +1,8 @@ + + + + + +
+ + diff --git a/sample/remote-module/src/ColoredPage.tsx b/samples/basic/remote-module/src/ColoredPage.tsx similarity index 82% rename from sample/remote-module/src/ColoredPage.tsx rename to samples/basic/remote-module/src/ColoredPage.tsx index afe2781ac..f022f1600 100644 --- a/sample/remote-module/src/ColoredPage.tsx +++ b/samples/basic/remote-module/src/ColoredPage.tsx @@ -1,4 +1,4 @@ -import { useBackgroundColor } from "@sample/shared"; +import { useBackgroundColor } from "@basic/shared"; export function ColoredPage() { const backgroundColor = useBackgroundColor(); diff --git a/sample/remote-module/src/CustomLayout.tsx b/samples/basic/remote-module/src/CustomLayout.tsx similarity index 100% rename from sample/remote-module/src/CustomLayout.tsx rename to samples/basic/remote-module/src/CustomLayout.tsx diff --git a/sample/remote-module/src/Hoisted.tsx b/samples/basic/remote-module/src/HoistedPage.tsx similarity index 83% rename from sample/remote-module/src/Hoisted.tsx rename to samples/basic/remote-module/src/HoistedPage.tsx index 16c895e0c..4fc920c3e 100644 --- a/sample/remote-module/src/Hoisted.tsx +++ b/samples/basic/remote-module/src/HoistedPage.tsx @@ -1,6 +1,6 @@ import { Link } from "react-router-dom"; -export function Hoisted() { +export function HoistedPage() { return (

Hoisted

@@ -10,4 +10,4 @@ export function Hoisted() { ); } -export const Component = Hoisted; +export const Component = HoistedPage; diff --git a/sample/remote-module/src/OfficevibeTab.tsx b/samples/basic/remote-module/src/OfficevibeTab.tsx similarity index 88% rename from sample/remote-module/src/OfficevibeTab.tsx rename to samples/basic/remote-module/src/OfficevibeTab.tsx index 687a03cfa..38a1b44bc 100644 --- a/sample/remote-module/src/OfficevibeTab.tsx +++ b/samples/basic/remote-module/src/OfficevibeTab.tsx @@ -1,7 +1,7 @@ export function OfficevibeTab() { return (
-

This is the Officevibe tab from @sample/remote-module!

+

This is the Officevibe tab from @basic/remote-module!

); } diff --git a/sample/remote-module/src/Remote.tsx b/samples/basic/remote-module/src/RemotePage.tsx similarity index 76% rename from sample/remote-module/src/Remote.tsx rename to samples/basic/remote-module/src/RemotePage.tsx index b9a700b24..851b4fd1b 100644 --- a/sample/remote-module/src/Remote.tsx +++ b/samples/basic/remote-module/src/RemotePage.tsx @@ -1,6 +1,6 @@ import { Link } from "react-router-dom"; -export function Remote() { +export function RemotePage() { return (

Remote

@@ -10,4 +10,4 @@ export function Remote() { ); } -export const Component = Remote; +export const Component = RemotePage; diff --git a/sample/remote-module/src/SkillsTab.tsx b/samples/basic/remote-module/src/SkillsTab.tsx similarity index 65% rename from sample/remote-module/src/SkillsTab.tsx rename to samples/basic/remote-module/src/SkillsTab.tsx index 90ea8fcd5..13552e026 100644 --- a/sample/remote-module/src/SkillsTab.tsx +++ b/samples/basic/remote-module/src/SkillsTab.tsx @@ -1,7 +1,7 @@ export function SkillsTab() { return (
-

This is the Skills tab from @sample/remote-module!

+

This is the Skills tab from @basic/remote-module!

); } diff --git a/samples/basic/remote-module/src/dev/App.tsx b/samples/basic/remote-module/src/dev/App.tsx new file mode 100644 index 000000000..e742d752b --- /dev/null +++ b/samples/basic/remote-module/src/dev/App.tsx @@ -0,0 +1,13 @@ +import { BackgroundColorContext } from "@basic/shared"; +import { useAppRouter } from "@basic/shell"; + +export function App() { + const appRouter = useAppRouter(); + + return ( + + {appRouter} + + ); +} + diff --git a/sample/local-module/src/dev/DevHome.tsx b/samples/basic/remote-module/src/dev/DevHome.tsx similarity index 100% rename from sample/local-module/src/dev/DevHome.tsx rename to samples/basic/remote-module/src/dev/DevHome.tsx diff --git a/sample/another-remote-module/src/dev/index.tsx b/samples/basic/remote-module/src/dev/index.tsx similarity index 95% rename from sample/another-remote-module/src/dev/index.tsx rename to samples/basic/remote-module/src/dev/index.tsx index ea0f88846..af125f7c2 100644 --- a/sample/another-remote-module/src/dev/index.tsx +++ b/samples/basic/remote-module/src/dev/index.tsx @@ -1,4 +1,4 @@ -import { registerShell } from "@sample/shell"; +import { registerShell } from "@basic/shell"; import { ConsoleLogger, Runtime, RuntimeContext, registerLocalModules } from "@squide/react-router"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; diff --git a/sample/remote-module/src/dev/register.tsx b/samples/basic/remote-module/src/dev/register.tsx similarity index 86% rename from sample/remote-module/src/dev/register.tsx rename to samples/basic/remote-module/src/dev/register.tsx index 607f60cd5..126befcb1 100644 --- a/sample/remote-module/src/dev/register.tsx +++ b/samples/basic/remote-module/src/dev/register.tsx @@ -9,7 +9,7 @@ export const registerDev: ModuleRegisterFunction = runtime => { runtime.registerRoute({ path: "/federated-tabs", - lazy: () => import("@sample/shared/FederatedTabsLayout.tsx") + lazy: () => import("@basic/shared/FederatedTabsLayout.tsx") }); runtime.registerNavigationItem({ diff --git a/samples/basic/remote-module/src/dev/session.ts b/samples/basic/remote-module/src/dev/session.ts new file mode 100644 index 000000000..3432fc0a9 --- /dev/null +++ b/samples/basic/remote-module/src/dev/session.ts @@ -0,0 +1,9 @@ +import type { Session, SessionManager } from "@basic/shared"; +import { LocalStorageSessionManager } from "@squide/fakes"; +import type { SessionAccessorFunction } from "@squide/react-router"; + +export const sessionManager = new LocalStorageSessionManager() as SessionManager; + +export const sessionAccessor: SessionAccessorFunction = () => { + return sessionManager.getSession(); +}; diff --git a/sample/remote-module/src/register.tsx b/samples/basic/remote-module/src/register.tsx similarity index 63% rename from sample/remote-module/src/register.tsx rename to samples/basic/remote-module/src/register.tsx index 0197fcc82..002bb20f6 100644 --- a/sample/remote-module/src/register.tsx +++ b/samples/basic/remote-module/src/register.tsx @@ -1,46 +1,10 @@ -import { BackgroundColorContext } from "@sample/shared"; -import { getMswPlugin } from "@squide/msw"; +import { BackgroundColorContext } from "@basic/shared"; import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; -import type { ReactNode } from "react"; -import { requestHandlers } from "../mocks/handlers.ts"; - -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - refetchOnWindowFocus: false, - retry: failureCount => { - return failureCount <= 2; - } - } - } -}); - -function Providers({ children }: { children: ReactNode }) { - return ( - - {children} - - - ); -} function registerRoutes(runtime: Runtime) { runtime.registerRoute({ path: "/remote", - lazy: async () => import("./Remote.tsx") - }); - - runtime.registerRoute({ - path: "/fetch", - lazy: async () => { - const { Fetch } = await import("./Fetch.tsx"); - - return { - element: - }; - } + lazy: async () => import("./RemotePage.tsx") }); runtime.registerRoute({ @@ -49,7 +13,7 @@ function registerRoutes(runtime: Runtime) { children: [ { index: true, - lazy: () => import("./Hoisted.tsx") + lazy: () => import("./HoistedPage.tsx") } ] }, { @@ -81,11 +45,6 @@ function registerRoutes(runtime: Runtime) { to: "/remote" }); - runtime.registerNavigationItem({ - $label: "Fetch", - to: "/fetch" - }); - runtime.registerNavigationItem({ $label: Hoisted, to: "/hoisted" @@ -147,13 +106,6 @@ function registerRoutes(runtime: Runtime) { }); } -function registerMsw(runtime: Runtime) { - const mswPlugin = getMswPlugin(runtime); - - mswPlugin.registerRequestHandlers(requestHandlers); -} - export const register: ModuleRegisterFunction = runtime => { registerRoutes(runtime); - registerMsw(runtime); }; diff --git a/sample/remote-module/swc.build.js b/samples/basic/remote-module/swc.build.js similarity index 100% rename from sample/remote-module/swc.build.js rename to samples/basic/remote-module/swc.build.js diff --git a/sample/remote-module/swc.dev.js b/samples/basic/remote-module/swc.dev.js similarity index 100% rename from sample/remote-module/swc.dev.js rename to samples/basic/remote-module/swc.dev.js diff --git a/samples/basic/remote-module/tsconfig.json b/samples/basic/remote-module/tsconfig.json new file mode 100644 index 000000000..6df5c8822 --- /dev/null +++ b/samples/basic/remote-module/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "@workleap/typescript-configs/web-application.json", + "compilerOptions": { + "paths": { + "@squide/core": ["../../../packages/core/src/index.ts"], + "@squide/react-router": ["../../../packages/react-router/src/index.ts"], + "@squide/webpack-module-federation": ["../../../packages/webpack-module-federation/src/index.ts"], + "@squide/webpack-module-federation/defineConfig.js": ["../../../packages/webpack-module-federation/src/defineConfig.js"], + "@squide/fakes": ["../../../packages/fakes/src/index.ts"], + "@basic/shell": ["../shell/src/index.ts"], + "@basic/shared": ["../shared/src/index.ts"], + "@basic/shared/FederatedTabsLayout.tsx": ["../shared/src/FederatedTabsLayout.tsx"] + } + }, + "exclude": ["dist", "node_modules"] +} diff --git a/samples/basic/remote-module/webpack.build.js b/samples/basic/remote-module/webpack.build.js new file mode 100644 index 000000000..c5710dc85 --- /dev/null +++ b/samples/basic/remote-module/webpack.build.js @@ -0,0 +1,18 @@ +// @ts-check + +import { defineBuildRemoteModuleConfig } from "@squide/webpack-module-federation/defineConfig.js"; +import { swcConfig } from "./swc.build.js"; + +// The trailing / is very important, otherwise paths will not be resolved correctly. +const publicPath = process.env.NETLIFY === "true" ? "https://squide-remote-module.netlify.app/" : "http://localhost:8081/"; + +export default defineBuildRemoteModuleConfig(swcConfig, "remote1", publicPath, { + sharedDependencies: { + "@basic/shared": { + singleton: true + } + }, + environmentVariables: { + "NETLIFY": process.env.NETLIFY === "true" + } +}); diff --git a/samples/basic/remote-module/webpack.dev.js b/samples/basic/remote-module/webpack.dev.js new file mode 100644 index 000000000..49eea2ece --- /dev/null +++ b/samples/basic/remote-module/webpack.dev.js @@ -0,0 +1,28 @@ +// @ts-check + +import { defineDevRemoteModuleConfig } from "@squide/webpack-module-federation/defineConfig.js"; +import { defineDevConfig } from "@workleap/webpack-configs"; +import path from "node:path"; +import { swcConfig } from "./swc.dev.js"; + +let config; + +if (!process.env.LOCAL) { + config = defineDevRemoteModuleConfig(swcConfig, "remote1", 8081, { + sharedDependencies: { + "@basic/shared": { + singleton: true + } + }, + environmentVariables: { + "NETLIFY": process.env.NETLIFY === "true" + } + }); +} else { + config = defineDevConfig(swcConfig, { + entry: path.resolve("./src/dev/index.tsx") + }); +} + +export default config; + diff --git a/sample/shared/.eslintrc.json b/samples/basic/shared/.eslintrc.json similarity index 100% rename from sample/shared/.eslintrc.json rename to samples/basic/shared/.eslintrc.json diff --git a/sample/shared/nodemon.json b/samples/basic/shared/nodemon.json similarity index 100% rename from sample/shared/nodemon.json rename to samples/basic/shared/nodemon.json diff --git a/sample/shared/package.json b/samples/basic/shared/package.json similarity index 95% rename from sample/shared/package.json rename to samples/basic/shared/package.json index 8ea2c8d71..d6d46cc6b 100644 --- a/sample/shared/package.json +++ b/samples/basic/shared/package.json @@ -1,5 +1,5 @@ { - "name": "@sample/shared", + "name": "@basic/shared", "author": "Workleap", "version": "0.0.0", "description": "Shared package to showcase @squide.", @@ -20,7 +20,6 @@ }, "scripts": { "dev": "nodemon", - "dev-msw": "pnpm nodemon", "build": "tsup --config ./tsup.build.ts", "serve-build": "pnpm build" }, diff --git a/sample/shared/src/BackgroundColorContext.ts b/samples/basic/shared/src/BackgroundColorContext.ts similarity index 100% rename from sample/shared/src/BackgroundColorContext.ts rename to samples/basic/shared/src/BackgroundColorContext.ts diff --git a/sample/shared/src/FederatedTabsLayout.tsx b/samples/basic/shared/src/FederatedTabsLayout.tsx similarity index 96% rename from sample/shared/src/FederatedTabsLayout.tsx rename to samples/basic/shared/src/FederatedTabsLayout.tsx index de11ccbe2..5a74ae5c8 100644 --- a/sample/shared/src/FederatedTabsLayout.tsx +++ b/samples/basic/shared/src/FederatedTabsLayout.tsx @@ -30,7 +30,7 @@ export function FederatedTabsLayout() {

Every tab is registered by a different module and is lazy loaded.

{renderedTabs} -
+
Loading...
}> diff --git a/sample/shared/src/appContext.ts b/samples/basic/shared/src/appContext.ts similarity index 62% rename from sample/shared/src/appContext.ts rename to samples/basic/shared/src/appContext.ts index ea92aee21..5bec8965b 100644 --- a/sample/shared/src/appContext.ts +++ b/samples/basic/shared/src/appContext.ts @@ -1,3 +1,3 @@ export interface AppContext { - name?: string; + name: string; } diff --git a/sample/shared/src/eventBus.ts b/samples/basic/shared/src/eventBus.ts similarity index 100% rename from sample/shared/src/eventBus.ts rename to samples/basic/shared/src/eventBus.ts diff --git a/sample/shared/src/index.ts b/samples/basic/shared/src/index.ts similarity index 69% rename from sample/shared/src/index.ts rename to samples/basic/shared/src/index.ts index 7db82352d..c487c2f74 100644 --- a/sample/shared/src/index.ts +++ b/samples/basic/shared/src/index.ts @@ -3,6 +3,4 @@ export * from "./appContext.ts"; export * from "./eventBus.ts"; export * from "./isNetlify.ts"; export * from "./session.ts"; -export * from "./subscription.ts"; -export * from "./telemetryService.ts"; diff --git a/sample/shared/src/isNetlify.ts b/samples/basic/shared/src/isNetlify.ts similarity index 100% rename from sample/shared/src/isNetlify.ts rename to samples/basic/shared/src/isNetlify.ts diff --git a/samples/basic/shared/src/session.ts b/samples/basic/shared/src/session.ts new file mode 100644 index 000000000..a433b0c30 --- /dev/null +++ b/samples/basic/shared/src/session.ts @@ -0,0 +1,11 @@ +export interface Session { + user: { + name: string; + }; +} + +export interface SessionManager { + setSession: (session: Session) => void; + getSession: () => Session; + clearSession: () => void; +} diff --git a/samples/basic/shared/tsconfig.json b/samples/basic/shared/tsconfig.json new file mode 100644 index 000000000..3e87a3590 --- /dev/null +++ b/samples/basic/shared/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "@workleap/typescript-configs/library.json", + "compilerOptions": { + "paths": { + "@squide/core": ["../../../packages/core/src/index.ts"], + "@squide/react-router": ["../../../packages/react-router/src/index.ts"], + "@squide/webpack-module-federation": ["../../../packages/webpack-module-federation/src/index.ts"], + "@squide/webpack-module-federation/defineConfig.js": ["../../../packages/webpack-module-federation/src/defineConfig.js"], + "@squide/fakes": ["../../../packages/fakes/src/index.ts"] + } + }, + "exclude": ["dist", "node_modules"] +} diff --git a/sample/shared/tsup.build.ts b/samples/basic/shared/tsup.build.ts similarity index 100% rename from sample/shared/tsup.build.ts rename to samples/basic/shared/tsup.build.ts diff --git a/sample/shared/tsup.dev.ts b/samples/basic/shared/tsup.dev.ts similarity index 100% rename from sample/shared/tsup.dev.ts rename to samples/basic/shared/tsup.dev.ts diff --git a/sample/shell/.eslintrc.json b/samples/basic/shell/.eslintrc.json similarity index 100% rename from sample/shell/.eslintrc.json rename to samples/basic/shell/.eslintrc.json diff --git a/sample/shell/nodemon.json b/samples/basic/shell/nodemon.json similarity index 100% rename from sample/shell/nodemon.json rename to samples/basic/shell/nodemon.json diff --git a/samples/basic/shell/package.json b/samples/basic/shell/package.json new file mode 100644 index 000000000..c09e725a3 --- /dev/null +++ b/samples/basic/shell/package.json @@ -0,0 +1,46 @@ +{ + "name": "@basic/shell", + "author": "Workleap", + "version": "0.0.0", + "description": "Application shell package to showcase @squide.", + "private": true, + "license": "Apache-2.0", + "type": "module", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "scripts": { + "dev": "nodemon", + "build": "tsup --config ./tsup.build.ts", + "serve-build": "pnpm build" + }, + "peerDependencies": { + "@basic/shared": "*", + "@squide/react-router": "*", + "@squide/webpack-module-federation": "*", + "react": "*", + "react-dom": "*", + "react-router-dom": "*" + }, + "devDependencies": { + "@remix-run/router": "1.9.0", + "@basic/shared": "workspace:*", + "@squide/react-router": "workspace:*", + "@squide/webpack-module-federation": "1.0.5", + "@types/react": "18.2.28", + "@types/react-dom": "18.2.13", + "@workleap/eslint-plugin": "3.0.0", + "@workleap/tsup-configs": "3.0.1", + "@workleap/typescript-configs": "3.0.2", + "nodemon": "3.0.1", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-router-dom": "6.16.0", + "tsup": "7.2.0", + "typescript": "5.2.2" + } +} diff --git a/samples/basic/shell/src/AuthenticatedLayout.tsx b/samples/basic/shell/src/AuthenticatedLayout.tsx new file mode 100644 index 000000000..ba0cccc29 --- /dev/null +++ b/samples/basic/shell/src/AuthenticatedLayout.tsx @@ -0,0 +1,87 @@ +import { useApplicationEventBusListener, type Session, type SessionManager } from "@basic/shared"; +import { isNavigationLink, useNavigationItems, useRenderedNavigationItems, useSession, type NavigationLinkRenderProps, type NavigationSectionRenderProps, type RenderItemFunction, type RenderSectionFunction } from "@squide/react-router"; +import { Suspense, useCallback, type MouseEvent, type ReactNode } from "react"; +import { Link, Outlet, useNavigate } from "react-router-dom"; + +type RenderLinkItemFunction = (item: NavigationLinkRenderProps, index: number, level: number) => ReactNode; + +type RenderSectionItemFunction = (item: NavigationSectionRenderProps, index: number, level: number) => ReactNode; + +const renderLinkItem: RenderLinkItemFunction = ({ label, linkProps, additionalProps: { highlight, ...additionalProps } }, index, level) => { + return ( +
  • + + {label} + +
  • + ); +}; + +const renderSectionItem: RenderSectionItemFunction = ({ label, section }, index, level) => { + return ( +
  • + {label} +
    + ({section}) +
    +
  • + ); +}; + +const renderItem: RenderItemFunction = (item, index, level) => { + return isNavigationLink(item) ? renderLinkItem(item, index, level) : renderSectionItem(item, index, level); +}; + +const renderSection: RenderSectionFunction = (elements, index, level) => { + return ( +
      + {elements} +
    + ); +}; + +export interface AuthenticatedLayoutProps { + sessionManager: SessionManager; +} + +export function AuthenticatedLayout({ sessionManager }: AuthenticatedLayoutProps) { + const session = useSession() as Session; + + const navigate = useNavigate(); + + const handleModulesMessage = useCallback((data: unknown) => { + console.log("[sample] Message received from a module: ", data); + }, []); + + useApplicationEventBusListener("write-to-host", handleModulesMessage); + + const onDisconnect = useCallback(async (event: MouseEvent) => { + event.preventDefault(); + + sessionManager.clearSession(); + + navigate("/logout"); + }, [navigate, sessionManager]); + + const navigationItems = useNavigationItems(); + const renderedNavigationItems = useRenderedNavigationItems(navigationItems, renderItem, renderSection); + + return ( + <> +
    + +
    + (User: {session.user.name}) +
    +
    + +
    +
    + Loading...
    }> + + + + ); +} diff --git a/samples/basic/shell/src/AuthenticationBoundary.tsx b/samples/basic/shell/src/AuthenticationBoundary.tsx new file mode 100644 index 000000000..cc247d65d --- /dev/null +++ b/samples/basic/shell/src/AuthenticationBoundary.tsx @@ -0,0 +1,8 @@ +import { useIsAuthenticated } from "@squide/react-router"; +import { Navigate, Outlet } from "react-router-dom"; + +export function AuthenticationBoundary() { + return useIsAuthenticated() ? : ; +} + +export const Component = AuthenticationBoundary; diff --git a/samples/basic/shell/src/LoginPage.tsx b/samples/basic/shell/src/LoginPage.tsx new file mode 100644 index 000000000..8bc194a43 --- /dev/null +++ b/samples/basic/shell/src/LoginPage.tsx @@ -0,0 +1,65 @@ +import type { SessionManager } from "@basic/shared"; +import { useIsAuthenticated } from "@squide/react-router"; +import { useCallback, useState, type ChangeEvent, type MouseEvent } from "react"; +import { Navigate, useNavigate } from "react-router-dom"; + +export interface LoginProps { + sessionManager: SessionManager; +} + +export function LoginPage({ sessionManager }: LoginProps) { + const [username, setUserName] = useState(""); + const [password, setPassword] = useState(""); + + const navigate = useNavigate(); + + const handleClick = useCallback((event: MouseEvent) => { + event.preventDefault(); + + if (username === "temp" && password === "temp") { + sessionManager.setSession({ + user: { + name: username + } + }); + + navigate("/"); + } + }, [username, password, navigate, sessionManager]); + + const handleUserNameChange = useCallback((event: ChangeEvent) => { + setUserName(event.target.value); + }, []); + + const handlePasswordChange = useCallback((event: ChangeEvent) => { + setPassword(event.target.value); + }, []); + + const isAuthenticated = useIsAuthenticated(); + + if (isAuthenticated) { + return ; + } + + return ( +
    +

    Login

    +
    +
    + + +
    +
    + + +
    +
    + +
    +
    Hint: use temp/temp :)
    +
    +
    + ); +} diff --git a/sample/shell/src/Logout.tsx b/samples/basic/shell/src/LogoutPage.tsx similarity index 77% rename from sample/shell/src/Logout.tsx rename to samples/basic/shell/src/LogoutPage.tsx index 7af5e47cd..27b37547c 100644 --- a/sample/shell/src/Logout.tsx +++ b/samples/basic/shell/src/LogoutPage.tsx @@ -1,6 +1,6 @@ import { Link } from "react-router-dom"; -export function Logout() { +export function LogoutPage() { return (

    Logged out

    @@ -9,3 +9,5 @@ export function Logout() {
    ); } + +export const Component = LogoutPage; diff --git a/sample/shell/src/ModuleErrorBoundary.tsx b/samples/basic/shell/src/ModuleErrorBoundary.tsx similarity index 95% rename from sample/shell/src/ModuleErrorBoundary.tsx rename to samples/basic/shell/src/ModuleErrorBoundary.tsx index a593fe37c..30b1a637b 100644 --- a/sample/shell/src/ModuleErrorBoundary.tsx +++ b/samples/basic/shell/src/ModuleErrorBoundary.tsx @@ -33,3 +33,5 @@ export function ModuleErrorBoundary() {
    ); } + +export const ErrorBoundary = ModuleErrorBoundary; diff --git a/sample/shell/src/NoMatch.tsx b/samples/basic/shell/src/NoMatchPage.tsx similarity index 71% rename from sample/shell/src/NoMatch.tsx rename to samples/basic/shell/src/NoMatchPage.tsx index cac3cc455..8118b8544 100644 --- a/sample/shell/src/NoMatch.tsx +++ b/samples/basic/shell/src/NoMatchPage.tsx @@ -1,10 +1,10 @@ import { Link } from "react-router-dom"; -export interface NoMatchProps { +export interface NoMatchPageProps { path: string; } -export function NoMatch({ path }: NoMatchProps) { +export function NoMatchPage({ path }: NoMatchPageProps) { return (

    404

    diff --git a/sample/shell/src/RootErrorBoundary.tsx b/samples/basic/shell/src/RootErrorBoundary.tsx similarity index 100% rename from sample/shell/src/RootErrorBoundary.tsx rename to samples/basic/shell/src/RootErrorBoundary.tsx diff --git a/sample/shell/src/RootLayout.tsx b/samples/basic/shell/src/RootLayout.tsx similarity index 100% rename from sample/shell/src/RootLayout.tsx rename to samples/basic/shell/src/RootLayout.tsx diff --git a/samples/basic/shell/src/index.ts b/samples/basic/shell/src/index.ts new file mode 100644 index 000000000..02b7b1d3f --- /dev/null +++ b/samples/basic/shell/src/index.ts @@ -0,0 +1,3 @@ +export * from "./register.tsx"; +export * from "./useAppRouter.tsx"; + diff --git a/samples/basic/shell/src/register.tsx b/samples/basic/shell/src/register.tsx new file mode 100644 index 000000000..f1a9585b0 --- /dev/null +++ b/samples/basic/shell/src/register.tsx @@ -0,0 +1,95 @@ +import type { SessionManager } from "@basic/shared"; +import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; +import { ManagedRoutes } from "@squide/react-router"; +import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; +import { RootLayout } from "./RootLayout.tsx"; + +function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { + runtime.registerRoute({ + // Pathless route to declare a root layout and a root error boundary. + $visibility: "public", + element: , + children: [ + { + // Public pages like the login and logout pages will be rendered under this pathless route. + $visibility: "public", + $name: "root-error-boundary", + errorElement: , + children: [ + { + // Pathless route to declare an authenticated boundary. + lazy: () => import("./AuthenticationBoundary.tsx"), + children: [ + { + // Pathless route to declare an authenticated layout. + lazy: async () => { + const { AuthenticatedLayout } = await import("./AuthenticatedLayout.tsx"); + + return { + element: + }; + }, + children: [ + { + // Pathless route to declare an error boundary inside the layout instead of outside. + // It's quite useful to prevent losing the layout when an unmanaged error occurs. + lazy: () => import("./ModuleErrorBoundary.tsx"), + children: [ + ManagedRoutes + ] + } + ] + } + ] + } + ] + } + ] + }, { + hoist: true + }); + + runtime.registerRoute({ + $visibility: "public", + path: "/login", + lazy: async () => { + const { LoginPage } = await import("./LoginPage.tsx"); + + return { + element: + }; + } + }, { + parentName: "root-error-boundary" + }); + + runtime.registerRoute({ + $visibility: "public", + path: "/logout", + lazy: () => import("./LogoutPage.tsx") + }, { + parentName: "root-error-boundary" + }); + + runtime.registerRoute({ + $visibility: "public", + path: "*", + lazy: async () => { + const { NoMatchPage } = await import("./NoMatchPage.tsx"); + + return { + element: + }; + } + }, { + parentName: "root-error-boundary" + }); +} + +export function registerShell(sessionManager: SessionManager) { + const register: ModuleRegisterFunction = runtime => { + registerRoutes(runtime, sessionManager); + }; + + return register; +} diff --git a/samples/basic/shell/src/useAppRouter.tsx b/samples/basic/shell/src/useAppRouter.tsx new file mode 100644 index 000000000..8eb32fdb0 --- /dev/null +++ b/samples/basic/shell/src/useAppRouter.tsx @@ -0,0 +1,23 @@ +import { useRoutes } from "@squide/react-router"; +import { useAreModulesReady } from "@squide/webpack-module-federation"; +import { useMemo } from "react"; +import { RouterProvider, createBrowserRouter } from "react-router-dom"; + +export function useAppRouter() { + const routes = useRoutes(); + + // Re-render the app once all the remotes are registered, otherwise the remotes routes won't be added to the router. + const areModulesReady = useAreModulesReady(); + + const router = useMemo(() => { + return createBrowserRouter(routes); + }, [routes]); + + if (!areModulesReady) { + return
    Loading...
    ; + } + + return ( + + ); +} diff --git a/samples/basic/shell/tsconfig.json b/samples/basic/shell/tsconfig.json new file mode 100644 index 000000000..c76e50dcc --- /dev/null +++ b/samples/basic/shell/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "@workleap/typescript-configs/library.json", + "compilerOptions": { + "paths": { + "@squide/core": ["../../../packages/core/src/index.ts"], + "@squide/react-router": ["../../../packages/react-router/src/index.ts"], + "@squide/webpack-module-federation": ["../../../packages/webpack-module-federation/src/index.ts"], + "@squide/webpack-module-federation/defineConfig.js": ["../../../packages/webpack-module-federation/src/defineConfig.js"], + "@squide/fakes": ["../../../packages/fakes/src/index.ts"], + "@basic/shared": ["../shared/src/index.ts"], + "@basic/shared/FederatedTabsLayout.tsx": ["../shared/src/FederatedTabsLayout.tsx"] + } + }, + "exclude": ["dist", "node_modules"] +} diff --git a/sample/shell/tsup.build.ts b/samples/basic/shell/tsup.build.ts similarity index 100% rename from sample/shell/tsup.build.ts rename to samples/basic/shell/tsup.build.ts diff --git a/sample/shell/tsup.dev.ts b/samples/basic/shell/tsup.dev.ts similarity index 100% rename from sample/shell/tsup.dev.ts rename to samples/basic/shell/tsup.dev.ts diff --git a/samples/endpoints/host/.browserslistrc b/samples/endpoints/host/.browserslistrc new file mode 100644 index 000000000..f8a8f866a --- /dev/null +++ b/samples/endpoints/host/.browserslistrc @@ -0,0 +1 @@ +extends @workleap/browserslist-config diff --git a/samples/endpoints/host/.eslintrc.json b/samples/endpoints/host/.eslintrc.json new file mode 100644 index 000000000..ab20e5046 --- /dev/null +++ b/samples/endpoints/host/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://json.schemastore.org/eslintrc", + "root": true, + "extends": "plugin:@workleap/web-application" +} diff --git a/samples/endpoints/host/_redirects b/samples/endpoints/host/_redirects new file mode 100644 index 000000000..7797f7c6a --- /dev/null +++ b/samples/endpoints/host/_redirects @@ -0,0 +1 @@ +/* /index.html 200 diff --git a/sample/host/mocks/browser.ts b/samples/endpoints/host/mocks/browser.ts similarity index 100% rename from sample/host/mocks/browser.ts rename to samples/endpoints/host/mocks/browser.ts diff --git a/sample/remote-module/mocks/handlers.ts b/samples/endpoints/host/mocks/characterHandlers.ts similarity index 99% rename from sample/remote-module/mocks/handlers.ts rename to samples/endpoints/host/mocks/characterHandlers.ts index aa2bdb0ed..d6d090e58 100644 --- a/sample/remote-module/mocks/handlers.ts +++ b/samples/endpoints/host/mocks/characterHandlers.ts @@ -1,12 +1,14 @@ +/* eslint-disable max-len */ + import { rest, type RestHandler } from "msw"; -export const requestHandlers: RestHandler[] = [ - rest.get("https://rickandmortyapi.com/api/character/1,2,3,4,5", async (req, res, ctx) => { +export const characterHandlers: RestHandler[] = [ + rest.get("/api/character/1,2,3,4,5", async (req, res, ctx) => { return res( ctx.status(200), ctx.json([{ "id": 1, - "name": "DATA FROM MSW", + "name": "Rick Sanchez", "status": "Alive", "species": "Human", "type": "", diff --git a/samples/endpoints/host/nodemon.json b/samples/endpoints/host/nodemon.json new file mode 100644 index 000000000..7a5919df8 --- /dev/null +++ b/samples/endpoints/host/nodemon.json @@ -0,0 +1,5 @@ +{ + "watch": ["swc.dev.js", "webpack.dev.js"], + "exec": "webpack serve --config webpack.dev.js", + "verbose": true +} diff --git a/sample/host/package.json b/samples/endpoints/host/package.json similarity index 84% rename from sample/host/package.json rename to samples/endpoints/host/package.json index 521864ce0..8f98851f2 100644 --- a/sample/host/package.json +++ b/samples/endpoints/host/package.json @@ -1,5 +1,5 @@ { - "name": "@sample/host", + "name": "@endpoints/host", "author": "Workleap", "version": "0.0.0", "description": "Host application to showcase @squide.", @@ -14,9 +14,10 @@ "serve-build": "pnpm build && pnpm http-server dist -p 8080 -P http://localhost:8080? -c-1" }, "devDependencies": { + "@squide/fakes": "workspace:*", "@swc/core": "1.3.92", "@swc/helpers": "0.5.3", - "@squide/fakes": "workspace:*", + "@tanstack/react-query": "rc", "@types/react": "18.2.28", "@types/react-dom": "18.2.13", "@types/webpack": "5.28.3", @@ -37,15 +38,16 @@ "webpack-dev-server": "4.15.1" }, "dependencies": { - "@sample/local-module": "workspace:*", - "@sample/shared": "workspace:*", - "@sample/shell": "workspace:*", + "@endpoints/local-module": "workspace:*", + "@endpoints/shared": "workspace:*", + "@endpoints/shell": "workspace:*", "@squide/msw": "workspace:*", "@squide/react-router": "workspace:*", "@squide/webpack-module-federation": "workspace:*", "axios": "1.5.1", "react": "18.2.0", "react-dom": "18.2.0", - "react-router-dom": "6.16.0" + "react-router-dom": "6.16.0", + "react-error-boundary": "4.0.11" } } diff --git a/sample/host/public/favicon.png b/samples/endpoints/host/public/favicon.png similarity index 100% rename from sample/host/public/favicon.png rename to samples/endpoints/host/public/favicon.png diff --git a/sample/another-remote-module/public/index.html b/samples/endpoints/host/public/index.html similarity index 100% rename from sample/another-remote-module/public/index.html rename to samples/endpoints/host/public/index.html diff --git a/sample/host/public/mockServiceWorker.js b/samples/endpoints/host/public/mockServiceWorker.js similarity index 100% rename from sample/host/public/mockServiceWorker.js rename to samples/endpoints/host/public/mockServiceWorker.js diff --git a/sample/local-module/src/dev/App.tsx b/samples/endpoints/host/src/App.tsx similarity index 74% rename from sample/local-module/src/dev/App.tsx rename to samples/endpoints/host/src/App.tsx index 81dfdd533..20ec8047b 100644 --- a/sample/local-module/src/dev/App.tsx +++ b/samples/endpoints/host/src/App.tsx @@ -1,5 +1,5 @@ -import { LoggerTelemetryService } from "@sample/shared"; -import { useAppRouter } from "@sample/shell"; +import { LoggerTelemetryService } from "@endpoints/shared"; +import { useAppRouter } from "@endpoints/shell"; import { useLogger } from "@squide/react-router"; import { sessionManager } from "./session.ts"; diff --git a/sample/remote-module/src/Fetch.tsx b/samples/endpoints/host/src/HomePage.tsx similarity index 74% rename from sample/remote-module/src/Fetch.tsx rename to samples/endpoints/host/src/HomePage.tsx index 205075c07..858422321 100644 --- a/sample/remote-module/src/Fetch.tsx +++ b/samples/endpoints/host/src/HomePage.tsx @@ -7,10 +7,10 @@ interface Character { species: string; } -export function Fetch() { - const { data: characters } = useSuspenseQuery({ queryKey: ["https://rickandmortyapi.com/api/character/", "1", "2", "3", "4", "5"], queryFn: () => { +export function HomePage() { + const { data: characters } = useSuspenseQuery({ queryKey: ["/api/character/1,2,3,4,5"], queryFn: () => { return axios - .get("https://rickandmortyapi.com/api/character/1,2,3,4,5") + .get("/api/character/1,2,3,4,5") .then(({ data }) => { return data; }); @@ -18,8 +18,8 @@ export function Fetch() { return (
    -

    Fetch

    -

    An example fetching data with React Query.

    +

    Home

    +

    Fetching from @endpoints/host

    {characters.map((x: Character) => { return ( @@ -36,5 +36,3 @@ export function Fetch() {
    ); } - -export const Component = Fetch; diff --git a/sample/host/src/bootstrap.tsx b/samples/endpoints/host/src/bootstrap.tsx similarity index 77% rename from sample/host/src/bootstrap.tsx rename to samples/endpoints/host/src/bootstrap.tsx index 6f45b9016..1597e4f5d 100644 --- a/sample/host/src/bootstrap.tsx +++ b/samples/endpoints/host/src/bootstrap.tsx @@ -1,6 +1,6 @@ -import { registerLocalModule } from "@sample/local-module"; -import { isNetlify, type AppContext } from "@sample/shared"; -import { registerShell } from "@sample/shell"; +import { registerLocalModule } from "@endpoints/local-module"; +import { isNetlify } from "@endpoints/shared"; +import { registerShell } from "@endpoints/shell"; import { MswPlugin } from "@squide/msw"; import { ConsoleLogger, Runtime, RuntimeContext, registerLocalModules } from "@squide/react-router"; import { registerRemoteModules, type RemoteDefinition } from "@squide/webpack-module-federation"; @@ -14,10 +14,6 @@ const Remotes: RemoteDefinition[] = [ { name: "remote1", url: isNetlify ? "https://squide-remote-module.netlify.app" : "http://localhost:8081" - }, - { - name: "remote2", - url: isNetlify ? "https://squide-another-remote-module.netlify.app" : "http://localhost:8082" } ]; @@ -31,13 +27,9 @@ const runtime = new Runtime({ sessionAccessor }); -const context: AppContext = { - name: "Sample app" -}; - -registerLocalModules([registerShell(sessionManager), registerHost, registerLocalModule], runtime, { context }); +registerLocalModules([registerShell(sessionManager), registerHost, registerLocalModule], runtime); -registerRemoteModules(Remotes, runtime, { context }).then(() => { +registerRemoteModules(Remotes, runtime).then(() => { if (process.env.USE_MSW) { import("../mocks/browser.ts").then(({ startMsw }) => { // Will start MSW with the request handlers provided by every module. diff --git a/samples/endpoints/host/src/index.ts b/samples/endpoints/host/src/index.ts new file mode 100644 index 000000000..93cf609d8 --- /dev/null +++ b/samples/endpoints/host/src/index.ts @@ -0,0 +1,10 @@ +// Importing a bootstrap file is required when sharing dependencies between the host and the remotes. +// Otherwise we get: Uncaught Error: Shared module is not available for eager consumption +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore doesn't support file extension. +import("./bootstrap"); + +// TS1208: 'index.ts' cannot be compiled under '--isolatedModules' because it is considered a global script file. Add an import, export, or an +// empty 'export {}' statement to make it a module. +export { }; + diff --git a/samples/endpoints/host/src/register.tsx b/samples/endpoints/host/src/register.tsx new file mode 100644 index 000000000..0656c5f08 --- /dev/null +++ b/samples/endpoints/host/src/register.tsx @@ -0,0 +1,54 @@ +import { getMswPlugin } from "@squide/msw"; +import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import type { ReactNode } from "react"; +import { characterHandlers } from "../mocks/characterHandlers.ts"; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + retry: failureCount => { + return failureCount <= 2; + } + } + } +}); + +function Providers({ children }: { children: ReactNode }) { + return ( + + {children} + + ); +} + +function registerRoutes(runtime: Runtime) { + runtime.registerRoute({ + index: true, + lazy: async () => { + const { HomePage } = await import("./HomePage.tsx"); + + return { + element: + }; + } + }); + + runtime.registerNavigationItem({ + $label: "Home", + $priority: 999, + to: "/" + }); +} + +function registerMsw(runtime: Runtime) { + const mswPlugin = getMswPlugin(runtime); + + mswPlugin.registerRequestHandlers(characterHandlers); +} + +export const registerHost: ModuleRegisterFunction = runtime => { + registerRoutes(runtime); + registerMsw(runtime); +}; diff --git a/sample/host/src/session.ts b/samples/endpoints/host/src/session.ts similarity index 90% rename from sample/host/src/session.ts rename to samples/endpoints/host/src/session.ts index ae08e9374..f03b206d1 100644 --- a/sample/host/src/session.ts +++ b/samples/endpoints/host/src/session.ts @@ -1,4 +1,4 @@ -import type { Session, SessionManager } from "@sample/shared"; +import type { Session, SessionManager } from "@endpoints/shared"; import type { SessionAccessorFunction } from "@squide/react-router"; export class InMemorySessionManager implements SessionManager { diff --git a/samples/endpoints/host/swc.build.js b/samples/endpoints/host/swc.build.js new file mode 100644 index 000000000..7e0820823 --- /dev/null +++ b/samples/endpoints/host/swc.build.js @@ -0,0 +1,7 @@ +// @ts-check + +import { browserslistToSwc, defineBuildConfig } from "@workleap/swc-configs"; + +const targets = browserslistToSwc(); + +export const swcConfig = defineBuildConfig(targets); diff --git a/samples/endpoints/host/swc.dev.js b/samples/endpoints/host/swc.dev.js new file mode 100644 index 000000000..9d6fb9fa3 --- /dev/null +++ b/samples/endpoints/host/swc.dev.js @@ -0,0 +1,7 @@ +// @ts-check + +import { browserslistToSwc, defineDevConfig } from "@workleap/swc-configs"; + +const targets = browserslistToSwc(); + +export const swcConfig = defineDevConfig(targets); diff --git a/samples/endpoints/host/tsconfig.json b/samples/endpoints/host/tsconfig.json new file mode 100644 index 000000000..3f837ee64 --- /dev/null +++ b/samples/endpoints/host/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "@workleap/typescript-configs/web-application.json", + "compilerOptions": { + "paths": { + "@squide/core": ["../../../packages/core/src/index.ts"], + "@squide/react-router": ["../../../packages/react-router/src/index.ts"], + "@squide/webpack-module-federation": ["../../../packages/webpack-module-federation/src/index.ts"], + "@squide/webpack-module-federation/defineConfig.js": ["../../../packages/webpack-module-federation/src/defineConfig.js"], + "@squide/msw": ["../../../packages/msw/src/index.ts"], + "@squide/fakes": ["../../../packages/fakes/src/index.ts"], + "@endpoints/shell": ["../shell/src/index.ts"], + "@endpoints/shared": ["../shared/src/index.ts"], + "@endpoints/shared/FederatedTabsLayout.tsx": ["../shared/src/FederatedTabsLayout.tsx"], + "@endpoints/local-module": ["../local-module/src/register.ts"] + } + }, + "exclude": ["dist", "node_modules"] +} diff --git a/sample/host/webpack.build.js b/samples/endpoints/host/webpack.build.js similarity index 95% rename from sample/host/webpack.build.js rename to samples/endpoints/host/webpack.build.js index 8a7004552..d12c58c14 100644 --- a/sample/host/webpack.build.js +++ b/samples/endpoints/host/webpack.build.js @@ -8,7 +8,7 @@ const publicPath = process.env.NETLIFY === "true" ? "https://squide-host.netlify export default defineBuildHostConfig(swcConfig, "host", publicPath, { sharedDependencies: { - "@sample/shared": { + "@endpoints/shared": { singleton: true, eager: true } diff --git a/sample/host/webpack.dev.js b/samples/endpoints/host/webpack.dev.js similarity index 93% rename from sample/host/webpack.dev.js rename to samples/endpoints/host/webpack.dev.js index ce00b5e19..2678e2d89 100644 --- a/sample/host/webpack.dev.js +++ b/samples/endpoints/host/webpack.dev.js @@ -6,7 +6,7 @@ import { swcConfig } from "./swc.dev.js"; export default defineDevHostConfig(swcConfig, "host", 8080, { overlay: false, sharedDependencies: { - "@sample/shared": { + "@endpoints/shared": { singleton: true, eager: true } diff --git a/samples/endpoints/local-module/.browserslistrc b/samples/endpoints/local-module/.browserslistrc new file mode 100644 index 000000000..f8a8f866a --- /dev/null +++ b/samples/endpoints/local-module/.browserslistrc @@ -0,0 +1 @@ +extends @workleap/browserslist-config diff --git a/samples/endpoints/local-module/.eslintrc.json b/samples/endpoints/local-module/.eslintrc.json new file mode 100644 index 000000000..bf0564209 --- /dev/null +++ b/samples/endpoints/local-module/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://json.schemastore.org/eslintrc", + "root": true, + "extends": "plugin:@workleap/react-library" +} diff --git a/sample/remote-module/mocks/browser.ts b/samples/endpoints/local-module/mocks/browser.ts similarity index 100% rename from sample/remote-module/mocks/browser.ts rename to samples/endpoints/local-module/mocks/browser.ts diff --git a/samples/endpoints/local-module/mocks/characterHandlers.ts b/samples/endpoints/local-module/mocks/characterHandlers.ts new file mode 100644 index 000000000..e7dcb375f --- /dev/null +++ b/samples/endpoints/local-module/mocks/characterHandlers.ts @@ -0,0 +1,60 @@ +/* eslint-disable max-len */ + +import { rest, type RestHandler } from "msw"; + +function simulateDelay(delay: number) { + return new Promise(resolve => { + setTimeout(() => { + resolve(undefined); + }, delay); + }); +} + +export const characterHandlers: RestHandler[] = [ + rest.get("/api/character/1,2", async (req, res, ctx) => { + await simulateDelay(2000); + + return res( + ctx.status(200), + ctx.json([{ + "id": 1, + "name": "Rick Sanchez", + "status": "Alive", + "species": "Human", + "type": "", + "gender": "Male", + "origin": { + "name": "Earth (C-137)", + "url": "https://rickandmortyapi.com/api/location/1" + }, + "location": { + "name": "Citadel of Ricks", + "url": "https://rickandmortyapi.com/api/location/3" + }, + "image": "https://rickandmortyapi.com/api/character/avatar/1.jpeg", + "episode": ["https://rickandmortyapi.com/api/episode/1", "https://rickandmortyapi.com/api/episode/2", "https://rickandmortyapi.com/api/episode/3", "https://rickandmortyapi.com/api/episode/4", "https://rickandmortyapi.com/api/episode/5", "https://rickandmortyapi.com/api/episode/6", "https://rickandmortyapi.com/api/episode/7", "https://rickandmortyapi.com/api/episode/8", "https://rickandmortyapi.com/api/episode/9", "https://rickandmortyapi.com/api/episode/10", "https://rickandmortyapi.com/api/episode/11", "https://rickandmortyapi.com/api/episode/12", "https://rickandmortyapi.com/api/episode/13", "https://rickandmortyapi.com/api/episode/14", "https://rickandmortyapi.com/api/episode/15", "https://rickandmortyapi.com/api/episode/16", "https://rickandmortyapi.com/api/episode/17", "https://rickandmortyapi.com/api/episode/18", "https://rickandmortyapi.com/api/episode/19", "https://rickandmortyapi.com/api/episode/20", "https://rickandmortyapi.com/api/episode/21", "https://rickandmortyapi.com/api/episode/22", "https://rickandmortyapi.com/api/episode/23", "https://rickandmortyapi.com/api/episode/24", "https://rickandmortyapi.com/api/episode/25", "https://rickandmortyapi.com/api/episode/26", "https://rickandmortyapi.com/api/episode/27", "https://rickandmortyapi.com/api/episode/28", "https://rickandmortyapi.com/api/episode/29", "https://rickandmortyapi.com/api/episode/30", "https://rickandmortyapi.com/api/episode/31", "https://rickandmortyapi.com/api/episode/32", "https://rickandmortyapi.com/api/episode/33", "https://rickandmortyapi.com/api/episode/34", "https://rickandmortyapi.com/api/episode/35", "https://rickandmortyapi.com/api/episode/36", "https://rickandmortyapi.com/api/episode/37", "https://rickandmortyapi.com/api/episode/38", "https://rickandmortyapi.com/api/episode/39", "https://rickandmortyapi.com/api/episode/40", "https://rickandmortyapi.com/api/episode/41", "https://rickandmortyapi.com/api/episode/42", "https://rickandmortyapi.com/api/episode/43", "https://rickandmortyapi.com/api/episode/44", "https://rickandmortyapi.com/api/episode/45", "https://rickandmortyapi.com/api/episode/46", "https://rickandmortyapi.com/api/episode/47", "https://rickandmortyapi.com/api/episode/48", "https://rickandmortyapi.com/api/episode/49", "https://rickandmortyapi.com/api/episode/50", "https://rickandmortyapi.com/api/episode/51"], + "url": "https://rickandmortyapi.com/api/character/1", + "created": "2017-11-04T18:48:46.250Z" + }, { + "id": 2, + "name": "Morty Smith", + "status": "Alive", + "species": "Human", + "type": "", + "gender": "Male", + "origin": { + "name": "unknown", + "url": "" + }, + "location": { + "name": "Citadel of Ricks", + "url": "https://rickandmortyapi.com/api/location/3" + }, + "image": "https://rickandmortyapi.com/api/character/avatar/2.jpeg", + "episode": ["https://rickandmortyapi.com/api/episode/1", "https://rickandmortyapi.com/api/episode/2", "https://rickandmortyapi.com/api/episode/3", "https://rickandmortyapi.com/api/episode/4", "https://rickandmortyapi.com/api/episode/5", "https://rickandmortyapi.com/api/episode/6", "https://rickandmortyapi.com/api/episode/7", "https://rickandmortyapi.com/api/episode/8", "https://rickandmortyapi.com/api/episode/9", "https://rickandmortyapi.com/api/episode/10", "https://rickandmortyapi.com/api/episode/11", "https://rickandmortyapi.com/api/episode/12", "https://rickandmortyapi.com/api/episode/13", "https://rickandmortyapi.com/api/episode/14", "https://rickandmortyapi.com/api/episode/15", "https://rickandmortyapi.com/api/episode/16", "https://rickandmortyapi.com/api/episode/17", "https://rickandmortyapi.com/api/episode/18", "https://rickandmortyapi.com/api/episode/19", "https://rickandmortyapi.com/api/episode/20", "https://rickandmortyapi.com/api/episode/21", "https://rickandmortyapi.com/api/episode/22", "https://rickandmortyapi.com/api/episode/23", "https://rickandmortyapi.com/api/episode/24", "https://rickandmortyapi.com/api/episode/25", "https://rickandmortyapi.com/api/episode/26", "https://rickandmortyapi.com/api/episode/27", "https://rickandmortyapi.com/api/episode/28", "https://rickandmortyapi.com/api/episode/29", "https://rickandmortyapi.com/api/episode/30", "https://rickandmortyapi.com/api/episode/31", "https://rickandmortyapi.com/api/episode/32", "https://rickandmortyapi.com/api/episode/33", "https://rickandmortyapi.com/api/episode/34", "https://rickandmortyapi.com/api/episode/35", "https://rickandmortyapi.com/api/episode/36", "https://rickandmortyapi.com/api/episode/37", "https://rickandmortyapi.com/api/episode/38", "https://rickandmortyapi.com/api/episode/39", "https://rickandmortyapi.com/api/episode/40", "https://rickandmortyapi.com/api/episode/41", "https://rickandmortyapi.com/api/episode/42", "https://rickandmortyapi.com/api/episode/43", "https://rickandmortyapi.com/api/episode/44", "https://rickandmortyapi.com/api/episode/45", "https://rickandmortyapi.com/api/episode/46", "https://rickandmortyapi.com/api/episode/47", "https://rickandmortyapi.com/api/episode/48", "https://rickandmortyapi.com/api/episode/49", "https://rickandmortyapi.com/api/episode/50", "https://rickandmortyapi.com/api/episode/51"], + "url": "https://rickandmortyapi.com/api/character/2", + "created": "2017-11-04T18:50:21.651Z" + }]) + ); + }) +]; diff --git a/samples/endpoints/local-module/nodemon.json b/samples/endpoints/local-module/nodemon.json new file mode 100644 index 000000000..79a8918cc --- /dev/null +++ b/samples/endpoints/local-module/nodemon.json @@ -0,0 +1,5 @@ +{ + "watch": ["tsup.dev.ts"], + "exec": "tsup --config ./tsup.dev.ts", + "verbose": true +} diff --git a/samples/endpoints/local-module/nodemon.local.json b/samples/endpoints/local-module/nodemon.local.json new file mode 100644 index 000000000..f5a800db0 --- /dev/null +++ b/samples/endpoints/local-module/nodemon.local.json @@ -0,0 +1,5 @@ +{ + "watch": ["swc.dev.js", "webpack.dev.js"], + "exec": "webpack serve --config webpack.config.js", + "verbose": true +} diff --git a/samples/endpoints/local-module/package.json b/samples/endpoints/local-module/package.json new file mode 100644 index 000000000..596169f84 --- /dev/null +++ b/samples/endpoints/local-module/package.json @@ -0,0 +1,71 @@ +{ + "name": "@endpoints/local-module", + "author": "Workleap", + "version": "0.0.0", + "description": "Local module to showcase @squide.", + "private": true, + "license": "Apache-2.0", + "type": "module", + "exports": { + ".": { + "import": "./dist/register.js", + "types": "./dist/register.d.ts", + "default": "./dist/register.js" + } + }, + "scripts": { + "dev": "nodemon", + "dev-local": "cross-env USE_MSW=true nodemon --config nodemon.local.json", + "build": "tsup --config ./tsup.build.ts", + "serve-build": "pnpm build" + }, + "peerDependencies": { + "@endpoints/shared": "*", + "@endpoints/shell": "*", + "@squide/fakes": "*", + "@squide/msw": "*", + "@squide/react-router": "*", + "@tanstack/react-query": "rc", + "axios": "*", + "msw": "*", + "react": "*", + "react-dom": "*", + "react-router-dom": "*" + }, + "devDependencies": { + "@swc/core": "1.3.92", + "@swc/helpers": "0.5.3", + "@tanstack/react-query-devtools": "rc", + "@types/react": "18.2.28", + "@types/react-dom": "18.2.13", + "@types/webpack": "5.28.3", + "@workleap/browserslist-config": "2.0.0", + "@workleap/eslint-plugin": "3.0.0", + "@workleap/swc-configs": "2.1.2", + "@workleap/tsup-configs": "3.0.1", + "@workleap/typescript-configs": "3.0.2", + "@workleap/webpack-configs": "1.1.0", + "browserslist": "4.22.1", + "cross-env": "7.0.3", + "nodemon": "3.0.1", + "tsup": "7.2.0", + "typescript": "5.2.2", + "webpack": "5.88.2", + "webpack-cli": "5.1.4", + "webpack-dev-server": "4.15.1" + }, + "dependencies": { + "@endpoints/shared": "workspace:*", + "@endpoints/shell": "workspace:*", + "@squide/fakes": "workspace:*", + "@squide/msw": "workspace:*", + "@squide/react-router": "workspace:*", + "@tanstack/react-query": "rc", + "axios": "1.5.1", + "msw": "1.3.2", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-router-dom": "6.16.0", + "react-error-boundary": "4.0.11" + } +} diff --git a/sample/local-module/public/favicon.png b/samples/endpoints/local-module/public/favicon.png similarity index 100% rename from sample/local-module/public/favicon.png rename to samples/endpoints/local-module/public/favicon.png diff --git a/sample/host/public/index.html b/samples/endpoints/local-module/public/index.html similarity index 100% rename from sample/host/public/index.html rename to samples/endpoints/local-module/public/index.html diff --git a/samples/endpoints/local-module/src/CharactersTab.tsx b/samples/endpoints/local-module/src/CharactersTab.tsx new file mode 100644 index 000000000..4f2310df6 --- /dev/null +++ b/samples/endpoints/local-module/src/CharactersTab.tsx @@ -0,0 +1,37 @@ +import { useSuspenseQuery } from "@tanstack/react-query"; +import axios from "axios"; + +interface Character { + id: number; + name: string; + species: string; +} + +export function CharactersTab() { + const { data: characters } = useSuspenseQuery({ queryKey: ["/api/character/1,2"], queryFn: () => { + return axios + .get("/api/character/1,2") + .then(({ data }) => { + return data; + }); + } }); + + return ( +
    +

    Fetching from the @endpoints/local-module

    +
    + {characters.map((x: Character) => { + return ( +
    + Id: {x.id} + - + Name: {x.name} + - + Species: {x.species} +
    + ); + })} +
    +
    + ); +} diff --git a/samples/endpoints/local-module/src/SubscriptionPage.tsx b/samples/endpoints/local-module/src/SubscriptionPage.tsx new file mode 100644 index 000000000..cff231970 --- /dev/null +++ b/samples/endpoints/local-module/src/SubscriptionPage.tsx @@ -0,0 +1,21 @@ +import { toSubscriptionStatusLabel, useSubscription } from "@endpoints/shared"; + +export function SubscriptionPage() { + const subscription = useSubscription(); + + return ( +
    +

    Subscription

    +
    + Company: {subscription?.company} +
    +
    + Contact: {subscription?.contact} +
    +
    + Status: {toSubscriptionStatusLabel(subscription?.status)} +
    + +
    + ); +} diff --git a/sample/another-remote-module/src/dev/App.tsx b/samples/endpoints/local-module/src/dev/App.tsx similarity index 74% rename from sample/another-remote-module/src/dev/App.tsx rename to samples/endpoints/local-module/src/dev/App.tsx index 81dfdd533..20ec8047b 100644 --- a/sample/another-remote-module/src/dev/App.tsx +++ b/samples/endpoints/local-module/src/dev/App.tsx @@ -1,5 +1,5 @@ -import { LoggerTelemetryService } from "@sample/shared"; -import { useAppRouter } from "@sample/shell"; +import { LoggerTelemetryService } from "@endpoints/shared"; +import { useAppRouter } from "@endpoints/shell"; import { useLogger } from "@squide/react-router"; import { sessionManager } from "./session.ts"; diff --git a/sample/remote-module/src/dev/DevHome.tsx b/samples/endpoints/local-module/src/dev/DevHomePage.tsx similarity index 80% rename from sample/remote-module/src/dev/DevHome.tsx rename to samples/endpoints/local-module/src/dev/DevHomePage.tsx index 113d1f3d9..f7e62419b 100644 --- a/sample/remote-module/src/dev/DevHome.tsx +++ b/samples/endpoints/local-module/src/dev/DevHomePage.tsx @@ -1,4 +1,4 @@ -export function DevHome() { +export function DevHomePage() { return (

    Remote module development home page

    diff --git a/samples/endpoints/local-module/src/dev/index.tsx b/samples/endpoints/local-module/src/dev/index.tsx new file mode 100644 index 000000000..df382aa47 --- /dev/null +++ b/samples/endpoints/local-module/src/dev/index.tsx @@ -0,0 +1,40 @@ +import { registerShell } from "@endpoints/shell"; +import { MswPlugin } from "@squide/msw"; +import { ConsoleLogger, Runtime, RuntimeContext, registerLocalModules } from "@squide/react-router"; +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import { registerLocalModule } from "../register.tsx"; +import { App } from "./App.tsx"; +import { registerDev } from "./register.tsx"; +import { sessionAccessor, sessionManager } from "./session.ts"; + +const mswPlugin = new MswPlugin(); + +// Create the shell runtime. +// Services, loggers and sessionAccessor could be reuse through a shared packages or faked when in isolation. +const runtime = new Runtime({ + loggers: [new ConsoleLogger()], + sessionAccessor +}); + +registerLocalModules([registerShell(sessionManager), registerDev, registerLocalModule], runtime); + +// Register MSW after the local modules has been registered since the request handlers +// will be registered by the modules. +if (process.env.USE_MSW) { + import("../../mocks/browser.ts").then(({ startMsw }) => { + startMsw(mswPlugin.requestHandlers); + }); +} + +const root = createRoot(document.getElementById("root")!); + +root.render( + + + + + +); + + diff --git a/sample/another-remote-module/src/dev/register.tsx b/samples/endpoints/local-module/src/dev/register.tsx similarity index 72% rename from sample/another-remote-module/src/dev/register.tsx rename to samples/endpoints/local-module/src/dev/register.tsx index 9d3fcbfe8..9628ce656 100644 --- a/sample/another-remote-module/src/dev/register.tsx +++ b/samples/endpoints/local-module/src/dev/register.tsx @@ -1,9 +1,9 @@ import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; -import { DevHome } from "./DevHome.tsx"; +import { DevHomePage } from "./DevHomePage.tsx"; export const registerDev: ModuleRegisterFunction = runtime => { runtime.registerRoute({ index: true, - element: + element: }); }; diff --git a/sample/another-remote-module/src/dev/session.ts b/samples/endpoints/local-module/src/dev/session.ts similarity index 86% rename from sample/another-remote-module/src/dev/session.ts rename to samples/endpoints/local-module/src/dev/session.ts index 6f20a918b..5417cd9d9 100644 --- a/sample/another-remote-module/src/dev/session.ts +++ b/samples/endpoints/local-module/src/dev/session.ts @@ -1,4 +1,4 @@ -import type { Session } from "@sample/shared"; +import type { Session } from "@endpoints/shared"; import { LocalStorageSessionManager } from "@squide/fakes"; import type { SessionAccessorFunction } from "@squide/react-router"; diff --git a/samples/endpoints/local-module/src/register.tsx b/samples/endpoints/local-module/src/register.tsx new file mode 100644 index 000000000..d0c04d014 --- /dev/null +++ b/samples/endpoints/local-module/src/register.tsx @@ -0,0 +1,94 @@ +import { getMswPlugin } from "@squide/msw"; +import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import type { ReactNode } from "react"; +import { characterHandlers } from "../mocks/characterHandlers.ts"; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + retry: failureCount => { + return failureCount <= 2; + } + } + } +}); + +function Providers({ children }: { children: ReactNode }) { + return ( + + {children} + {process.env.LOCAL && ( + + )} + + ); +} + +function registerRoutes(runtime: Runtime) { + runtime.registerRoute({ + path: "/subscription", + lazy: async () => { + const { SubscriptionPage } = await import("./SubscriptionPage.tsx"); + + return { + element: + }; + } + }); + + runtime.registerRoute({ + path: "/federated-tabs", + lazy: async () => { + const { FederatedTabsLayout } = await import("@endpoints/shared/FederatedTabsLayout.tsx"); + + return { + element: + }; + } + }); + + runtime.registerRoute({ + index: true, + lazy: async () => { + const { CharactersTab } = await import("./CharactersTab.tsx"); + + return { + element: + }; + } + }, { + parentPath: "/federated-tabs" + }); + + runtime.registerNavigationItem({ + $label: "Subscription", + to: "/subscription" + }); + + runtime.registerNavigationItem({ + $label: "Tabs", + $priority: 100, + to: "/federated-tabs" + }); + + runtime.registerNavigationItem({ + $label: "Characters", + to: "/federated-tabs" + }, { + menuId: "/federated-tabs" + }); +} + +function registerMsw(runtime: Runtime) { + const mswPlugin = getMswPlugin(runtime); + + mswPlugin.registerRequestHandlers(characterHandlers); +} + +export const registerLocalModule: ModuleRegisterFunction = runtime => { + registerRoutes(runtime); + registerMsw(runtime); +}; diff --git a/samples/endpoints/local-module/swc.config.js b/samples/endpoints/local-module/swc.config.js new file mode 100644 index 000000000..9d6fb9fa3 --- /dev/null +++ b/samples/endpoints/local-module/swc.config.js @@ -0,0 +1,7 @@ +// @ts-check + +import { browserslistToSwc, defineDevConfig } from "@workleap/swc-configs"; + +const targets = browserslistToSwc(); + +export const swcConfig = defineDevConfig(targets); diff --git a/samples/endpoints/local-module/tsconfig.json b/samples/endpoints/local-module/tsconfig.json new file mode 100644 index 000000000..ddec9262d --- /dev/null +++ b/samples/endpoints/local-module/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "@workleap/typescript-configs/library.json", + "compilerOptions": { + "paths": { + "@squide/core": ["../../../packages/core/src/index.ts"], + "@squide/react-router": ["../../../packages/react-router/src/index.ts"], + "@squide/webpack-module-federation": ["../../../packages/webpack-module-federation/src/index.ts"], + "@squide/webpack-module-federation/defineConfig.js": ["../../../packages/webpack-module-federation/src/defineConfig.js"], + "@squide/msw": ["../../../packages/msw/src/index.ts"], + "@squide/fakes": ["../../../packages/fakes/src/index.ts"], + "@endpoints/shell": ["../shell/src/index.ts"], + "@endpoints/shared": ["../shared/src/index.ts"], + "@endpoints/shared/FederatedTabsLayout.tsx": ["../shared/src/FederatedTabsLayout.tsx"] + } + }, + "exclude": ["dist", "node_modules"] +} diff --git a/samples/endpoints/local-module/tsup.build.ts b/samples/endpoints/local-module/tsup.build.ts new file mode 100644 index 000000000..1b2c65a89 --- /dev/null +++ b/samples/endpoints/local-module/tsup.build.ts @@ -0,0 +1,3 @@ +import { defineBuildConfig } from "@workleap/tsup-configs"; + +export default defineBuildConfig(); diff --git a/samples/endpoints/local-module/tsup.dev.ts b/samples/endpoints/local-module/tsup.dev.ts new file mode 100644 index 000000000..8aa2904c8 --- /dev/null +++ b/samples/endpoints/local-module/tsup.dev.ts @@ -0,0 +1,3 @@ +import { defineDevConfig } from "@workleap/tsup-configs"; + +export default defineDevConfig(); diff --git a/samples/endpoints/local-module/webpack.config.js b/samples/endpoints/local-module/webpack.config.js new file mode 100644 index 000000000..f102677b4 --- /dev/null +++ b/samples/endpoints/local-module/webpack.config.js @@ -0,0 +1,15 @@ +// @ts-check + +import { defineDevConfig } from "@workleap/webpack-configs"; +import path from "node:path"; +import { swcConfig } from "./swc.config.js"; + +export default defineDevConfig(swcConfig, { + entry: path.resolve("./src/dev/index.tsx"), + overlay: false, + environmentVariables: { + "LOCAL": process.env.LOCAL === "true", + "USE_MSW": process.env.USE_MSW === "true" + } +}); + diff --git a/samples/endpoints/remote-module/.browserslistrc b/samples/endpoints/remote-module/.browserslistrc new file mode 100644 index 000000000..f8a8f866a --- /dev/null +++ b/samples/endpoints/remote-module/.browserslistrc @@ -0,0 +1 @@ +extends @workleap/browserslist-config diff --git a/samples/endpoints/remote-module/.eslintrc.json b/samples/endpoints/remote-module/.eslintrc.json new file mode 100644 index 000000000..ab20e5046 --- /dev/null +++ b/samples/endpoints/remote-module/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://json.schemastore.org/eslintrc", + "root": true, + "extends": "plugin:@workleap/web-application" +} diff --git a/samples/endpoints/remote-module/mocks/browser.ts b/samples/endpoints/remote-module/mocks/browser.ts new file mode 100644 index 000000000..7d722244b --- /dev/null +++ b/samples/endpoints/remote-module/mocks/browser.ts @@ -0,0 +1,7 @@ +import { setupWorker, type RestHandler, type StartOptions } from "msw"; + +export function startMsw(requestHandlers: RestHandler[], options?: StartOptions) { + const worker = setupWorker(...requestHandlers); + + worker.start(options); +} diff --git a/samples/endpoints/remote-module/mocks/episodeHandlers.ts b/samples/endpoints/remote-module/mocks/episodeHandlers.ts new file mode 100644 index 000000000..9d0f2f7cf --- /dev/null +++ b/samples/endpoints/remote-module/mocks/episodeHandlers.ts @@ -0,0 +1,72 @@ +/* eslint-disable max-len */ + +import { rest, type RestHandler } from "msw"; + +export const episodeHandlers: RestHandler[] = [ + rest.get("/api/episode/1,2,3,4,5,6,7", async (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json([{ + "id": 1, + "name": "Pilot", + "air_date": "December 2, 2013", + "episode": "S01E01", + "characters": ["https://rickandmortyapi.com/api/character/1", "https://rickandmortyapi.com/api/character/2", "https://rickandmortyapi.com/api/character/35", "https://rickandmortyapi.com/api/character/38", "https://rickandmortyapi.com/api/character/62", "https://rickandmortyapi.com/api/character/92", "https://rickandmortyapi.com/api/character/127", "https://rickandmortyapi.com/api/character/144", "https://rickandmortyapi.com/api/character/158", "https://rickandmortyapi.com/api/character/175", "https://rickandmortyapi.com/api/character/179", "https://rickandmortyapi.com/api/character/181", "https://rickandmortyapi.com/api/character/239", "https://rickandmortyapi.com/api/character/249", "https://rickandmortyapi.com/api/character/271", "https://rickandmortyapi.com/api/character/338", "https://rickandmortyapi.com/api/character/394", "https://rickandmortyapi.com/api/character/395", "https://rickandmortyapi.com/api/character/435"], + "url": "https://rickandmortyapi.com/api/episode/1", + "created": "2017-11-10T12:56:33.798Z" + }, { + "id": 2, + "name": "Lawnmower Dog", + "air_date": "December 9, 2013", + "episode": "S01E02", + "characters": ["https://rickandmortyapi.com/api/character/1", "https://rickandmortyapi.com/api/character/2", "https://rickandmortyapi.com/api/character/38", "https://rickandmortyapi.com/api/character/46", "https://rickandmortyapi.com/api/character/63", "https://rickandmortyapi.com/api/character/80", "https://rickandmortyapi.com/api/character/175", "https://rickandmortyapi.com/api/character/221", "https://rickandmortyapi.com/api/character/239", "https://rickandmortyapi.com/api/character/246", "https://rickandmortyapi.com/api/character/304", "https://rickandmortyapi.com/api/character/305", "https://rickandmortyapi.com/api/character/306", "https://rickandmortyapi.com/api/character/329", "https://rickandmortyapi.com/api/character/338", "https://rickandmortyapi.com/api/character/396", "https://rickandmortyapi.com/api/character/397", "https://rickandmortyapi.com/api/character/398", "https://rickandmortyapi.com/api/character/405"], + "url": "https://rickandmortyapi.com/api/episode/2", + "created": "2017-11-10T12:56:33.916Z" + }, { + "id": 3, + "name": "Anatomy Park", + "air_date": "December 16, 2013", + "episode": "S01E03", + "characters": ["https://rickandmortyapi.com/api/character/1", "https://rickandmortyapi.com/api/character/2", "https://rickandmortyapi.com/api/character/12", "https://rickandmortyapi.com/api/character/17", "https://rickandmortyapi.com/api/character/38", "https://rickandmortyapi.com/api/character/45", "https://rickandmortyapi.com/api/character/96", "https://rickandmortyapi.com/api/character/97", "https://rickandmortyapi.com/api/character/98", "https://rickandmortyapi.com/api/character/99", "https://rickandmortyapi.com/api/character/100", "https://rickandmortyapi.com/api/character/101", "https://rickandmortyapi.com/api/character/108", "https://rickandmortyapi.com/api/character/112", "https://rickandmortyapi.com/api/character/114", "https://rickandmortyapi.com/api/character/169", "https://rickandmortyapi.com/api/character/175", "https://rickandmortyapi.com/api/character/186", "https://rickandmortyapi.com/api/character/201", "https://rickandmortyapi.com/api/character/268", "https://rickandmortyapi.com/api/character/300", "https://rickandmortyapi.com/api/character/302", "https://rickandmortyapi.com/api/character/338", "https://rickandmortyapi.com/api/character/356"], + "url": "https://rickandmortyapi.com/api/episode/3", + "created": "2017-11-10T12:56:34.022Z" + }, { + "id": 4, + "name": "M. Night Shaym-Aliens!", + "air_date": "January 13, 2014", + "episode": "S01E04", + "characters": ["https://rickandmortyapi.com/api/character/1", "https://rickandmortyapi.com/api/character/2", "https://rickandmortyapi.com/api/character/38", "https://rickandmortyapi.com/api/character/87", "https://rickandmortyapi.com/api/character/175", "https://rickandmortyapi.com/api/character/179", "https://rickandmortyapi.com/api/character/181", "https://rickandmortyapi.com/api/character/191", "https://rickandmortyapi.com/api/character/239", "https://rickandmortyapi.com/api/character/241", "https://rickandmortyapi.com/api/character/270", "https://rickandmortyapi.com/api/character/337", "https://rickandmortyapi.com/api/character/338"], + "url": "https://rickandmortyapi.com/api/episode/4", + "created": "2017-11-10T12:56:34.129Z" + }, { + "id": 5, + "name": "Meeseeks and Destroy", + "air_date": "January 20, 2014", + "episode": "S01E05", + "characters": ["https://rickandmortyapi.com/api/character/1", "https://rickandmortyapi.com/api/character/2", "https://rickandmortyapi.com/api/character/38", "https://rickandmortyapi.com/api/character/41", "https://rickandmortyapi.com/api/character/89", "https://rickandmortyapi.com/api/character/116", "https://rickandmortyapi.com/api/character/117", "https://rickandmortyapi.com/api/character/120", "https://rickandmortyapi.com/api/character/175", "https://rickandmortyapi.com/api/character/193", "https://rickandmortyapi.com/api/character/238", "https://rickandmortyapi.com/api/character/242", "https://rickandmortyapi.com/api/character/271", "https://rickandmortyapi.com/api/character/303", "https://rickandmortyapi.com/api/character/326", "https://rickandmortyapi.com/api/character/333", "https://rickandmortyapi.com/api/character/338", "https://rickandmortyapi.com/api/character/343", "https://rickandmortyapi.com/api/character/399", "https://rickandmortyapi.com/api/character/400"], + "url": "https://rickandmortyapi.com/api/episode/5", + "created": "2017-11-10T12:56:34.236Z" + }, { + "id": 6, + "name": "Rick Potion #9", + "air_date": "January 27, 2014", + "episode": "S01E06", + "characters": ["https://rickandmortyapi.com/api/character/1", "https://rickandmortyapi.com/api/character/2", "https://rickandmortyapi.com/api/character/3", "https://rickandmortyapi.com/api/character/4", "https://rickandmortyapi.com/api/character/5", "https://rickandmortyapi.com/api/character/38", "https://rickandmortyapi.com/api/character/58", "https://rickandmortyapi.com/api/character/82", "https://rickandmortyapi.com/api/character/83", "https://rickandmortyapi.com/api/character/92", "https://rickandmortyapi.com/api/character/155", "https://rickandmortyapi.com/api/character/175", "https://rickandmortyapi.com/api/character/179", "https://rickandmortyapi.com/api/character/181", "https://rickandmortyapi.com/api/character/216", "https://rickandmortyapi.com/api/character/234", "https://rickandmortyapi.com/api/character/239", "https://rickandmortyapi.com/api/character/249", "https://rickandmortyapi.com/api/character/251", "https://rickandmortyapi.com/api/character/271", "https://rickandmortyapi.com/api/character/293", "https://rickandmortyapi.com/api/character/338", "https://rickandmortyapi.com/api/character/343", "https://rickandmortyapi.com/api/character/394"], + "url": "https://rickandmortyapi.com/api/episode/6", + "created": "2017-11-10T12:56:34.339Z" + }, { + "id": 7, + "name": "Raising Gazorpazorp", + "air_date": "March 10, 2014", + "episode": "S01E07", + "characters": ["https://rickandmortyapi.com/api/character/1", "https://rickandmortyapi.com/api/character/2", "https://rickandmortyapi.com/api/character/3", "https://rickandmortyapi.com/api/character/4", "https://rickandmortyapi.com/api/character/5", "https://rickandmortyapi.com/api/character/59", "https://rickandmortyapi.com/api/character/151", "https://rickandmortyapi.com/api/character/168", "https://rickandmortyapi.com/api/character/211", "https://rickandmortyapi.com/api/character/230", "https://rickandmortyapi.com/api/character/258", "https://rickandmortyapi.com/api/character/329", "https://rickandmortyapi.com/api/character/376", "https://rickandmortyapi.com/api/character/401"], + "url": "https://rickandmortyapi.com/api/episode/7", + "created": "2017-11-10T12:56:34.441Z" + }]) + ); + }) +]; + +/* + +*/ diff --git a/samples/endpoints/remote-module/mocks/locationHandlers.ts b/samples/endpoints/remote-module/mocks/locationHandlers.ts new file mode 100644 index 000000000..d978c88a7 --- /dev/null +++ b/samples/endpoints/remote-module/mocks/locationHandlers.ts @@ -0,0 +1,42 @@ +/* eslint-disable max-len */ + +import { rest, type RestHandler } from "msw"; + +export const locationHandlers: RestHandler[] = [ + rest.get("/api/location/1,2,3", async (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json([{ + "id": 1, + "name": "Earth (C-137)", + "type": "Planet", + "dimension": "Dimension C-137", + "residents": ["https://rickandmortyapi.com/api/character/38", "https://rickandmortyapi.com/api/character/45", "https://rickandmortyapi.com/api/character/71", "https://rickandmortyapi.com/api/character/82", "https://rickandmortyapi.com/api/character/83", "https://rickandmortyapi.com/api/character/92", "https://rickandmortyapi.com/api/character/112", "https://rickandmortyapi.com/api/character/114", "https://rickandmortyapi.com/api/character/116", "https://rickandmortyapi.com/api/character/117", "https://rickandmortyapi.com/api/character/120", "https://rickandmortyapi.com/api/character/127", "https://rickandmortyapi.com/api/character/155", "https://rickandmortyapi.com/api/character/169", "https://rickandmortyapi.com/api/character/175", "https://rickandmortyapi.com/api/character/179", "https://rickandmortyapi.com/api/character/186", "https://rickandmortyapi.com/api/character/201", "https://rickandmortyapi.com/api/character/216", "https://rickandmortyapi.com/api/character/239", "https://rickandmortyapi.com/api/character/271", "https://rickandmortyapi.com/api/character/302", "https://rickandmortyapi.com/api/character/303", "https://rickandmortyapi.com/api/character/338", "https://rickandmortyapi.com/api/character/343", "https://rickandmortyapi.com/api/character/356", "https://rickandmortyapi.com/api/character/394"], + "url": "https://rickandmortyapi.com/api/location/1", + "created": "2017-11-10T12:42:04.162Z" + }, { + "id": 2, + "name": "Abadango", + "type": "Cluster", + "dimension": "unknown", + "residents": ["https://rickandmortyapi.com/api/character/6"], + "url": "https://rickandmortyapi.com/api/location/2", + "created": "2017-11-10T13:06:38.182Z" + }, { + "id": 3, + "name": "Citadel of Ricks", + "type": "Space station", + "dimension": "unknown", + "residents": ["https://rickandmortyapi.com/api/character/8", "https://rickandmortyapi.com/api/character/14", "https://rickandmortyapi.com/api/character/15", "https://rickandmortyapi.com/api/character/18", "https://rickandmortyapi.com/api/character/21", "https://rickandmortyapi.com/api/character/22", "https://rickandmortyapi.com/api/character/27", "https://rickandmortyapi.com/api/character/42", "https://rickandmortyapi.com/api/character/43", "https://rickandmortyapi.com/api/character/44", "https://rickandmortyapi.com/api/character/48", "https://rickandmortyapi.com/api/character/53", "https://rickandmortyapi.com/api/character/56", "https://rickandmortyapi.com/api/character/61", "https://rickandmortyapi.com/api/character/69", "https://rickandmortyapi.com/api/character/72", "https://rickandmortyapi.com/api/character/73", "https://rickandmortyapi.com/api/character/74", "https://rickandmortyapi.com/api/character/77", "https://rickandmortyapi.com/api/character/78", "https://rickandmortyapi.com/api/character/85", "https://rickandmortyapi.com/api/character/86", "https://rickandmortyapi.com/api/character/95", "https://rickandmortyapi.com/api/character/118", "https://rickandmortyapi.com/api/character/119", "https://rickandmortyapi.com/api/character/123", "https://rickandmortyapi.com/api/character/135", "https://rickandmortyapi.com/api/character/143", "https://rickandmortyapi.com/api/character/152", "https://rickandmortyapi.com/api/character/164", "https://rickandmortyapi.com/api/character/165", "https://rickandmortyapi.com/api/character/187", "https://rickandmortyapi.com/api/character/200", "https://rickandmortyapi.com/api/character/206", "https://rickandmortyapi.com/api/character/209", "https://rickandmortyapi.com/api/character/220", "https://rickandmortyapi.com/api/character/229", "https://rickandmortyapi.com/api/character/231", "https://rickandmortyapi.com/api/character/235", "https://rickandmortyapi.com/api/character/267", "https://rickandmortyapi.com/api/character/278", "https://rickandmortyapi.com/api/character/281", "https://rickandmortyapi.com/api/character/283", "https://rickandmortyapi.com/api/character/284", "https://rickandmortyapi.com/api/character/285", "https://rickandmortyapi.com/api/character/286", "https://rickandmortyapi.com/api/character/287", "https://rickandmortyapi.com/api/character/288", "https://rickandmortyapi.com/api/character/289", "https://rickandmortyapi.com/api/character/291", "https://rickandmortyapi.com/api/character/295", "https://rickandmortyapi.com/api/character/298", "https://rickandmortyapi.com/api/character/299", "https://rickandmortyapi.com/api/character/322", "https://rickandmortyapi.com/api/character/325", "https://rickandmortyapi.com/api/character/328", "https://rickandmortyapi.com/api/character/330", "https://rickandmortyapi.com/api/character/345", "https://rickandmortyapi.com/api/character/359", "https://rickandmortyapi.com/api/character/366", "https://rickandmortyapi.com/api/character/378", "https://rickandmortyapi.com/api/character/385", "https://rickandmortyapi.com/api/character/392", "https://rickandmortyapi.com/api/character/461", "https://rickandmortyapi.com/api/character/462", "https://rickandmortyapi.com/api/character/463", "https://rickandmortyapi.com/api/character/464", "https://rickandmortyapi.com/api/character/465", "https://rickandmortyapi.com/api/character/466", "https://rickandmortyapi.com/api/character/472", "https://rickandmortyapi.com/api/character/473", "https://rickandmortyapi.com/api/character/474", "https://rickandmortyapi.com/api/character/475", "https://rickandmortyapi.com/api/character/476", "https://rickandmortyapi.com/api/character/477", "https://rickandmortyapi.com/api/character/478", "https://rickandmortyapi.com/api/character/479", "https://rickandmortyapi.com/api/character/480", "https://rickandmortyapi.com/api/character/481", "https://rickandmortyapi.com/api/character/482", "https://rickandmortyapi.com/api/character/483", "https://rickandmortyapi.com/api/character/484", "https://rickandmortyapi.com/api/character/485", "https://rickandmortyapi.com/api/character/486", "https://rickandmortyapi.com/api/character/487", "https://rickandmortyapi.com/api/character/488", "https://rickandmortyapi.com/api/character/489", "https://rickandmortyapi.com/api/character/2", "https://rickandmortyapi.com/api/character/1", "https://rickandmortyapi.com/api/character/801", "https://rickandmortyapi.com/api/character/802", "https://rickandmortyapi.com/api/character/803", "https://rickandmortyapi.com/api/character/804", "https://rickandmortyapi.com/api/character/805", "https://rickandmortyapi.com/api/character/806", "https://rickandmortyapi.com/api/character/810", "https://rickandmortyapi.com/api/character/811", "https://rickandmortyapi.com/api/character/812", "https://rickandmortyapi.com/api/character/819", "https://rickandmortyapi.com/api/character/820", "https://rickandmortyapi.com/api/character/818"], + "url": "https://rickandmortyapi.com/api/location/3", + "created": "2017-11-10T13:08:13.191Z" + }]) + ); + }), + + rest.get("/api/location/failing", async (req, res, ctx) => { + return res( + ctx.status(500) + ); + }) +]; diff --git a/samples/endpoints/remote-module/nodemon.json b/samples/endpoints/remote-module/nodemon.json new file mode 100644 index 000000000..7a5919df8 --- /dev/null +++ b/samples/endpoints/remote-module/nodemon.json @@ -0,0 +1,5 @@ +{ + "watch": ["swc.dev.js", "webpack.dev.js"], + "exec": "webpack serve --config webpack.dev.js", + "verbose": true +} diff --git a/sample/remote-module/package.json b/samples/endpoints/remote-module/package.json similarity index 86% rename from sample/remote-module/package.json rename to samples/endpoints/remote-module/package.json index 3be3fb370..f9c3b6770 100644 --- a/sample/remote-module/package.json +++ b/samples/endpoints/remote-module/package.json @@ -1,5 +1,5 @@ { - "name": "@sample/remote-module", + "name": "@endpoints/remote-module", "author": "Workleap", "version": "0.0.0", "description": "Remote module to showcase @squide.", @@ -7,7 +7,7 @@ "license": "Apache-2.0", "type": "module", "scripts": { - "dev": "cross-env USE_MSW=true nodemon", + "dev": "nodemon", "dev-local": "cross-env USE_MSW=true LOCAL=true pnpm dev", "build": "cross-env USE_MSW=true webpack --config webpack.build.js", "serve-build": "pnpm build && pnpm http-server dist -p 8081 -P http://localhost:8081? -c-1" @@ -15,7 +15,6 @@ "devDependencies": { "@swc/core": "1.3.92", "@swc/helpers": "0.5.3", - "@squide/fakes": "workspace:*", "@tanstack/react-query-devtools": "rc", "@types/react": "18.2.28", "@types/react-dom": "18.2.13", @@ -25,11 +24,9 @@ "@workleap/swc-configs": "2.1.2", "@workleap/typescript-configs": "3.0.2", "@workleap/webpack-configs": "1.1.0", - "axios": "1.5.1", "browserslist": "4.22.1", "cross-env": "7.0.3", "http-server": "14.1.1", - "msw": "1.3.2", "nodemon": "3.0.1", "typescript": "5.2.2", "webpack": "5.88.2", @@ -37,14 +34,18 @@ "webpack-dev-server": "4.15.1" }, "dependencies": { - "@sample/shared": "workspace:*", - "@sample/shell": "workspace:*", + "@endpoints/shared": "workspace:*", + "@endpoints/shell": "workspace:*", + "@squide/fakes": "workspace:*", "@squide/msw": "workspace:*", "@squide/react-router": "workspace:*", "@squide/webpack-module-federation": "workspace:*", "@tanstack/react-query": "rc", + "axios": "1.5.1", + "msw": "1.3.2", "react": "18.2.0", "react-dom": "18.2.0", - "react-router-dom": "6.16.0" + "react-router-dom": "6.16.0", + "react-error-boundary": "4.0.11" } } diff --git a/sample/remote-module/public/favicon.png b/samples/endpoints/remote-module/public/favicon.png similarity index 100% rename from sample/remote-module/public/favicon.png rename to samples/endpoints/remote-module/public/favicon.png diff --git a/sample/local-module/public/index.html b/samples/endpoints/remote-module/public/index.html similarity index 100% rename from sample/local-module/public/index.html rename to samples/endpoints/remote-module/public/index.html diff --git a/sample/remote-module/public/mockServiceWorker.js b/samples/endpoints/remote-module/public/mockServiceWorker.js similarity index 100% rename from sample/remote-module/public/mockServiceWorker.js rename to samples/endpoints/remote-module/public/mockServiceWorker.js diff --git a/samples/endpoints/remote-module/src/EpisodesTab.tsx b/samples/endpoints/remote-module/src/EpisodesTab.tsx new file mode 100644 index 000000000..1e26a4c04 --- /dev/null +++ b/samples/endpoints/remote-module/src/EpisodesTab.tsx @@ -0,0 +1,37 @@ +import { useSuspenseQuery } from "@tanstack/react-query"; +import axios from "axios"; + +interface Episode { + id: number; + name: string; + episode: string; +} + +export function EpisodesTab() { + const { data: episodes } = useSuspenseQuery({ queryKey: ["/api/episode/1,2,3,4,5,6,7"], queryFn: () => { + return axios + .get("/api/episode/1,2,3,4,5,6,7") + .then(({ data }) => { + return data; + }); + } }); + + return ( +
    +

    Fetching from @endpoints/remote-module

    +
    + {episodes.map((x: Episode) => { + return ( +
    + Id: {x.id} + - + Name: {x.name} + - + Episode: {x.episode} +
    + ); + })} +
    +
    + ); +} diff --git a/samples/endpoints/remote-module/src/FailingTab.tsx b/samples/endpoints/remote-module/src/FailingTab.tsx new file mode 100644 index 000000000..029de2884 --- /dev/null +++ b/samples/endpoints/remote-module/src/FailingTab.tsx @@ -0,0 +1,16 @@ +import { useSuspenseQuery } from "@tanstack/react-query"; +import axios from "axios"; + +export function FailingTab() { + useSuspenseQuery({ queryKey: ["/api/location/failing"], queryFn: () => { + return axios + .get("/api/location/failing") + .then(({ data }) => { + return data; + }); + } }); + + return ( +
    Something went wront because the API call should have failed and you shouldn't see this!
    + ); +} diff --git a/samples/endpoints/remote-module/src/LocationsTab.tsx b/samples/endpoints/remote-module/src/LocationsTab.tsx new file mode 100644 index 000000000..edeb23196 --- /dev/null +++ b/samples/endpoints/remote-module/src/LocationsTab.tsx @@ -0,0 +1,37 @@ +import { useSuspenseQuery } from "@tanstack/react-query"; +import axios from "axios"; + +interface Location { + id: number; + name: string; + type: string; +} + +export function LocationsTab() { + const { data: locations } = useSuspenseQuery({ queryKey: ["/api/location/1,2,3"], queryFn: () => { + return axios + .get("/api/location/1,2,3") + .then(({ data }) => { + return data; + }); + } }); + + return ( +
    +

    Fetching from @endpoints/remote-module

    +
    + {locations.map((x: Location) => { + return ( +
    + Id: {x.id} + - + Name: {x.name} + - + Type: {x.type} +
    + ); + })} +
    +
    + ); +} diff --git a/samples/endpoints/remote-module/src/Providers.tsx b/samples/endpoints/remote-module/src/Providers.tsx new file mode 100644 index 000000000..f2bdbf360 --- /dev/null +++ b/samples/endpoints/remote-module/src/Providers.tsx @@ -0,0 +1,25 @@ +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import type { ReactNode } from "react"; + +export const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + retry: failureCount => { + return failureCount <= 2; + } + } + } +}); + +export function Providers({ children }: { children: ReactNode }) { + return ( + + {children} + {process.env.LOCAL && ( + + )} + + ); +} diff --git a/samples/endpoints/remote-module/src/dev/App.tsx b/samples/endpoints/remote-module/src/dev/App.tsx new file mode 100644 index 000000000..200ad0690 --- /dev/null +++ b/samples/endpoints/remote-module/src/dev/App.tsx @@ -0,0 +1,13 @@ +import { LoggerTelemetryService } from "@endpoints/shared"; +import { useAppRouter } from "@endpoints/shell"; +import { useLogger } from "@squide/react-router"; +import { sessionManager } from "./session.ts"; + +export function App() { + const logger = useLogger(); + + const telemetryService = new LoggerTelemetryService(logger); + + return useAppRouter(process.env.USE_MSW as unknown as boolean, sessionManager, telemetryService); +} + diff --git a/samples/endpoints/remote-module/src/dev/DevHomePage.tsx b/samples/endpoints/remote-module/src/dev/DevHomePage.tsx new file mode 100644 index 000000000..f7e62419b --- /dev/null +++ b/samples/endpoints/remote-module/src/dev/DevHomePage.tsx @@ -0,0 +1,8 @@ +export function DevHomePage() { + return ( +
    +

    Remote module development home page

    +

    Hey!

    +
    + ); +} diff --git a/sample/remote-module/src/dev/index.tsx b/samples/endpoints/remote-module/src/dev/index.tsx similarity index 96% rename from sample/remote-module/src/dev/index.tsx rename to samples/endpoints/remote-module/src/dev/index.tsx index 6980bda5d..e6413bc3e 100644 --- a/sample/remote-module/src/dev/index.tsx +++ b/samples/endpoints/remote-module/src/dev/index.tsx @@ -1,4 +1,4 @@ -import { registerShell } from "@sample/shell"; +import { registerShell } from "@endpoints/shell"; import { MswPlugin } from "@squide/msw"; import { ConsoleLogger, Runtime, RuntimeContext, registerLocalModules } from "@squide/react-router"; import { StrictMode } from "react"; diff --git a/samples/endpoints/remote-module/src/dev/register.tsx b/samples/endpoints/remote-module/src/dev/register.tsx new file mode 100644 index 000000000..5567785bb --- /dev/null +++ b/samples/endpoints/remote-module/src/dev/register.tsx @@ -0,0 +1,26 @@ +import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; +import { Providers } from "../Providers.tsx"; +import { DevHomePage } from "./DevHomePage.tsx"; + +export const registerDev: ModuleRegisterFunction = runtime => { + runtime.registerRoute({ + index: true, + element: + }); + + runtime.registerRoute({ + path: "/federated-tabs", + lazy: async () => { + const { FederatedTabsLayout } = await import("@endpoints/shared/FederatedTabsLayout.tsx"); + + return { + element: + }; + } + }); + + runtime.registerNavigationItem({ + $label: "Tabs", + to: "/federated-tabs" + }); +}; diff --git a/sample/local-module/src/dev/session.ts b/samples/endpoints/remote-module/src/dev/session.ts similarity index 86% rename from sample/local-module/src/dev/session.ts rename to samples/endpoints/remote-module/src/dev/session.ts index 6f20a918b..5417cd9d9 100644 --- a/sample/local-module/src/dev/session.ts +++ b/samples/endpoints/remote-module/src/dev/session.ts @@ -1,4 +1,4 @@ -import type { Session } from "@sample/shared"; +import type { Session } from "@endpoints/shared"; import { LocalStorageSessionManager } from "@squide/fakes"; import type { SessionAccessorFunction } from "@squide/react-router"; diff --git a/samples/endpoints/remote-module/src/register.tsx b/samples/endpoints/remote-module/src/register.tsx new file mode 100644 index 000000000..7bb82b58e --- /dev/null +++ b/samples/endpoints/remote-module/src/register.tsx @@ -0,0 +1,81 @@ +import { getMswPlugin } from "@squide/msw"; +import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; +import { episodeHandlers } from "../mocks/episodeHandlers.ts"; +import { locationHandlers } from "../mocks/locationHandlers.ts"; +import { Providers } from "./Providers.tsx"; + +function registerRoutes(runtime: Runtime) { + runtime.registerRoute({ + path: "/federated-tabs/episodes", + lazy: async () => { + const { EpisodesTab } = await import("./EpisodesTab.tsx"); + + return { + element: + }; + } + }, { + parentPath: "/federated-tabs" + }); + + runtime.registerRoute({ + path: "/federated-tabs/locations", + lazy: async () => { + const { LocationsTab } = await import("./LocationsTab.tsx"); + + return { + element: + }; + } + }, { + parentPath: "/federated-tabs" + }); + + runtime.registerRoute({ + path: "/federated-tabs/failing", + lazy: async () => { + const { FailingTab } = await import("./FailingTab.tsx"); + + return { + element: + }; + } + }, { + parentPath: "/federated-tabs" + }); + + runtime.registerNavigationItem({ + $label: "Episodes", + to: "/federated-tabs/episodes" + }, { + menuId: "/federated-tabs" + }); + + runtime.registerNavigationItem({ + $label: "Locations", + to: "/federated-tabs/locations" + }, { + menuId: "/federated-tabs" + }); + + runtime.registerNavigationItem({ + $label: "Failing", + to: "/federated-tabs/failing" + }, { + menuId: "/federated-tabs" + }); +} + +function registerMsw(runtime: Runtime) { + const mswPlugin = getMswPlugin(runtime); + + mswPlugin.registerRequestHandlers([ + ...episodeHandlers, + ...locationHandlers + ]); +} + +export const register: ModuleRegisterFunction = runtime => { + registerRoutes(runtime); + registerMsw(runtime); +}; diff --git a/samples/endpoints/remote-module/swc.build.js b/samples/endpoints/remote-module/swc.build.js new file mode 100644 index 000000000..7e0820823 --- /dev/null +++ b/samples/endpoints/remote-module/swc.build.js @@ -0,0 +1,7 @@ +// @ts-check + +import { browserslistToSwc, defineBuildConfig } from "@workleap/swc-configs"; + +const targets = browserslistToSwc(); + +export const swcConfig = defineBuildConfig(targets); diff --git a/samples/endpoints/remote-module/swc.dev.js b/samples/endpoints/remote-module/swc.dev.js new file mode 100644 index 000000000..9d6fb9fa3 --- /dev/null +++ b/samples/endpoints/remote-module/swc.dev.js @@ -0,0 +1,7 @@ +// @ts-check + +import { browserslistToSwc, defineDevConfig } from "@workleap/swc-configs"; + +const targets = browserslistToSwc(); + +export const swcConfig = defineDevConfig(targets); diff --git a/samples/endpoints/remote-module/tsconfig.json b/samples/endpoints/remote-module/tsconfig.json new file mode 100644 index 000000000..f5dc566b5 --- /dev/null +++ b/samples/endpoints/remote-module/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "@workleap/typescript-configs/web-application.json", + "compilerOptions": { + "paths": { + "@squide/core": ["../../../packages/core/src/index.ts"], + "@squide/react-router": ["../../../packages/react-router/src/index.ts"], + "@squide/webpack-module-federation": ["../../../packages/webpack-module-federation/src/index.ts"], + "@squide/webpack-module-federation/defineConfig.js": ["../../../packages/webpack-module-federation/src/defineConfig.js"], + "@squide/msw": ["../../../packages/msw/src/index.ts"], + "@squide/fakes": ["../../../packages/fakes/src/index.ts"], + "@endpoints/shell": ["../shell/src/index.ts"], + "@endpoints/shared": ["../shared/src/index.ts"], + "@endpoints/shared/FederatedTabsLayout.tsx": ["../shared/src/FederatedTabsLayout.tsx"] + } + }, + "exclude": ["dist", "node_modules"] +} diff --git a/sample/remote-module/webpack.build.js b/samples/endpoints/remote-module/webpack.build.js similarity index 95% rename from sample/remote-module/webpack.build.js rename to samples/endpoints/remote-module/webpack.build.js index d8fe26392..5aaaff654 100644 --- a/sample/remote-module/webpack.build.js +++ b/samples/endpoints/remote-module/webpack.build.js @@ -8,7 +8,7 @@ const publicPath = process.env.NETLIFY === "true" ? "https://squide-remote-modul export default defineBuildRemoteModuleConfig(swcConfig, "remote1", publicPath, { sharedDependencies: { - "@sample/shared": { + "@endpoints/shared": { singleton: true } }, diff --git a/sample/remote-module/webpack.dev.js b/samples/endpoints/remote-module/webpack.dev.js similarity index 90% rename from sample/remote-module/webpack.dev.js rename to samples/endpoints/remote-module/webpack.dev.js index 684e7cbe4..30277d5be 100644 --- a/sample/remote-module/webpack.dev.js +++ b/samples/endpoints/remote-module/webpack.dev.js @@ -10,12 +10,13 @@ let config; if (!process.env.LOCAL) { config = defineDevRemoteModuleConfig(swcConfig, "remote1", 8081, { sharedDependencies: { - "@sample/shared": { + "@endpoints/shared": { singleton: true } }, environmentVariables: { "NETLIFY": process.env.NETLIFY === "true", + "LOCAL": process.env.LOCAL === "true", "USE_MSW": process.env.USE_MSW === "true" } }); diff --git a/samples/endpoints/shared/.eslintrc.json b/samples/endpoints/shared/.eslintrc.json new file mode 100644 index 000000000..bf0564209 --- /dev/null +++ b/samples/endpoints/shared/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://json.schemastore.org/eslintrc", + "root": true, + "extends": "plugin:@workleap/react-library" +} diff --git a/samples/endpoints/shared/nodemon.json b/samples/endpoints/shared/nodemon.json new file mode 100644 index 000000000..79a8918cc --- /dev/null +++ b/samples/endpoints/shared/nodemon.json @@ -0,0 +1,5 @@ +{ + "watch": ["tsup.dev.ts"], + "exec": "tsup --config ./tsup.dev.ts", + "verbose": true +} diff --git a/samples/endpoints/shared/package.json b/samples/endpoints/shared/package.json new file mode 100644 index 000000000..0a671a805 --- /dev/null +++ b/samples/endpoints/shared/package.json @@ -0,0 +1,52 @@ +{ + "name": "@endpoints/shared", + "author": "Workleap", + "version": "0.0.0", + "description": "Shared package to showcase @squide.", + "private": true, + "license": "Apache-2.0", + "type": "module", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "./FederatedTabsLayout.tsx": { + "import": "./dist/FederatedTabsLayout.js", + "types": "./dist/FederatedTabsLayout.d.ts", + "default": "./dist/FederatedTabsLayout.js" + } + }, + "scripts": { + "dev": "nodemon", + "dev-msw": "pnpm nodemon", + "build": "tsup --config ./tsup.build.ts", + "serve-build": "pnpm build" + }, + "peerDependencies": { + "@squide/react-router": "*", + "@tanstack/react-query": "*", + "react": "*", + "react-dom": "*", + "react-router-dom": "*", + "react-error-boundary": "*" + }, + "devDependencies": { + "@remix-run/router": "1.9.0", + "@squide/react-router": "workspace:*", + "@tanstack/react-query": "4.36.1", + "@types/react": "18.2.28", + "@types/react-dom": "18.2.13", + "@workleap/eslint-plugin": "3.0.0", + "@workleap/tsup-configs": "3.0.1", + "@workleap/typescript-configs": "3.0.2", + "nodemon": "3.0.1", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-router-dom": "6.16.0", + "react-error-boundary": "4.0.11", + "tsup": "7.2.0", + "typescript": "5.2.2" + } +} diff --git a/samples/endpoints/shared/src/FederatedTabsLayout.tsx b/samples/endpoints/shared/src/FederatedTabsLayout.tsx new file mode 100644 index 000000000..2206890aa --- /dev/null +++ b/samples/endpoints/shared/src/FederatedTabsLayout.tsx @@ -0,0 +1,74 @@ +import { useNavigationItems, useRenderedNavigationItems, type NavigationLinkRenderProps, type RenderItemFunction, type RenderSectionFunction } from "@squide/react-router"; +import { QueryErrorResetBoundary } from "@tanstack/react-query"; +import { Suspense, useCallback, type MouseEvent } from "react"; +import { ErrorBoundary, type FallbackProps } from "react-error-boundary"; +import { Link, Outlet, useNavigate } from "react-router-dom"; + +const renderItem: RenderItemFunction = (item, index, level) => { + const { label, linkProps } = item as NavigationLinkRenderProps; + + return ( +
  • + + {label} + +
  • + ); +}; + +const renderSection: RenderSectionFunction = elements => { + return ( +
      + {elements} +
    + ); +}; + +function TabsError({ resetErrorBoundary }: FallbackProps) { + const navigate = useNavigate(); + + const handleReset = useCallback((event: MouseEvent) => { + event.preventDefault(); + + resetErrorBoundary(); + navigate("/federated-tabs"); + }, [resetErrorBoundary, navigate]); + + return ( +
    +
    + An error occured while rendering the tab. +
    + +
    + ); +} + +export function FederatedTabsLayout() { + const navigationItems = useNavigationItems("/federated-tabs"); + const renderedTabs = useRenderedNavigationItems(navigationItems, renderItem, renderSection); + + return ( +
    +

    Tabs

    +

    Every tab is registered by a different module and is lazy loaded.

    + {renderedTabs} +
    + + {({ reset }) => ( + + Loading...
    }> + + + + )} + +
    +
    + ); +} + + diff --git a/samples/endpoints/shared/src/index.ts b/samples/endpoints/shared/src/index.ts new file mode 100644 index 000000000..0490fb04b --- /dev/null +++ b/samples/endpoints/shared/src/index.ts @@ -0,0 +1,5 @@ +export * from "./isNetlify.ts"; +export * from "./session.ts"; +export * from "./subscription.ts"; +export * from "./telemetryService.ts"; + diff --git a/samples/endpoints/shared/src/isNetlify.ts b/samples/endpoints/shared/src/isNetlify.ts new file mode 100644 index 000000000..60dddc162 --- /dev/null +++ b/samples/endpoints/shared/src/isNetlify.ts @@ -0,0 +1,3 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +export const isNetlify = process.env.NETLIFY === true; diff --git a/sample/shared/src/session.ts b/samples/endpoints/shared/src/session.ts similarity index 100% rename from sample/shared/src/session.ts rename to samples/endpoints/shared/src/session.ts diff --git a/sample/shared/src/subscription.ts b/samples/endpoints/shared/src/subscription.ts similarity index 94% rename from sample/shared/src/subscription.ts rename to samples/endpoints/shared/src/subscription.ts index 07fea2c00..ea301e22e 100644 --- a/sample/shared/src/subscription.ts +++ b/samples/endpoints/shared/src/subscription.ts @@ -3,6 +3,8 @@ import { createContext, useContext } from "react"; export type SubscriptionStatus = "unknown" | "trial" | "paid" | "not-paid"; export interface Subscription { + company: string; + contact: string; status: SubscriptionStatus; } diff --git a/sample/shared/src/telemetryService.ts b/samples/endpoints/shared/src/telemetryService.ts similarity index 100% rename from sample/shared/src/telemetryService.ts rename to samples/endpoints/shared/src/telemetryService.ts diff --git a/samples/endpoints/shared/tsconfig.json b/samples/endpoints/shared/tsconfig.json new file mode 100644 index 000000000..f1ddba989 --- /dev/null +++ b/samples/endpoints/shared/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "@workleap/typescript-configs/library.json", + "compilerOptions": { + "paths": { + "@squide/core": ["../../../packages/core/src/index.ts"], + "@squide/react-router": ["../../../packages/react-router/src/index.ts"], + "@squide/webpack-module-federation": ["../../../packages/webpack-module-federation/src/index.ts"], + "@squide/webpack-module-federation/defineConfig.js": ["../../../packages/webpack-module-federation/src/defineConfig.js"], + "@squide/msw": ["../../../packages/msw/src/index.ts"], + "@squide/fakes": ["../../../packages/fakes/src/index.ts"], + } + }, + "exclude": ["dist", "node_modules"] +} diff --git a/samples/endpoints/shared/tsup.build.ts b/samples/endpoints/shared/tsup.build.ts new file mode 100644 index 000000000..1b2c65a89 --- /dev/null +++ b/samples/endpoints/shared/tsup.build.ts @@ -0,0 +1,3 @@ +import { defineBuildConfig } from "@workleap/tsup-configs"; + +export default defineBuildConfig(); diff --git a/samples/endpoints/shared/tsup.dev.ts b/samples/endpoints/shared/tsup.dev.ts new file mode 100644 index 000000000..8aa2904c8 --- /dev/null +++ b/samples/endpoints/shared/tsup.dev.ts @@ -0,0 +1,3 @@ +import { defineDevConfig } from "@workleap/tsup-configs"; + +export default defineDevConfig(); diff --git a/samples/endpoints/shell/.eslintrc.json b/samples/endpoints/shell/.eslintrc.json new file mode 100644 index 000000000..bf0564209 --- /dev/null +++ b/samples/endpoints/shell/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://json.schemastore.org/eslintrc", + "root": true, + "extends": "plugin:@workleap/react-library" +} diff --git a/sample/shell/mocks/authenticationHandlers.ts b/samples/endpoints/shell/mocks/authenticationHandlers.ts similarity index 84% rename from sample/shell/mocks/authenticationHandlers.ts rename to samples/endpoints/shell/mocks/authenticationHandlers.ts index 2721332a9..d252781cf 100644 --- a/sample/shell/mocks/authenticationHandlers.ts +++ b/samples/endpoints/shell/mocks/authenticationHandlers.ts @@ -10,7 +10,7 @@ function simulateDelay(delay: number) { } export const authenticationHandlers: RestHandler[] = [ - rest.post("/login", async (req, res, ctx) => { + rest.post("/api/login", async (req, res, ctx) => { const { username, password } = await req.json(); if (username !== "temp" || password !== "temp") { @@ -19,7 +19,7 @@ export const authenticationHandlers: RestHandler[] = [ ); } - await simulateDelay(2000); + await simulateDelay(1000); sessionManager.setSession({ userId: Math.random(), @@ -31,7 +31,7 @@ export const authenticationHandlers: RestHandler[] = [ ); }), - rest.post("/logout", async (req, res, ctx) => { + rest.post("/api/logout", async (req, res, ctx) => { sessionManager.clearSession(); return res( @@ -39,7 +39,7 @@ export const authenticationHandlers: RestHandler[] = [ ); }), - rest.get("/session", async (req, res, ctx) => { + rest.get("/api/session", async (req, res, ctx) => { const session = sessionManager.getSession(); if (!session) { diff --git a/sample/shell/mocks/session.ts b/samples/endpoints/shell/mocks/session.ts similarity index 100% rename from sample/shell/mocks/session.ts rename to samples/endpoints/shell/mocks/session.ts diff --git a/sample/shell/mocks/subscriptionHandlers.ts b/samples/endpoints/shell/mocks/subscriptionHandlers.ts similarity index 75% rename from sample/shell/mocks/subscriptionHandlers.ts rename to samples/endpoints/shell/mocks/subscriptionHandlers.ts index bc864c9a5..7a62e38d2 100644 --- a/sample/shell/mocks/subscriptionHandlers.ts +++ b/samples/endpoints/shell/mocks/subscriptionHandlers.ts @@ -2,7 +2,7 @@ import { rest, type RestHandler } from "msw"; import { sessionManager } from "./session.ts"; export const subscriptionHandlers: RestHandler[] = [ - rest.get("/subscription", async (req, res, ctx) => { + rest.get("/api/subscription", async (req, res, ctx) => { if (!sessionManager.getSession()) { return res( ctx.status(401) @@ -12,6 +12,8 @@ export const subscriptionHandlers: RestHandler[] = [ return res( ctx.status(200), ctx.json({ + company: "Workleap", + contact: "John Doe", status: "paid" }) ); diff --git a/samples/endpoints/shell/nodemon.json b/samples/endpoints/shell/nodemon.json new file mode 100644 index 000000000..79a8918cc --- /dev/null +++ b/samples/endpoints/shell/nodemon.json @@ -0,0 +1,5 @@ +{ + "watch": ["tsup.dev.ts"], + "exec": "tsup --config ./tsup.dev.ts", + "verbose": true +} diff --git a/sample/shell/package.json b/samples/endpoints/shell/package.json similarity index 90% rename from sample/shell/package.json rename to samples/endpoints/shell/package.json index b7ea2fb79..5d3bb8af3 100644 --- a/sample/shell/package.json +++ b/samples/endpoints/shell/package.json @@ -1,5 +1,5 @@ { - "name": "@sample/shell", + "name": "@endpoints/shell", "author": "Workleap", "version": "0.0.0", "description": "Application shell package to showcase @squide.", @@ -20,7 +20,7 @@ "serve-build": "pnpm build" }, "peerDependencies": { - "@sample/shared": "*", + "@endpoints/shared": "*", "@squide/fakes": "*", "@squide/msw": "*", "@squide/react-router": "*", @@ -33,7 +33,7 @@ }, "devDependencies": { "@remix-run/router": "1.9.0", - "@sample/shared": "workspace:*", + "@endpoints/shared": "workspace:*", "@squide/fakes": "workspace:*", "@squide/msw": "workspace:*", "@squide/react-router": "workspace:*", @@ -49,6 +49,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-router-dom": "6.16.0", + "react-error-boundary": "4.0.11", "tsup": "7.2.0", "typescript": "5.2.2" } diff --git a/sample/shell/src/AuthenticatedLayout.tsx b/samples/endpoints/shell/src/AuthenticatedLayout.tsx similarity index 90% rename from sample/shell/src/AuthenticatedLayout.tsx rename to samples/endpoints/shell/src/AuthenticatedLayout.tsx index c6a1c9d75..7a46cb242 100644 --- a/sample/shell/src/AuthenticatedLayout.tsx +++ b/samples/endpoints/shell/src/AuthenticatedLayout.tsx @@ -1,4 +1,4 @@ -import { toSubscriptionStatusLabel, useApplicationEventBusListener, useSubscription, type Session, type SessionManager } from "@sample/shared"; +import { toSubscriptionStatusLabel, useSubscription, type Session, type SessionManager } from "@endpoints/shared"; import { isNavigationLink, useLogger, useNavigationItems, useRenderedNavigationItems, useSession, type NavigationLinkRenderProps, type NavigationSectionRenderProps, type RenderItemFunction, type RenderSectionFunction } from "@squide/react-router"; import axios from "axios"; import { Suspense, useCallback, type MouseEvent, type ReactNode } from "react"; @@ -47,22 +47,15 @@ export interface AuthenticatedLayoutProps { export function AuthenticatedLayout({ sessionManager }: AuthenticatedLayoutProps) { const logger = useLogger(); - const session = useSession() as Session; const subscription = useSubscription(); const navigate = useNavigate(); - const handleModulesMessage = useCallback((data: unknown) => { - console.log("[sample] Message received from a module: ", data); - }, []); - - useApplicationEventBusListener("write-to-host", handleModulesMessage); - const onDisconnect = useCallback(async (event: MouseEvent) => { event.preventDefault(); - axios.post("/logout") + axios.post("/api/logout") .then(() => { sessionManager.clearSession(); diff --git a/sample/shell/src/AuthenticationBoundary.tsx b/samples/endpoints/shell/src/AuthenticationBoundary.tsx similarity index 88% rename from sample/shell/src/AuthenticationBoundary.tsx rename to samples/endpoints/shell/src/AuthenticationBoundary.tsx index 36f89373e..658c75aa0 100644 --- a/sample/shell/src/AuthenticationBoundary.tsx +++ b/samples/endpoints/shell/src/AuthenticationBoundary.tsx @@ -12,3 +12,5 @@ export function AuthenticationBoundary() { return ; } + +export const Component = AuthenticationBoundary; diff --git a/sample/shell/src/Login.tsx b/samples/endpoints/shell/src/LoginPage.tsx similarity index 96% rename from sample/shell/src/Login.tsx rename to samples/endpoints/shell/src/LoginPage.tsx index 220d94912..d0a8e28a6 100644 --- a/sample/shell/src/Login.tsx +++ b/samples/endpoints/shell/src/LoginPage.tsx @@ -3,7 +3,7 @@ import axios from "axios"; import { useCallback, useState, type ChangeEvent, type MouseEvent } from "react"; import { Navigate } from "react-router-dom"; -export function Login() { +export function LoginPage() { const [username, setUserName] = useState(""); const [password, setPassword] = useState(""); const [errorMessage, setErrorMessage] = useState(); @@ -15,7 +15,7 @@ export function Login() { setIsBusy(true); setErrorMessage(undefined); - axios.post("/login", { username, password }) + axios.post("/api/login", { username, password }) .then(() => { setIsBusy(false); @@ -76,3 +76,5 @@ export function Login() {
    ); } + +export const Component = LoginPage; diff --git a/samples/endpoints/shell/src/LogoutPage.tsx b/samples/endpoints/shell/src/LogoutPage.tsx new file mode 100644 index 000000000..27b37547c --- /dev/null +++ b/samples/endpoints/shell/src/LogoutPage.tsx @@ -0,0 +1,13 @@ +import { Link } from "react-router-dom"; + +export function LogoutPage() { + return ( +
    +

    Logged out

    +
    You are logged out from the application!
    + Log in again +
    + ); +} + +export const Component = LogoutPage; diff --git a/samples/endpoints/shell/src/ModuleErrorBoundary.tsx b/samples/endpoints/shell/src/ModuleErrorBoundary.tsx new file mode 100644 index 000000000..30b1a637b --- /dev/null +++ b/samples/endpoints/shell/src/ModuleErrorBoundary.tsx @@ -0,0 +1,37 @@ +import { useLogger } from "@squide/react-router"; +import { useCallback } from "react"; +import { isRouteErrorResponse, useLocation, useRouteError } from "react-router-dom"; + +function getErrorMessage(error: unknown) { + if (isRouteErrorResponse(error)) { + return `${error.status} ${error.statusText}`; + } + + return error instanceof Error + ? error.message + : JSON.stringify(error); +} + +export function ModuleErrorBoundary() { + const error = useRouteError(); + const location = useLocation(); + const logger = useLogger(); + + const handleReloadButtonClick = useCallback(() => { + window.location.reload(); + }, []); + + logger.error(`[shell] An unmanaged error occurred while rendering the route with path ${location.pathname}`, error); + + return ( +
    +

    Unmanaged error

    +

    An unmanaged error occurred inside a module. Still, other parts of the application are fully functional!

    + 👉 {getErrorMessage(error)} +

    + +
    + ); +} + +export const ErrorBoundary = ModuleErrorBoundary; diff --git a/samples/endpoints/shell/src/NoMatchPage.tsx b/samples/endpoints/shell/src/NoMatchPage.tsx new file mode 100644 index 000000000..8118b8544 --- /dev/null +++ b/samples/endpoints/shell/src/NoMatchPage.tsx @@ -0,0 +1,15 @@ +import { Link } from "react-router-dom"; + +export interface NoMatchPageProps { + path: string; +} + +export function NoMatchPage({ path }: NoMatchPageProps) { + return ( +
    +

    404

    +

    We can't find the path "{path}".

    + Go back home +
    + ); +} diff --git a/samples/endpoints/shell/src/RootErrorBoundary.tsx b/samples/endpoints/shell/src/RootErrorBoundary.tsx new file mode 100644 index 000000000..c3c995fd8 --- /dev/null +++ b/samples/endpoints/shell/src/RootErrorBoundary.tsx @@ -0,0 +1,35 @@ +import { useLogger } from "@squide/react-router"; +import { useCallback } from "react"; +import { isRouteErrorResponse, useLocation, useRouteError } from "react-router-dom"; + +function getErrorMessage(error: unknown) { + if (isRouteErrorResponse(error)) { + return `${error.status} ${error.statusText}`; + } + + return error instanceof Error + ? error.message + : JSON.stringify(error); +} + +export function RootErrorBoundary() { + const error = useRouteError(); + const location = useLocation(); + const logger = useLogger(); + + const handleReloadButtonClick = useCallback(() => { + window.location.reload(); + }, []); + + logger.error(`[shell] An unmanaged error occurred while rendering the route with path ${location.pathname}`, error); + + return ( +
    +

    Unmanaged error

    +

    An unmanaged error occurred and the application is broken, try refreshing your browser.

    + 👉 {getErrorMessage(error)} +

    + +
    + ); +} diff --git a/samples/endpoints/shell/src/RootLayout.tsx b/samples/endpoints/shell/src/RootLayout.tsx new file mode 100644 index 000000000..a818a4409 --- /dev/null +++ b/samples/endpoints/shell/src/RootLayout.tsx @@ -0,0 +1,9 @@ +import { Outlet } from "react-router-dom"; + +export function RootLayout() { + return ( +
    + +
    + ); +} diff --git a/samples/endpoints/shell/src/index.ts b/samples/endpoints/shell/src/index.ts new file mode 100644 index 000000000..02b7b1d3f --- /dev/null +++ b/samples/endpoints/shell/src/index.ts @@ -0,0 +1,3 @@ +export * from "./register.tsx"; +export * from "./useAppRouter.tsx"; + diff --git a/sample/shell/src/register.tsx b/samples/endpoints/shell/src/register.tsx similarity index 72% rename from sample/shell/src/register.tsx rename to samples/endpoints/shell/src/register.tsx index c1c8e0406..bf5967628 100644 --- a/sample/shell/src/register.tsx +++ b/samples/endpoints/shell/src/register.tsx @@ -1,4 +1,4 @@ -import type { SessionManager } from "@sample/shared"; +import type { SessionManager } from "@endpoints/shared"; import { getMswPlugin } from "@squide/msw"; import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { ManagedRoutes } from "@squide/react-router"; @@ -21,13 +21,7 @@ function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { children: [ { // Pathless route to declare an authenticated boundary. - lazy: async () => { - const { AuthenticationBoundary } = await import("./AuthenticationBoundary.tsx"); - - return { - element: - }; - }, + lazy: () => import("./AuthenticationBoundary.tsx"), children: [ { // Pathless route to declare an authenticated layout. @@ -42,13 +36,7 @@ function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { { // Pathless route to declare an error boundary inside the layout instead of outside. // It's quite useful to prevent losing the layout when an unmanaged error occurs. - lazy: async () => { - const { ModuleErrorBoundary } = await import("./ModuleErrorBoundary.tsx"); - - return { - errorElement: - }; - }, + lazy: () => import("./ModuleErrorBoundary.tsx"), children: [ ManagedRoutes ] @@ -67,13 +55,7 @@ function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { runtime.registerRoute({ $visibility: "public", path: "/login", - lazy: async () => { - const { Login } = await import("./Login.tsx"); - - return { - element: - }; - } + lazy: () => import("./LoginPage.tsx") }, { parentName: "root-error-boundary" }); @@ -81,13 +63,7 @@ function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { runtime.registerRoute({ $visibility: "public", path: "/logout", - lazy: async () => { - const { Logout } = await import("./Logout.tsx"); - - return { - element: - }; - } + lazy: () => import("./LogoutPage.tsx") }, { parentName: "root-error-boundary" }); @@ -96,10 +72,10 @@ function registerRoutes(runtime: Runtime, sessionManager: SessionManager) { $visibility: "public", path: "*", lazy: async () => { - const { NoMatch } = await import("./NoMatch.tsx"); + const { NoMatchPage } = await import("./NoMatchPage.tsx"); return { - element: + element: }; } }, { diff --git a/sample/shell/src/useAppRouter.tsx b/samples/endpoints/shell/src/useAppRouter.tsx similarity index 94% rename from sample/shell/src/useAppRouter.tsx rename to samples/endpoints/shell/src/useAppRouter.tsx index 055f865f4..185d64099 100644 --- a/sample/shell/src/useAppRouter.tsx +++ b/samples/endpoints/shell/src/useAppRouter.tsx @@ -1,4 +1,4 @@ -import { SubscriptionContext, TelemetryServiceContext, useTelemetryService, type Session, type SessionManager, type Subscription, type TelemetryService } from "@sample/shared"; +import { SubscriptionContext, TelemetryServiceContext, useTelemetryService, type Session, type SessionManager, type Subscription, type TelemetryService } from "@endpoints/shared"; import { useIsMswStarted } from "@squide/msw"; import { useIsMatchingRouteProtected, useLogger, useRoutes } from "@squide/react-router"; import { useAreModulesReady } from "@squide/webpack-module-federation"; @@ -46,7 +46,7 @@ export function useAppRouter(waitForMsw: boolean, sessionManager: SessionManager if (isActiveRouteProtected) { logger.debug(`[shell] Fetching session data as "${window.location}" is a protected route.`); - const sessionPromise = axios.get("/session") + const sessionPromise = axios.get("/api/session") .then(({ data }) => { const session: Session = { user: { @@ -60,9 +60,11 @@ export function useAppRouter(waitForMsw: boolean, sessionManager: SessionManager sessionManager.setSession(session); }); - const subscriptionPromise = axios.get("/subscription") + const subscriptionPromise = axios.get("/api/subscription") .then(({ data }) => { const _subscription: Subscription = { + company: data.company, + contact: data.contact, status: data.status }; diff --git a/samples/endpoints/shell/tsconfig.json b/samples/endpoints/shell/tsconfig.json new file mode 100644 index 000000000..42197450d --- /dev/null +++ b/samples/endpoints/shell/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "@workleap/typescript-configs/library.json", + "compilerOptions": { + "paths": { + "@squide/core": ["../../../packages/core/src/index.ts"], + "@squide/react-router": ["../../../packages/react-router/src/index.ts"], + "@squide/webpack-module-federation": ["../../../packages/webpack-module-federation/src/index.ts"], + "@squide/webpack-module-federation/defineConfig.js": ["../../../packages/webpack-module-federation/src/defineConfig.js"], + "@squide/msw": ["../../../packages/msw/src/index.ts"], + "@squide/fakes": ["../../../packages/fakes/src/index.ts"], + "@endpoints/shared": ["../shared/src/index.ts"], + "@endpoints/shared/FederatedTabsLayout.tsx": ["../shared/src/FederatedTabsLayout.tsx"] + } + }, + "exclude": ["dist", "node_modules"] +} diff --git a/samples/endpoints/shell/tsup.build.ts b/samples/endpoints/shell/tsup.build.ts new file mode 100644 index 000000000..1b2c65a89 --- /dev/null +++ b/samples/endpoints/shell/tsup.build.ts @@ -0,0 +1,3 @@ +import { defineBuildConfig } from "@workleap/tsup-configs"; + +export default defineBuildConfig(); diff --git a/samples/endpoints/shell/tsup.dev.ts b/samples/endpoints/shell/tsup.dev.ts new file mode 100644 index 000000000..8aa2904c8 --- /dev/null +++ b/samples/endpoints/shell/tsup.dev.ts @@ -0,0 +1,3 @@ +import { defineDevConfig } from "@workleap/tsup-configs"; + +export default defineDevConfig(); From fea5e797651be4af1ae858828fdb2d9d67f11bd4 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Fri, 13 Oct 2023 18:11:05 -0400 Subject: [PATCH 23/51] Added tests for useAreModulesReady --- .../registration/registerLocalModules.md | 27 ++- .../registration/registerRemoteModules.md | 30 ++- .../src/federation/registerLocalModules.ts | 28 ++- .../webpack-module-federation/jest.config.ts | 4 +- .../webpack-module-federation/package.json | 2 + .../src/registerRemoteModules.ts | 17 +- .../src/useAreModulesReady.ts | 2 +- .../webpack-module-federation/swc.jest.ts | 4 +- .../tests/useAreModulesReady.test.tsx | 197 ++++++++++++++++++ pnpm-lock.yaml | 6 + .../another-remote-module/src/dev/index.tsx | 4 +- .../src/dev/register.tsx | 10 - 12 files changed, 304 insertions(+), 27 deletions(-) create mode 100644 packages/webpack-module-federation/tests/useAreModulesReady.test.tsx diff --git a/docs/reference/registration/registerLocalModules.md b/docs/reference/registration/registerLocalModules.md index c985dcaed..f31c32030 100644 --- a/docs/reference/registration/registerLocalModules.md +++ b/docs/reference/registration/registerLocalModules.md @@ -24,10 +24,15 @@ registerLocalModules(registerFunctions: [], runtime, options?: { context? }) ### Returns -Nothing +An array of `LocalModuleRegistrationError` if any happens during the registration. + +- `LocalModuleRegistrationError`: + - `error`: The original error object. ## Usage +### Register a local module + ```tsx !#11 host/src/bootstrap.tsx import { registerLocalModules, Runtime } from "@squide/react-router"; import { register } from "@sample/local-module"; @@ -59,3 +64,23 @@ export function register: ModuleRegisterFunction(runtime, c }); } ``` + +### Handle the registration errors + +```tsx !#11-15 host/src/bootstrap.tsx +import { registerLocalModules, Runtime } from "@squide/react-router"; +import { register } from "@sample/local-module"; +import type { AppContext } from "@sample/shared"; + +const runtime = new Runtime(); + +const context: AppContext = { + name: "Test app" +}; + +registerLocalModules([register], runtime, { context }).then(errors => { + errors.forEach(x => { + console.log(x); + }); +}); +``` diff --git a/docs/reference/registration/registerRemoteModules.md b/docs/reference/registration/registerRemoteModules.md index 98d96868f..b2208068a 100644 --- a/docs/reference/registration/registerRemoteModules.md +++ b/docs/reference/registration/registerRemoteModules.md @@ -24,9 +24,9 @@ registerRemoteModules(remotes: [], runtime, options?: { context? }) ### Returns -A `Promise` object with an array of `RegistrationError` if any happens during the registration. +A `Promise` object with an array of `RemoteModuleRegistrationError` if any happens during the registration. -- `RegistrationError`: +- `RemoteModuleRegistrationError`: - `url`: The URL of the module federation remote that failed to load. - `containerName`: The name of the [dynamic container](https://webpack.js.org/concepts/module-federation/#dynamic-remote-containers) that Squide attempted to recover. - `moduleName`: The name of the [module](#name) that Squide attempted to recover. @@ -34,6 +34,8 @@ A `Promise` object with an array of `RegistrationError` if any happens during th ## Usage +### Register a remote module + ```tsx !#11-13,15 host/src/bootstrap.tsx import { Runtime } from "@squide/react-router"; import { registerRemoteModules, type RemoteDefinition } from "@squide/webpack-module-federation"; @@ -70,6 +72,30 @@ export function register: ModuleRegisterFunction(runtime, c } ``` +### Handle the registration errors + +```tsx !#15-19 host/src/bootstrap.tsx +import { Runtime } from "@squide/react-router"; +import { registerRemoteModules, type RemoteDefinition } from "@squide/webpack-module-federation"; +import type { AppContext } from "@sample/shared"; + +const runtime = new Runtime(); + +const context: AppContext = { + name: "Test app" +}; + +const Remotes: RemoteDefinition = [ + { name: "remote1", url: "http://localhost:8081" } +]; + +registerRemoteModules(Remotes, runtime, { context }).then(errors => { + errors.forEach(x => { + console.log(x); + }); +}); +``` + ## Remote definition To ease the configuration of remote modules, make sure that you first import the `RemoteDefinition` type and assign it to your remote definitions array declaration. diff --git a/packages/core/src/federation/registerLocalModules.ts b/packages/core/src/federation/registerLocalModules.ts index 3a76afb37..668cde255 100644 --- a/packages/core/src/federation/registerLocalModules.ts +++ b/packages/core/src/federation/registerLocalModules.ts @@ -7,6 +7,15 @@ let registrationStatus: ModuleRegistrationStatus = "none"; // Aliasing to make the name more explicit to external modules. export { registrationStatus as localModulesRegistrationStatus }; +export function resetLocalModulesRegistrationStatus() { + registrationStatus = "none"; +} + +export interface LocalModuleRegistraError { + // The registration error. + error: unknown; +} + export interface RegisterLocalModulesOptions { context?: TContext; } @@ -16,17 +25,32 @@ export function registerLocalModules { runtime.logger.information(`[squide] ${index + 1}/${registerFunctions.length} Registering local module${registerFunctions.length !== 1 ? "s" : ""}.`); - x(runtime, context); + try { + x(runtime, context); + } catch (error: unknown) { + runtime.logger.error( + `[squide] ${index + 1}/${registerFunctions.length} An error occured while registering a local module.`, + error + ); + + errors.push({ + error + }); + } runtime.logger.information(`[squide] ${index + 1}/${registerFunctions.length} Local module${registerFunctions.length !== 1 ? "s" : ""} registration completed.`); }); registrationStatus = "ready"; + + return errors; } diff --git a/packages/webpack-module-federation/jest.config.ts b/packages/webpack-module-federation/jest.config.ts index 3bf2eaaf1..ecc6a2f7a 100644 --- a/packages/webpack-module-federation/jest.config.ts +++ b/packages/webpack-module-federation/jest.config.ts @@ -5,7 +5,7 @@ import { swcConfig } from "./swc.jest.ts"; import { compilerOptions } from "./tsconfig.json"; const config: Config = { - testEnvironment: "node", + testEnvironment: "jsdom", transformIgnorePatterns: [ // Must exclude @workleap/webpack-configs from the transform ignore files // because it's an ESM only project which must be processed by SWC. @@ -18,7 +18,7 @@ const config: Config = { transform: { // Must add the ".js" file extension because the files from @workleap/webpack-configs // are ESM only and therefore must be processed by SWC. - "^.+\\.(js|ts)$": ["@swc/jest", swcConfig as Record] + "^.+\\.(js|ts|tsx)$": ["@swc/jest", swcConfig as Record] }, moduleNameMapper: { ...pathsToModuleNameMapper(compilerOptions.paths, { diff --git a/packages/webpack-module-federation/package.json b/packages/webpack-module-federation/package.json index 41bf584a5..b2448df3d 100644 --- a/packages/webpack-module-federation/package.json +++ b/packages/webpack-module-federation/package.json @@ -46,6 +46,7 @@ "@swc/core": "1.3.92", "@swc/helpers": "0.5.3", "@swc/jest": "0.2.29", + "@testing-library/react": "14.0.0", "@types/jest": "29.5.5", "@types/node": "20.8.4", "@types/react": "18.2.28", @@ -56,6 +57,7 @@ "@workleap/typescript-configs": "3.0.2", "@workleap/webpack-configs": "1.1.0", "jest": "29.7.0", + "jest-environment-jsdom": "29.7.0", "react": "18.2.0", "react-dom": "18.2.0", "ts-jest": "29.1.1", diff --git a/packages/webpack-module-federation/src/registerRemoteModules.ts b/packages/webpack-module-federation/src/registerRemoteModules.ts index 9d62b6ee3..310f922f4 100644 --- a/packages/webpack-module-federation/src/registerRemoteModules.ts +++ b/packages/webpack-module-federation/src/registerRemoteModules.ts @@ -7,14 +7,19 @@ let registrationStatus: ModuleRegistrationStatus = "none"; // Aliasing to make the name more explicit to external modules. export { registrationStatus as remoteModulesRegistrationStatus }; -export interface RegistrationError { - // The remote base URL +// Strictly for testing purpose. +export function resetRemoteModuleRegistrationStatus() { + registrationStatus = "none"; +} + +export interface RemoteModuleRegistrationError { + // The remote base URL. url: string; - // The remote container name + // The remote container name. containerName: string; - // The remote resource module name + // The remote resource module name. moduleName: string; - // The registration error + // The registration error. error: unknown; } @@ -27,7 +32,7 @@ export async function registerRemoteModules(remotes: RemoteDefinition[], runtime throw new Error("[squide] registerRemoteModules() can only be called once."); } - const errors: RegistrationError[] = []; + const errors: RemoteModuleRegistrationError[] = []; runtime.logger.information(`[squide] Found ${remotes.length} remote module${remotes.length !== 1 ? "s" : ""} to register.`); diff --git a/packages/webpack-module-federation/src/useAreModulesReady.ts b/packages/webpack-module-federation/src/useAreModulesReady.ts index 6d49a5d17..d64b21a50 100644 --- a/packages/webpack-module-federation/src/useAreModulesReady.ts +++ b/packages/webpack-module-federation/src/useAreModulesReady.ts @@ -11,7 +11,7 @@ export interface UseAreModulesReadyOptions { function areModulesReady() { // Validating for "in-progress" instead of "ready" for the local module because "registerLocalModules" // could never be called. - return localModulesRegistrationStatus !== "in-progress" && remoteModulesRegistrationStatus === "ready"; + return localModulesRegistrationStatus !== "in-progress" && remoteModulesRegistrationStatus !== "in-progress"; } export function useAreModulesReady({ interval = 10 }: UseAreModulesReadyOptions = {}) { diff --git a/packages/webpack-module-federation/swc.jest.ts b/packages/webpack-module-federation/swc.jest.ts index 2e6bdb4ce..dc009c5ef 100644 --- a/packages/webpack-module-federation/swc.jest.ts +++ b/packages/webpack-module-federation/swc.jest.ts @@ -1,3 +1,5 @@ import { defineJestConfig } from "@workleap/swc-configs"; -export const swcConfig = defineJestConfig(); +export const swcConfig = defineJestConfig({ + react: true +}); diff --git a/packages/webpack-module-federation/tests/useAreModulesReady.test.tsx b/packages/webpack-module-federation/tests/useAreModulesReady.test.tsx new file mode 100644 index 000000000..5bed6840f --- /dev/null +++ b/packages/webpack-module-federation/tests/useAreModulesReady.test.tsx @@ -0,0 +1,197 @@ +import { AbstractRuntime, RuntimeContext, registerLocalModules, resetLocalModulesRegistrationStatus } from "@squide/core"; +import { act, renderHook } from "@testing-library/react"; +import type { ReactNode } from "react"; +import { loadRemote } from "../src/loadRemote.ts"; +import { registerRemoteModules, resetRemoteModuleRegistrationStatus } from "../src/registerRemoteModules.ts"; +import { useAreModulesReady } from "../src/useAreModulesReady.ts"; + +// The mock implementation is defined directly in the tests. +jest.mock("../src/loadRemote.ts"); + +// The interval at which the hook will perform a check to determine if the modules are ready. +const CheckInterval = 10; + +class DummyRuntime extends AbstractRuntime { + registerRoute() { + throw new Error("Method not implemented."); + } + + get routes() { + return []; + } + + registerNavigationItem() { + throw new Error("Method not implemented."); + } + + getNavigationItems() { + return []; + } +} + +function renderWithRuntime(runtime: AbstractRuntime) { + return renderHook(() => useAreModulesReady({ interval: CheckInterval }), { + wrapper: ({ children }: { children?: ReactNode }) => ( + + {children} + + ) + }); +} + +beforeEach(() => { + // Since the module registration status variables are singletons, + // they are not reseted between the tests. + resetLocalModulesRegistrationStatus(); + resetRemoteModuleRegistrationStatus(); + + // Typing a mocked imported function is too complicated. + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (loadRemote.mockClear) { + // Typing a mocked imported function is too complicated. + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + loadRemote.mockClear(); + } + + jest.useFakeTimers(); +}); + +afterEach(() => { + jest.useRealTimers(); +}); + +test("when only local modules are registered, return true when all the local modules are registered", () => { + const runtime = new DummyRuntime(); + + registerLocalModules([ + () => {}, + () => {}, + () => {} + ], runtime); + + const { result } = renderWithRuntime(runtime); + + expect(result.current).toBeFalsy(); + + // To justify the usage of act, refer to: https://github.com/testing-library/react-hooks-testing-library/issues/241 + act(() => { + jest.advanceTimersByTime(CheckInterval + 1); + }); + + expect(result.current).toBeTruthy(); +}); + +test("when only remote modules are registered, return true when all the remote modules are registered", async () => { + // Typing a mocked imported function is too complicated. + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + loadRemote.mockResolvedValue({ + register: jest.fn() + }); + + const runtime = new DummyRuntime(); + + await registerRemoteModules([ + { name: "Dummy-1", url: "http://anything1.com" }, + { name: "Dummy-2", url: "http://anything2.com" }, + { name: "Dummy-3", url: "http://anything3.com" } + ], runtime); + + const { result } = renderWithRuntime(runtime); + + expect(result.current).toBeFalsy(); + + // To justify the usage of act, refer to: https://github.com/testing-library/react-hooks-testing-library/issues/241 + act(() => { + jest.advanceTimersByTime(CheckInterval + 1); + }); + + expect(result.current).toBeTruthy(); +}); + +test("when local and remote modules are registered, return true when all the remote modules are registered", async () => { + // Typing a mocked imported function is too complicated. + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + loadRemote.mockResolvedValue({ + register: jest.fn() + }); + + const runtime = new DummyRuntime(); + + registerLocalModules([ + () => {}, + () => {}, + () => {} + ], runtime); + + await registerRemoteModules([ + { name: "Dummy-1", url: "http://anything1.com" }, + { name: "Dummy-2", url: "http://anything2.com" }, + { name: "Dummy-3", url: "http://anything3.com" } + ], runtime); + + const { result } = renderWithRuntime(runtime); + + expect(result.current).toBeFalsy(); + + // To justify the usage of act, refer to: https://github.com/testing-library/react-hooks-testing-library/issues/241 + act(() => { + jest.advanceTimersByTime(CheckInterval + 1); + }); + + expect(result.current).toBeTruthy(); +}); + +test("when a local module registration fail, return true when all the other modules are registered", () => { + const runtime = new DummyRuntime(); + + registerLocalModules([ + () => {}, + () => { throw new Error("Registration failed!"); }, + () => {} + ], runtime); + + const { result } = renderWithRuntime(runtime); + + expect(result.current).toBeFalsy(); + + // To justify the usage of act, refer to: https://github.com/testing-library/react-hooks-testing-library/issues/241 + act(() => { + jest.advanceTimersByTime(CheckInterval + 1); + }); + + expect(result.current).toBeTruthy(); +}); + +test("when a remote module registration fail, return true when all the other modules are registered", async () => { + const resolvedValue = { + register: jest.fn() + }; + + // Typing a mocked imported function is too complicated. + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + loadRemote.mockResolvedValueOnce(resolvedValue).mockResolvedValueOnce(resolvedValue).mockRejectedValueOnce(null); + + const runtime = new DummyRuntime(); + + await registerRemoteModules([ + { name: "Dummy-1", url: "http://anything1.com" }, + { name: "Dummy-2", url: "http://anything2.com" }, + { name: "Dummy-3", url: "http://anything3.com" } + ], runtime); + + const { result } = renderWithRuntime(runtime); + + expect(result.current).toBeFalsy(); + + // To justify the usage of act, refer to: https://github.com/testing-library/react-hooks-testing-library/issues/241 + act(() => { + jest.advanceTimersByTime(CheckInterval + 1); + }); + + expect(result.current).toBeTruthy(); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 789f9258d..913d672f6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -241,6 +241,9 @@ importers: '@swc/jest': specifier: 0.2.29 version: 0.2.29(@swc/core@1.3.92) + '@testing-library/react': + specifier: 14.0.0 + version: 14.0.0(react-dom@18.2.0)(react@18.2.0) '@types/jest': specifier: 29.5.5 version: 29.5.5 @@ -271,6 +274,9 @@ importers: jest: specifier: 29.7.0 version: 29.7.0(@types/node@20.8.4)(ts-node@10.9.1) + jest-environment-jsdom: + specifier: 29.7.0 + version: 29.7.0 react: specifier: 18.2.0 version: 18.2.0 diff --git a/samples/basic/another-remote-module/src/dev/index.tsx b/samples/basic/another-remote-module/src/dev/index.tsx index 42d730435..0b01b35ba 100644 --- a/samples/basic/another-remote-module/src/dev/index.tsx +++ b/samples/basic/another-remote-module/src/dev/index.tsx @@ -2,7 +2,7 @@ import { registerShell } from "@basic/shell"; import { ConsoleLogger, Runtime, RuntimeContext, registerLocalModules } from "@squide/react-router"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; -import { registerLocalModule } from "../register.tsx"; +import { register as registerModule } from "../register.tsx"; import { App } from "./App.tsx"; import { registerDev } from "./register.tsx"; import { sessionAccessor, sessionManager } from "./session.ts"; @@ -14,7 +14,7 @@ const runtime = new Runtime({ sessionAccessor }); -registerLocalModules([registerShell(sessionManager), registerDev, registerLocalModule], runtime); +registerLocalModules([registerShell(sessionManager), registerDev, registerModule], runtime); const root = createRoot(document.getElementById("root")!); diff --git a/samples/basic/another-remote-module/src/dev/register.tsx b/samples/basic/another-remote-module/src/dev/register.tsx index 1072f8664..9628ce656 100644 --- a/samples/basic/another-remote-module/src/dev/register.tsx +++ b/samples/basic/another-remote-module/src/dev/register.tsx @@ -6,14 +6,4 @@ export const registerDev: ModuleRegisterFunction = runtime => { index: true, element: }); - - runtime.registerRoute({ - path: "/federated-tabs", - lazy: () => import("@basic/shared/FederatedTabsLayout.tsx") - }); - - runtime.registerNavigationItem({ - $label: "Tabs", - to: "/federated-tabs" - }); }; From 237f67710b1001351c3eaad2b561afae8d8522e8 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Fri, 13 Oct 2023 22:05:21 -0400 Subject: [PATCH 24/51] Fixed local dev environment for the samples --- .../src/federation/registerLocalModules.ts | 10 +- .../src/registerRemoteModules.ts | 14 +- pnpm-lock.yaml | 3 + .../basic/another-remote-module/nodemon.json | 5 + .../basic/another-remote-module/package.json | 5 +- .../another-remote-module/webpack.dev.js | 4 +- samples/basic/local-module/webpack.config.js | 4 +- samples/basic/remote-module/webpack.dev.js | 4 +- .../basic/shell/src/AuthenticatedLayout.tsx | 2 +- samples/endpoints/local-module/package.json | 2 +- .../local-module/public/mockServiceWorker.js | 303 ++++++++++++++++++ .../endpoints/local-module/src/dev/index.tsx | 3 + .../endpoints/local-module/webpack.config.js | 1 + .../endpoints/remote-module/src/dev/index.tsx | 4 +- .../endpoints/remote-module/webpack.dev.js | 2 + samples/endpoints/shell/src/useAppRouter.tsx | 8 + 16 files changed, 354 insertions(+), 20 deletions(-) create mode 100644 samples/basic/another-remote-module/nodemon.json create mode 100644 samples/endpoints/local-module/public/mockServiceWorker.js diff --git a/packages/core/src/federation/registerLocalModules.ts b/packages/core/src/federation/registerLocalModules.ts index 668cde255..6c216064c 100644 --- a/packages/core/src/federation/registerLocalModules.ts +++ b/packages/core/src/federation/registerLocalModules.ts @@ -22,23 +22,23 @@ export interface RegisterLocalModulesOptions { export function registerLocalModules(registerFunctions: ModuleRegisterFunction[], runtime: TRuntime, { context }: RegisterLocalModulesOptions = {}) { if (registrationStatus !== "none") { - throw new Error("[squide] registerLocalModules() can only be called once."); + throw new Error("[squide] [local] registerLocalModules() can only be called once."); } const errors: LocalModuleRegistraError[] = []; - runtime.logger.information(`[squide] Found ${registerFunctions.length} local module${registerFunctions.length !== 1 ? "s" : ""} to register.`); + runtime.logger.information(`[squide] [local] Found ${registerFunctions.length} local module${registerFunctions.length !== 1 ? "s" : ""} to register.`); registrationStatus = "in-progress"; registerFunctions.forEach((x, index) => { - runtime.logger.information(`[squide] ${index + 1}/${registerFunctions.length} Registering local module${registerFunctions.length !== 1 ? "s" : ""}.`); + runtime.logger.information(`[squide] [local] ${index + 1}/${registerFunctions.length} Registering local module.`); try { x(runtime, context); } catch (error: unknown) { runtime.logger.error( - `[squide] ${index + 1}/${registerFunctions.length} An error occured while registering a local module.`, + `[squide] [local] ${index + 1}/${registerFunctions.length} An error occured while registering a local module.`, error ); @@ -47,7 +47,7 @@ export function registerLocalModules
    - (User: {session.user.name}) + (User: {session?.user?.name})
    diff --git a/samples/endpoints/local-module/package.json b/samples/endpoints/local-module/package.json index 596169f84..5ba4ff37e 100644 --- a/samples/endpoints/local-module/package.json +++ b/samples/endpoints/local-module/package.json @@ -15,7 +15,7 @@ }, "scripts": { "dev": "nodemon", - "dev-local": "cross-env USE_MSW=true nodemon --config nodemon.local.json", + "dev-local": "cross-env USE_MSW=true LOCAL=true nodemon --config nodemon.local.json", "build": "tsup --config ./tsup.build.ts", "serve-build": "pnpm build" }, diff --git a/samples/endpoints/local-module/public/mockServiceWorker.js b/samples/endpoints/local-module/public/mockServiceWorker.js new file mode 100644 index 000000000..8ee70b3e4 --- /dev/null +++ b/samples/endpoints/local-module/public/mockServiceWorker.js @@ -0,0 +1,303 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker (1.2.2). + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + * - Please do NOT serve this file on production. + */ + +const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70' +const activeClientIds = new Set() + +self.addEventListener('install', function () { + self.skipWaiting() +}) + +self.addEventListener('activate', function (event) { + event.waitUntil(self.clients.claim()) +}) + +self.addEventListener('message', async function (event) { + const clientId = event.source.id + + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: INTEGRITY_CHECKSUM, + }) + break + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: true, + }) + break + } + + case 'MOCK_DEACTIVATE': { + activeClientIds.delete(clientId) + break + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break + } + } +}) + +self.addEventListener('fetch', function (event) { + const { request } = event + const accept = request.headers.get('accept') || '' + + // Bypass server-sent events. + if (accept.includes('text/event-stream')) { + return + } + + // Bypass navigation requests. + if (request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + // Generate unique request ID. + const requestId = Math.random().toString(16).slice(2) + + event.respondWith( + handleRequest(event, requestId).catch((error) => { + if (error.name === 'NetworkError') { + console.warn( + '[MSW] Successfully emulated a network error for the "%s %s" request.', + request.method, + request.url, + ) + return + } + + // At this point, any exception indicates an issue with the original request/response. + console.error( + `\ +[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`, + request.method, + request.url, + `${error.name}: ${error.message}`, + ) + }), + ) +}) + +async function handleRequest(event, requestId) { + const client = await resolveMainClient(event) + const response = await getResponse(event, client, requestId) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + ;(async function () { + const clonedResponse = response.clone() + sendToClient(client, { + type: 'RESPONSE', + payload: { + requestId, + type: clonedResponse.type, + ok: clonedResponse.ok, + status: clonedResponse.status, + statusText: clonedResponse.statusText, + body: + clonedResponse.body === null ? null : await clonedResponse.text(), + headers: Object.fromEntries(clonedResponse.headers.entries()), + redirected: clonedResponse.redirected, + }, + }) + })() + } + + return response +} + +// Resolve the main client for the given event. +// Client that issues a request doesn't necessarily equal the client +// that registered the worker. It's with the latter the worker should +// communicate with during the response resolving phase. +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (client?.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + +async function getResponse(event, client, requestId) { + const { request } = event + const clonedRequest = request.clone() + + function passthrough() { + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const headers = Object.fromEntries(clonedRequest.headers.entries()) + + // Remove MSW-specific request headers so the bypassed requests + // comply with the server's CORS preflight check. + // Operate with the headers as an object because request "Headers" + // are immutable. + delete headers['x-msw-bypass'] + + return fetch(clonedRequest, { headers }) + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough() + } + + // Bypass requests with the explicit bypass header. + // Such requests can be issued by "ctx.fetch()". + if (request.headers.get('x-msw-bypass') === 'true') { + return passthrough() + } + + // Notify the client that a request has been intercepted. + const clientMessage = await sendToClient(client, { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + mode: request.mode, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: await request.text(), + bodyUsed: request.bodyUsed, + keepalive: request.keepalive, + }, + }) + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data) + } + + case 'MOCK_NOT_FOUND': { + return passthrough() + } + + case 'NETWORK_ERROR': { + const { name, message } = clientMessage.data + const networkError = new Error(message) + networkError.name = name + + // Rejecting a "respondWith" promise emulates a network error. + throw networkError + } + } + + return passthrough() +} + +function sendToClient(client, message) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } + + resolve(event.data) + } + + client.postMessage(message, [channel.port2]) + }) +} + +function sleep(timeMs) { + return new Promise((resolve) => { + setTimeout(resolve, timeMs) + }) +} + +async function respondWithMock(response) { + await sleep(response.delay) + return new Response(response.body, response) +} diff --git a/samples/endpoints/local-module/src/dev/index.tsx b/samples/endpoints/local-module/src/dev/index.tsx index df382aa47..577c7986c 100644 --- a/samples/endpoints/local-module/src/dev/index.tsx +++ b/samples/endpoints/local-module/src/dev/index.tsx @@ -14,6 +14,7 @@ const mswPlugin = new MswPlugin(); // Services, loggers and sessionAccessor could be reuse through a shared packages or faked when in isolation. const runtime = new Runtime({ loggers: [new ConsoleLogger()], + plugins: [mswPlugin], sessionAccessor }); @@ -24,6 +25,8 @@ registerLocalModules([registerShell(sessionManager), registerDev, registerLocalM if (process.env.USE_MSW) { import("../../mocks/browser.ts").then(({ startMsw }) => { startMsw(mswPlugin.requestHandlers); + + mswPlugin.setAsStarted(); }); } diff --git a/samples/endpoints/local-module/webpack.config.js b/samples/endpoints/local-module/webpack.config.js index f102677b4..7921ee8b1 100644 --- a/samples/endpoints/local-module/webpack.config.js +++ b/samples/endpoints/local-module/webpack.config.js @@ -5,6 +5,7 @@ import path from "node:path"; import { swcConfig } from "./swc.config.js"; export default defineDevConfig(swcConfig, { + cache: false, entry: path.resolve("./src/dev/index.tsx"), overlay: false, environmentVariables: { diff --git a/samples/endpoints/remote-module/src/dev/index.tsx b/samples/endpoints/remote-module/src/dev/index.tsx index e6413bc3e..82db29031 100644 --- a/samples/endpoints/remote-module/src/dev/index.tsx +++ b/samples/endpoints/remote-module/src/dev/index.tsx @@ -13,8 +13,8 @@ const mswPlugin = new MswPlugin(); // Create the shell runtime. // Services, loggers and sessionAccessor could be reuse through a shared packages or faked when in isolation. const runtime = new Runtime({ - plugins: [mswPlugin], loggers: [new ConsoleLogger()], + plugins: [mswPlugin], sessionAccessor }); @@ -27,6 +27,8 @@ registerLocalModules([registerShell(sessionManager), registerDev, registerModule if (process.env.USE_MSW) { import("../../mocks/browser.ts").then(({ startMsw }) => { startMsw(mswPlugin.requestHandlers); + + mswPlugin.setAsStarted(); }); } diff --git a/samples/endpoints/remote-module/webpack.dev.js b/samples/endpoints/remote-module/webpack.dev.js index 30277d5be..be01c7a89 100644 --- a/samples/endpoints/remote-module/webpack.dev.js +++ b/samples/endpoints/remote-module/webpack.dev.js @@ -22,9 +22,11 @@ if (!process.env.LOCAL) { }); } else { config = defineDevConfig(swcConfig, { + cache: false, entry: path.resolve("./src/dev/index.tsx"), overlay: false, environmentVariables: { + "LOCAL": process.env.LOCAL === "true", "USE_MSW": process.env.USE_MSW === "true" } }); diff --git a/samples/endpoints/shell/src/useAppRouter.tsx b/samples/endpoints/shell/src/useAppRouter.tsx index 185d64099..039d7b54e 100644 --- a/samples/endpoints/shell/src/useAppRouter.tsx +++ b/samples/endpoints/shell/src/useAppRouter.tsx @@ -42,6 +42,14 @@ export function useAppRouter(waitForMsw: boolean, sessionManager: SessionManager const isActiveRouteProtected = useIsMatchingRouteProtected(window.location); useEffect(() => { + if (areModulesReady && !isMswStarted) { + logger.debug("[shell] Modules are ready, waiting for MSW to start."); + } + + if (!areModulesReady && isMswStarted) { + logger.debug("[shell] MSW is started, waiting for the modules to be ready."); + } + if (areModulesReady && isMswStarted) { if (isActiveRouteProtected) { logger.debug(`[shell] Fetching session data as "${window.location}" is a protected route.`); From afefc09c47b0acfe237c6a94c511f39e630da533 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Mon, 16 Oct 2023 01:02:21 -0400 Subject: [PATCH 25/51] Fixing MSW for the production build --- .../registration/registerLocalModules.md | 2 +- .../registration/registerRemoteModules.md | 2 +- docs/reference/runtime/useRuntime.md | 2 +- .../src/federation/registerLocalModules.ts | 10 ++++----- .../core/src/federation/registerModule.ts | 4 ++-- .../src/defineConfig.ts | 22 +++++++++++++++++++ .../src/registerRemoteModules.ts | 2 +- samples/basic/host/package.json | 1 + samples/endpoints/host/mocks/browser.ts | 6 ++--- samples/endpoints/host/mocks/handlers.ts | 4 ++++ samples/endpoints/host/package.json | 3 ++- samples/endpoints/host/src/bootstrap.tsx | 5 +++-- samples/endpoints/host/src/register.tsx | 16 +++++++++----- samples/endpoints/host/src/session.ts | 6 ----- .../endpoints/local-module/mocks/browser.ts | 6 ++--- .../endpoints/local-module/mocks/handlers.ts | 4 ++++ samples/endpoints/local-module/package.json | 4 ++-- .../local-module/src/CharactersTab.tsx | 2 +- .../endpoints/local-module/src/dev/index.tsx | 2 ++ .../endpoints/local-module/src/register.tsx | 16 +++++++++----- .../endpoints/remote-module/mocks/browser.ts | 6 ++--- .../endpoints/remote-module/mocks/handlers.ts | 6 +++++ samples/endpoints/remote-module/package.json | 2 +- .../endpoints/remote-module/src/dev/index.tsx | 2 ++ .../endpoints/remote-module/src/register.tsx | 22 ++++++++++--------- .../shell/src/AuthenticationBoundary.tsx | 3 ++- samples/endpoints/shell/src/useAppRouter.tsx | 2 +- 27 files changed, 107 insertions(+), 55 deletions(-) create mode 100644 samples/endpoints/host/mocks/handlers.ts create mode 100644 samples/endpoints/local-module/mocks/handlers.ts create mode 100644 samples/endpoints/remote-module/mocks/handlers.ts diff --git a/docs/reference/registration/registerLocalModules.md b/docs/reference/registration/registerLocalModules.md index f31c32030..079131342 100644 --- a/docs/reference/registration/registerLocalModules.md +++ b/docs/reference/registration/registerLocalModules.md @@ -24,7 +24,7 @@ registerLocalModules(registerFunctions: [], runtime, options?: { context? }) ### Returns -An array of `LocalModuleRegistrationError` if any happens during the registration. +A `Promise` object with an array of `LocalModuleRegistrationError` if any error happens during the registration. - `LocalModuleRegistrationError`: - `error`: The original error object. diff --git a/docs/reference/registration/registerRemoteModules.md b/docs/reference/registration/registerRemoteModules.md index b2208068a..ba3ed89d1 100644 --- a/docs/reference/registration/registerRemoteModules.md +++ b/docs/reference/registration/registerRemoteModules.md @@ -24,7 +24,7 @@ registerRemoteModules(remotes: [], runtime, options?: { context? }) ### Returns -A `Promise` object with an array of `RemoteModuleRegistrationError` if any happens during the registration. +A `Promise` object with an array of `RemoteModuleRegistrationError` if any error happens during the registration. - `RemoteModuleRegistrationError`: - `url`: The URL of the module federation remote that failed to load. diff --git a/docs/reference/runtime/useRuntime.md b/docs/reference/runtime/useRuntime.md index 5efef17f9..c18df5ee0 100644 --- a/docs/reference/runtime/useRuntime.md +++ b/docs/reference/runtime/useRuntime.md @@ -9,7 +9,7 @@ toc: Retrive a shared `Runtime` instance. !!!info -When possible, prefer [useRoutes](useRoutes.md), [useNavigationItems](useNavigationItems.md), [useLogger](useLogger.md), [useServices](useServices.md), [useService](useService.md) to `useRuntime`. +When possible, prefer [useRoutes](useRoutes.md), [useNavigationItems](useNavigationItems.md), [useLogger](useLogger.md) to `useRuntime`. !!! ## Reference diff --git a/packages/core/src/federation/registerLocalModules.ts b/packages/core/src/federation/registerLocalModules.ts index 6c216064c..069501c8a 100644 --- a/packages/core/src/federation/registerLocalModules.ts +++ b/packages/core/src/federation/registerLocalModules.ts @@ -1,6 +1,6 @@ import type { AbstractRuntime } from "../runtime/abstractRuntime.ts"; import type { ModuleRegistrationStatus } from "./moduleRegistrationStatus.ts"; -import type { ModuleRegisterFunction } from "./registerModule.ts"; +import { registerModule, type ModuleRegisterFunction } from "./registerModule.ts"; let registrationStatus: ModuleRegistrationStatus = "none"; @@ -20,7 +20,7 @@ export interface RegisterLocalModulesOptions { context?: TContext; } -export function registerLocalModules(registerFunctions: ModuleRegisterFunction[], runtime: TRuntime, { context }: RegisterLocalModulesOptions = {}) { +export async function registerLocalModules(registerFunctions: ModuleRegisterFunction[], runtime: TRuntime, { context }: RegisterLocalModulesOptions = {}) { if (registrationStatus !== "none") { throw new Error("[squide] [local] registerLocalModules() can only be called once."); } @@ -31,11 +31,11 @@ export function registerLocalModules { + await Promise.allSettled(registerFunctions.map(async (x, index) => { runtime.logger.information(`[squide] [local] ${index + 1}/${registerFunctions.length} Registering local module.`); try { - x(runtime, context); + registerModule(x as ModuleRegisterFunction, runtime, context); } catch (error: unknown) { runtime.logger.error( `[squide] [local] ${index + 1}/${registerFunctions.length} An error occured while registering a local module.`, @@ -48,7 +48,7 @@ export function registerLocalModules = (runtime: TRuntime, context?: TContext) => void; -export function registerModule(register: ModuleRegisterFunction, runtime: AbstractRuntime, context?: unknown) { - register(runtime, context); +export async function registerModule(register: ModuleRegisterFunction, runtime: AbstractRuntime, context?: unknown) { + return register(runtime, context); } diff --git a/packages/webpack-module-federation/src/defineConfig.ts b/packages/webpack-module-federation/src/defineConfig.ts index 7b8c9fa2c..7ca474541 100644 --- a/packages/webpack-module-federation/src/defineConfig.ts +++ b/packages/webpack-module-federation/src/defineConfig.ts @@ -60,6 +60,18 @@ export interface DefineHostModuleFederationPluginOptions extends ModuleFederatio router?: Router; } +const useNamedChunkIdsTransformer: WebpackConfigTransformer = (config: WebpackConfig) => { + config.optimization = { + ...(config.optimization ?? {}), + // Without named chunk ids, there are some Webpack features that do not work + // when used with Module Federation. One of these feature is using a dynamic import in + // a remote module. + chunkIds: "named" + }; + + return config; +}; + //////////////////////////// Host ///////////////////////////// // The function return type is mandatory, otherwise we got an error TS4058. @@ -134,6 +146,7 @@ export function defineBuildHostConfig(swcConfig: SwcConfig, applicationName: str cache = false, plugins = [], htmlWebpackPluginOptions, + transformers = [], router, sharedDependencies, moduleFederationPluginOptions = defineHostModuleFederationPluginOptions(applicationName, { router, shared: sharedDependencies }), @@ -149,6 +162,10 @@ export function defineBuildHostConfig(swcConfig: SwcConfig, applicationName: str ...plugins, new webpack.container.ModuleFederationPlugin(moduleFederationPluginOptions) ], + transformers: [ + useNamedChunkIdsTransformer, + ...transformers + ], ...webpackOptions }); } @@ -258,6 +275,7 @@ export function defineBuildRemoteModuleConfig(swcConfig: SwcConfig, applicationN cache = false, plugins = [], htmlWebpackPlugin = false, + transformers = [], router, sharedDependencies, moduleFederationPluginOptions = defineRemoteModuleFederationPluginOptions(applicationName, { router, shared: sharedDependencies }), @@ -273,6 +291,10 @@ export function defineBuildRemoteModuleConfig(swcConfig: SwcConfig, applicationN ...plugins, new webpack.container.ModuleFederationPlugin(moduleFederationPluginOptions) ], + transformers: [ + useNamedChunkIdsTransformer, + ...transformers + ], ...webpackOptions }); } diff --git a/packages/webpack-module-federation/src/registerRemoteModules.ts b/packages/webpack-module-federation/src/registerRemoteModules.ts index 0c41745d0..2a780420d 100644 --- a/packages/webpack-module-federation/src/registerRemoteModules.ts +++ b/packages/webpack-module-federation/src/registerRemoteModules.ts @@ -57,7 +57,7 @@ export async function registerRemoteModules(remotes: RemoteDefinition[], runtime runtime.logger.information(`[squide] [remote] ${index + 1}/${remotes.length} Registering module "${RemoteModuleName}" from container "${containerName}" of remote "${remoteUrl}".`); - registerModule(module.register, runtime, context); + await registerModule(module.register, runtime, context); runtime.logger.information(`[squide] [remote] ${index + 1}/${remotes.length} Container "${containerName}" of remote "${remoteUrl}" registration completed.`); } catch (error: unknown) { diff --git a/samples/basic/host/package.json b/samples/basic/host/package.json index f8ef43f6a..6c2544f78 100644 --- a/samples/basic/host/package.json +++ b/samples/basic/host/package.json @@ -11,6 +11,7 @@ "build": "pnpm build:webpack && pnpm build:copy-redirects", "build:webpack": "webpack --config webpack.build.js", "build:copy-redirects": "copyfiles _redirects dist", + "build:copy-public-files": "copyfiles -u 1 public/favicon.png dist", "serve-build": "pnpm build && pnpm http-server dist -p 8080 -P http://localhost:8080? -c-1" }, "devDependencies": { diff --git a/samples/endpoints/host/mocks/browser.ts b/samples/endpoints/host/mocks/browser.ts index 7801e361d..01d2b0321 100644 --- a/samples/endpoints/host/mocks/browser.ts +++ b/samples/endpoints/host/mocks/browser.ts @@ -1,7 +1,7 @@ -import { setupWorker, type RestHandler, type StartOptions } from "msw"; +import { setupWorker, type RestHandler } from "msw"; -export function startMsw(moduleRequestHandlers: RestHandler[], options?: StartOptions) { +export function startMsw(moduleRequestHandlers: RestHandler[]) { const worker = setupWorker(...moduleRequestHandlers); - worker.start(options); + worker.start(); } diff --git a/samples/endpoints/host/mocks/handlers.ts b/samples/endpoints/host/mocks/handlers.ts new file mode 100644 index 000000000..9d1c5e9ee --- /dev/null +++ b/samples/endpoints/host/mocks/handlers.ts @@ -0,0 +1,4 @@ +import type { RestHandler } from "msw"; +import { characterHandlers } from "./characterHandlers.ts"; + +export const requestHandlers: RestHandler[] = characterHandlers; diff --git a/samples/endpoints/host/package.json b/samples/endpoints/host/package.json index 8f98851f2..2fcf2a83a 100644 --- a/samples/endpoints/host/package.json +++ b/samples/endpoints/host/package.json @@ -8,9 +8,10 @@ "type": "module", "scripts": { "dev": "cross-env USE_MSW=true nodemon", - "build": "cross-env USE_MSW=true pnpm build:webpack && pnpm build:copy-redirects", + "build": "cross-env USE_MSW=true pnpm build:webpack && pnpm build:copy-redirects && pnpm build:copy-public-files", "build:webpack": "webpack --config webpack.build.js", "build:copy-redirects": "copyfiles _redirects dist", + "build:copy-public-files": "copyfiles -u 1 public/favicon.png public/mockServiceWorker.js dist", "serve-build": "pnpm build && pnpm http-server dist -p 8080 -P http://localhost:8080? -c-1" }, "devDependencies": { diff --git a/samples/endpoints/host/src/bootstrap.tsx b/samples/endpoints/host/src/bootstrap.tsx index 1597e4f5d..fd34d0906 100644 --- a/samples/endpoints/host/src/bootstrap.tsx +++ b/samples/endpoints/host/src/bootstrap.tsx @@ -31,12 +31,13 @@ registerLocalModules([registerShell(sessionManager), registerHost, registerLocal registerRemoteModules(Remotes, runtime).then(() => { if (process.env.USE_MSW) { + // Files including an import to the "msw" package are included dynamically to prevent adding + // MSW stuff to the bundled when it's not used. import("../mocks/browser.ts").then(({ startMsw }) => { // Will start MSW with the request handlers provided by every module. startMsw(mswPlugin.requestHandlers); - // Indicate to resources that are dependent on MSW - // that the service has been started. + // Indicate to resources that are dependent on MSW that the service has been started. mswPlugin.setAsStarted(); }); } diff --git a/samples/endpoints/host/src/register.tsx b/samples/endpoints/host/src/register.tsx index 0656c5f08..b45901d30 100644 --- a/samples/endpoints/host/src/register.tsx +++ b/samples/endpoints/host/src/register.tsx @@ -2,7 +2,6 @@ import { getMswPlugin } from "@squide/msw"; import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import type { ReactNode } from "react"; -import { characterHandlers } from "../mocks/characterHandlers.ts"; const queryClient = new QueryClient({ defaultOptions: { @@ -42,13 +41,20 @@ function registerRoutes(runtime: Runtime) { }); } -function registerMsw(runtime: Runtime) { - const mswPlugin = getMswPlugin(runtime); +async function registerMsw(runtime: Runtime) { + if (process.env.USE_MSW) { + const mswPlugin = getMswPlugin(runtime); - mswPlugin.registerRequestHandlers(characterHandlers); + // Files including an import to the "msw" package are included dynamically to prevent adding + // MSW stuff to the bundled when it's not used. + const requestHandlers = (await import("../mocks/handlers.ts")).requestHandlers; + + mswPlugin.registerRequestHandlers(requestHandlers); + } } export const registerHost: ModuleRegisterFunction = runtime => { registerRoutes(runtime); - registerMsw(runtime); + + return registerMsw(runtime); }; diff --git a/samples/endpoints/host/src/session.ts b/samples/endpoints/host/src/session.ts index f03b206d1..1836d503c 100644 --- a/samples/endpoints/host/src/session.ts +++ b/samples/endpoints/host/src/session.ts @@ -10,12 +10,6 @@ export class InMemorySessionManager implements SessionManager { getSession() { return this.#session; - - // return { - // user: { - // name: "John Doe" - // } - // }; } clearSession() { diff --git a/samples/endpoints/local-module/mocks/browser.ts b/samples/endpoints/local-module/mocks/browser.ts index 7d722244b..337545e34 100644 --- a/samples/endpoints/local-module/mocks/browser.ts +++ b/samples/endpoints/local-module/mocks/browser.ts @@ -1,7 +1,7 @@ -import { setupWorker, type RestHandler, type StartOptions } from "msw"; +import { setupWorker, type RestHandler } from "msw"; -export function startMsw(requestHandlers: RestHandler[], options?: StartOptions) { +export function startMsw(requestHandlers: RestHandler[]) { const worker = setupWorker(...requestHandlers); - worker.start(options); + worker.start(); } diff --git a/samples/endpoints/local-module/mocks/handlers.ts b/samples/endpoints/local-module/mocks/handlers.ts new file mode 100644 index 000000000..9d1c5e9ee --- /dev/null +++ b/samples/endpoints/local-module/mocks/handlers.ts @@ -0,0 +1,4 @@ +import type { RestHandler } from "msw"; +import { characterHandlers } from "./characterHandlers.ts"; + +export const requestHandlers: RestHandler[] = characterHandlers; diff --git a/samples/endpoints/local-module/package.json b/samples/endpoints/local-module/package.json index 5ba4ff37e..a1f1fa511 100644 --- a/samples/endpoints/local-module/package.json +++ b/samples/endpoints/local-module/package.json @@ -14,9 +14,9 @@ } }, "scripts": { - "dev": "nodemon", + "dev": "cross-env USE_MSW=true nodemon", "dev-local": "cross-env USE_MSW=true LOCAL=true nodemon --config nodemon.local.json", - "build": "tsup --config ./tsup.build.ts", + "build": "cross-env USE_MSW=true tsup --config ./tsup.build.ts", "serve-build": "pnpm build" }, "peerDependencies": { diff --git a/samples/endpoints/local-module/src/CharactersTab.tsx b/samples/endpoints/local-module/src/CharactersTab.tsx index 4f2310df6..95a21edfd 100644 --- a/samples/endpoints/local-module/src/CharactersTab.tsx +++ b/samples/endpoints/local-module/src/CharactersTab.tsx @@ -18,7 +18,7 @@ export function CharactersTab() { return (
    -

    Fetching from the @endpoints/local-module

    +

    Fetching from @endpoints/local-module

    {characters.map((x: Character) => { return ( diff --git a/samples/endpoints/local-module/src/dev/index.tsx b/samples/endpoints/local-module/src/dev/index.tsx index 577c7986c..5a97ca333 100644 --- a/samples/endpoints/local-module/src/dev/index.tsx +++ b/samples/endpoints/local-module/src/dev/index.tsx @@ -23,6 +23,8 @@ registerLocalModules([registerShell(sessionManager), registerDev, registerLocalM // Register MSW after the local modules has been registered since the request handlers // will be registered by the modules. if (process.env.USE_MSW) { + // Files including an import to the "msw" package are included dynamically to prevent adding + // MSW stuff to the bundled when it's not used. import("../../mocks/browser.ts").then(({ startMsw }) => { startMsw(mswPlugin.requestHandlers); diff --git a/samples/endpoints/local-module/src/register.tsx b/samples/endpoints/local-module/src/register.tsx index d0c04d014..d6b4e75c2 100644 --- a/samples/endpoints/local-module/src/register.tsx +++ b/samples/endpoints/local-module/src/register.tsx @@ -3,7 +3,6 @@ import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import type { ReactNode } from "react"; -import { characterHandlers } from "../mocks/characterHandlers.ts"; const queryClient = new QueryClient({ defaultOptions: { @@ -82,13 +81,20 @@ function registerRoutes(runtime: Runtime) { }); } -function registerMsw(runtime: Runtime) { - const mswPlugin = getMswPlugin(runtime); +async function registerMsw(runtime: Runtime) { + if (process.env.USE_MSW) { + const mswPlugin = getMswPlugin(runtime); - mswPlugin.registerRequestHandlers(characterHandlers); + // Files including an import to the "msw" package are included dynamically to prevent adding + // MSW stuff to the bundled when it's not used. + const requestHandlers = (await import("../mocks/handlers.ts")).requestHandlers; + + mswPlugin.registerRequestHandlers(requestHandlers); + } } export const registerLocalModule: ModuleRegisterFunction = runtime => { registerRoutes(runtime); - registerMsw(runtime); + + return registerMsw(runtime); }; diff --git a/samples/endpoints/remote-module/mocks/browser.ts b/samples/endpoints/remote-module/mocks/browser.ts index 7d722244b..337545e34 100644 --- a/samples/endpoints/remote-module/mocks/browser.ts +++ b/samples/endpoints/remote-module/mocks/browser.ts @@ -1,7 +1,7 @@ -import { setupWorker, type RestHandler, type StartOptions } from "msw"; +import { setupWorker, type RestHandler } from "msw"; -export function startMsw(requestHandlers: RestHandler[], options?: StartOptions) { +export function startMsw(requestHandlers: RestHandler[]) { const worker = setupWorker(...requestHandlers); - worker.start(options); + worker.start(); } diff --git a/samples/endpoints/remote-module/mocks/handlers.ts b/samples/endpoints/remote-module/mocks/handlers.ts new file mode 100644 index 000000000..4cfecd283 --- /dev/null +++ b/samples/endpoints/remote-module/mocks/handlers.ts @@ -0,0 +1,6 @@ +import type { RestHandler } from "msw"; +import { episodeHandlers } from "./episodeHandlers.ts"; +import { locationHandlers } from "./locationHandlers.ts"; + +export const requestHandlers: RestHandler[] = [...episodeHandlers, ...locationHandlers]; + diff --git a/samples/endpoints/remote-module/package.json b/samples/endpoints/remote-module/package.json index f9c3b6770..8a03cee30 100644 --- a/samples/endpoints/remote-module/package.json +++ b/samples/endpoints/remote-module/package.json @@ -7,7 +7,7 @@ "license": "Apache-2.0", "type": "module", "scripts": { - "dev": "nodemon", + "dev": "cross-env USE_MSW=true nodemon", "dev-local": "cross-env USE_MSW=true LOCAL=true pnpm dev", "build": "cross-env USE_MSW=true webpack --config webpack.build.js", "serve-build": "pnpm build && pnpm http-server dist -p 8081 -P http://localhost:8081? -c-1" diff --git a/samples/endpoints/remote-module/src/dev/index.tsx b/samples/endpoints/remote-module/src/dev/index.tsx index 82db29031..fa016713c 100644 --- a/samples/endpoints/remote-module/src/dev/index.tsx +++ b/samples/endpoints/remote-module/src/dev/index.tsx @@ -25,6 +25,8 @@ registerLocalModules([registerShell(sessionManager), registerDev, registerModule // Register MSW after the local modules has been registered since the request handlers // will be registered by the modules. if (process.env.USE_MSW) { + // Files including an import to the "msw" package are included dynamically to prevent adding + // MSW stuff to the bundled when it's not used. import("../../mocks/browser.ts").then(({ startMsw }) => { startMsw(mswPlugin.requestHandlers); diff --git a/samples/endpoints/remote-module/src/register.tsx b/samples/endpoints/remote-module/src/register.tsx index 7bb82b58e..cb1457e78 100644 --- a/samples/endpoints/remote-module/src/register.tsx +++ b/samples/endpoints/remote-module/src/register.tsx @@ -1,7 +1,5 @@ import { getMswPlugin } from "@squide/msw"; import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; -import { episodeHandlers } from "../mocks/episodeHandlers.ts"; -import { locationHandlers } from "../mocks/locationHandlers.ts"; import { Providers } from "./Providers.tsx"; function registerRoutes(runtime: Runtime) { @@ -66,16 +64,20 @@ function registerRoutes(runtime: Runtime) { }); } -function registerMsw(runtime: Runtime) { - const mswPlugin = getMswPlugin(runtime); +async function registerMsw(runtime: Runtime) { + if (process.env.USE_MSW) { + const mswPlugin = getMswPlugin(runtime); - mswPlugin.registerRequestHandlers([ - ...episodeHandlers, - ...locationHandlers - ]); + // Files including an import to "msw" package are included dynamically to prevent adding + // MSW stuff to the bundled when it's not used. + const requestHandlers = (await import("../mocks/handlers.ts")).requestHandlers; + + mswPlugin.registerRequestHandlers(requestHandlers); + } } -export const register: ModuleRegisterFunction = runtime => { +export const register: ModuleRegisterFunction = async runtime => { registerRoutes(runtime); - registerMsw(runtime); + + return registerMsw(runtime); }; diff --git a/samples/endpoints/shell/src/AuthenticationBoundary.tsx b/samples/endpoints/shell/src/AuthenticationBoundary.tsx index 658c75aa0..aec77b69a 100644 --- a/samples/endpoints/shell/src/AuthenticationBoundary.tsx +++ b/samples/endpoints/shell/src/AuthenticationBoundary.tsx @@ -3,8 +3,9 @@ import { Navigate, Outlet } from "react-router-dom"; export function AuthenticationBoundary() { const logger = useLogger(); + const isAuthenticated = useIsAuthenticated(); - if (useIsAuthenticated()) { + if (isAuthenticated) { return ; } diff --git a/samples/endpoints/shell/src/useAppRouter.tsx b/samples/endpoints/shell/src/useAppRouter.tsx index 039d7b54e..67e6b6565 100644 --- a/samples/endpoints/shell/src/useAppRouter.tsx +++ b/samples/endpoints/shell/src/useAppRouter.tsx @@ -23,7 +23,7 @@ export function useAppRouter(waitForMsw: boolean, sessionManager: SessionManager const [isReady, setIsReady] = useState(false); // Could be done with a ref (https://react.dev/reference/react/useRef) to save a re-render but for this sample - // it seemed unnecessary. If your app have multiple global data structures like this one thought, it should be considered. + // it seemed unnecessary. If your application loads a lot of data at bootstrapping, it should be considered. const [subscription, setSubscription] = useState(); const logger = useLogger(); From 22b7530ad4658ecdda048b799ab9a6799a028c81 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Mon, 16 Oct 2023 10:40:35 -0400 Subject: [PATCH 26/51] Renamed "dev-local" to "dev-isolated" --- docs/guides/develop-a-module-in-isolation.md | 20 +++++++++---------- .../basic/another-remote-module/package.json | 2 +- .../another-remote-module/webpack.dev.js | 2 +- ...demon.local.json => nodemon.isolated.json} | 0 samples/basic/local-module/package.json | 2 +- samples/basic/remote-module/package.json | 2 +- samples/basic/remote-module/webpack.dev.js | 2 +- ...demon.local.json => nodemon.isolated.json} | 0 samples/endpoints/local-module/package.json | 2 +- .../endpoints/local-module/src/register.tsx | 2 +- .../endpoints/local-module/webpack.config.js | 2 +- samples/endpoints/remote-module/package.json | 2 +- .../endpoints/remote-module/src/Providers.tsx | 2 +- .../endpoints/remote-module/webpack.dev.js | 6 +++--- 14 files changed, 23 insertions(+), 23 deletions(-) rename samples/basic/local-module/{nodemon.local.json => nodemon.isolated.json} (100%) rename samples/endpoints/local-module/{nodemon.local.json => nodemon.isolated.json} (100%) diff --git a/docs/guides/develop-a-module-in-isolation.md b/docs/guides/develop-a-module-in-isolation.md index 7f5992d3b..f0c5463f7 100644 --- a/docs/guides/develop-a-module-in-isolation.md +++ b/docs/guides/develop-a-module-in-isolation.md @@ -164,7 +164,7 @@ remote-module ### index.tsx -The `index.tsx` file is similar to the `bootstrap.tsx` file of an host application but, tailored for an isolated module. The key distinction is that, since the project is set up for local development, the module is registered with the [registerLocalModules](/reference/registration/registerLocalModules.md) function instead of the [registerRemoteModules](/reference/registration/registerRemoteModules.md) function: +The `index.tsx` file is similar to the `bootstrap.tsx` file of an host application but, tailored for an isolated module. The key distinction is that, since the project is set up for isolated development, the module is registered with the [registerLocalModules](/reference/registration/registerLocalModules.md) function instead of the [registerRemoteModules](/reference/registration/registerRemoteModules.md) function: ```tsx !#8-10,14 remote-module/src/index.tsx import { createRoot } from "react-dom/client"; @@ -233,16 +233,16 @@ function DevHome() { ### Add a new CLI script -Next, add a new `dev-local` script to the `package.json` file to start the local development server in **"isolation"**: +Next, add a new `dev-isolated` script to the `package.json` file to start the local development server in **"isolation"**: ```json !#3 remote-module/package.json { "dev": "webpack serve --config webpack.dev.js", - "dev-local": "cross-env LOCAL=true webpack serve --config webpack.dev.js", + "dev-isolated": "cross-env ISOLATED=true webpack serve --config webpack.dev.js", } ``` -The `dev-local` script is similar to the `dev` script but introduces a `LOCAL` environment variable. This new environment variable will be utilized by the `webpack.dev.js` file to conditionally setup the development server for **local** development in **isolation** or to be consumed by a host application through the `/remoteEntry.js` entry point: +The `dev-isolated` script is similar to the `dev` script but introduces a `ISOLATED` environment variable. This new environment variable will be utilized by the `webpack.dev.js` file to conditionally setup the development server for development in **isolation** or to be consumed by a host application through the `/remoteEntry.js` entry point: ### Configure webpack @@ -271,7 +271,7 @@ extends @workleap/browserslist-config #### `defineDevConfig` -To configure webpack, open the `webpack.dev.js` file and update the configuration to incorporate the `LOCAL` environment variable and the [defineDevConfig](https://gsoft-inc.github.io/wl-web-configs/webpack/configure-dev/) function: +To configure webpack, open the `webpack.dev.js` file and update the configuration to incorporate the `ISOLATED` environment variable and the [defineDevConfig](https://gsoft-inc.github.io/wl-web-configs/webpack/configure-dev/) function: ```js !#9,12 remote-module/webpack.dev.js // @ts-check @@ -282,7 +282,7 @@ import { swcConfig } from "./swc.dev.js"; let config; -if (!process.env.LOCAL) { +if (!process.env.ISOLATED) { config = defineDevRemoteModuleConfig(swcConfig, "remote1", 8081); } else { config = defineDevConfig(swcConfig); @@ -293,7 +293,7 @@ export default config; ### Try it :rocket: -Start the remote module in isolation by running the `dev-local` script. The application shell should wrap the pages of the module and the default page should be `DevHome`. +Start the remote module in isolation by running the `dev-isolated` script. The application shell should wrap the pages of the module and the default page should be `DevHome`. ## Setup a local module @@ -399,17 +399,17 @@ export default defineDevConfig(swcConfig); ### Add a new CLI script -Next, add a new `dev-local` script to the `package.json` file to start the local development server: +Next, add a new `dev-isolated` script to the `package.json` file to start the local development server: ```json local-module/package.json { - "dev-local": "webpack serve --config webpack.config.js" + "dev-isolated": "webpack serve --config webpack.config.js" } ``` ### Try it :rocket: -Start the remote module in isolation by running the `dev-local` script. The application shell should wrap the pages of the module and the default page should be `DevHome`. +Start the remote module in isolation by running the `dev-isolated` script. The application shell should wrap the pages of the module and the default page should be `DevHome`. !!!info If you are having issues with this guide, have a look at a working example on [GitHub](https://github.com/gsoft-inc/wl-squide/tree/main/sample). diff --git a/samples/basic/another-remote-module/package.json b/samples/basic/another-remote-module/package.json index 2c3917992..06e3bcbf7 100644 --- a/samples/basic/another-remote-module/package.json +++ b/samples/basic/another-remote-module/package.json @@ -8,7 +8,7 @@ "type": "module", "scripts": { "dev": "nodemon", - "dev-local": "cross-env LOCAL=true pnpm dev", + "dev-isolated": "cross-env ISOLATED=true pnpm dev", "build": "webpack --config webpack.build.js", "serve-build": "pnpm build && pnpm http-server dist -p 8082 -P http://localhost:8082? -c-1" }, diff --git a/samples/basic/another-remote-module/webpack.dev.js b/samples/basic/another-remote-module/webpack.dev.js index f5d7621a8..5ba9bfc0b 100644 --- a/samples/basic/another-remote-module/webpack.dev.js +++ b/samples/basic/another-remote-module/webpack.dev.js @@ -7,7 +7,7 @@ import { swcConfig } from "./swc.dev.js"; let config; -if (!process.env.LOCAL) { +if (!process.env.ISOLATED) { config = defineDevRemoteModuleConfig(swcConfig, "remote2", 8082, { sharedDependencies: { "@basic/shared": { diff --git a/samples/basic/local-module/nodemon.local.json b/samples/basic/local-module/nodemon.isolated.json similarity index 100% rename from samples/basic/local-module/nodemon.local.json rename to samples/basic/local-module/nodemon.isolated.json diff --git a/samples/basic/local-module/package.json b/samples/basic/local-module/package.json index 001f54372..c3ec359f3 100644 --- a/samples/basic/local-module/package.json +++ b/samples/basic/local-module/package.json @@ -15,7 +15,7 @@ }, "scripts": { "dev": "nodemon", - "dev-local": "nodemon --config nodemon.local.json", + "dev-isolated": "nodemon --config nodemon.isolated.json", "build": "tsup --config ./tsup.build.ts", "serve-build": "pnpm build" }, diff --git a/samples/basic/remote-module/package.json b/samples/basic/remote-module/package.json index d1ca21f0d..52d7b5f37 100644 --- a/samples/basic/remote-module/package.json +++ b/samples/basic/remote-module/package.json @@ -8,7 +8,7 @@ "type": "module", "scripts": { "dev": "nodemon", - "dev-local": "cross-env LOCAL=true nodemon", + "dev-isolated": "cross-env ISOLATED=true nodemon", "build": "webpack --config webpack.build.js", "serve-build": "pnpm build && pnpm http-server dist -p 8081 -P http://localhost:8081? -c-1" }, diff --git a/samples/basic/remote-module/webpack.dev.js b/samples/basic/remote-module/webpack.dev.js index 93c68bae2..830bcc7b2 100644 --- a/samples/basic/remote-module/webpack.dev.js +++ b/samples/basic/remote-module/webpack.dev.js @@ -7,7 +7,7 @@ import { swcConfig } from "./swc.dev.js"; let config; -if (!process.env.LOCAL) { +if (!process.env.ISOLATED) { config = defineDevRemoteModuleConfig(swcConfig, "remote1", 8081, { sharedDependencies: { "@basic/shared": { diff --git a/samples/endpoints/local-module/nodemon.local.json b/samples/endpoints/local-module/nodemon.isolated.json similarity index 100% rename from samples/endpoints/local-module/nodemon.local.json rename to samples/endpoints/local-module/nodemon.isolated.json diff --git a/samples/endpoints/local-module/package.json b/samples/endpoints/local-module/package.json index a1f1fa511..2f331383b 100644 --- a/samples/endpoints/local-module/package.json +++ b/samples/endpoints/local-module/package.json @@ -15,7 +15,7 @@ }, "scripts": { "dev": "cross-env USE_MSW=true nodemon", - "dev-local": "cross-env USE_MSW=true LOCAL=true nodemon --config nodemon.local.json", + "dev-isolated": "cross-env USE_MSW=true ISOLATED=true nodemon --config nodemon.isolated.json", "build": "cross-env USE_MSW=true tsup --config ./tsup.build.ts", "serve-build": "pnpm build" }, diff --git a/samples/endpoints/local-module/src/register.tsx b/samples/endpoints/local-module/src/register.tsx index d6b4e75c2..a4b6a4b3e 100644 --- a/samples/endpoints/local-module/src/register.tsx +++ b/samples/endpoints/local-module/src/register.tsx @@ -19,7 +19,7 @@ function Providers({ children }: { children: ReactNode }) { return ( {children} - {process.env.LOCAL && ( + {process.env.ISOLATED && ( )} diff --git a/samples/endpoints/local-module/webpack.config.js b/samples/endpoints/local-module/webpack.config.js index 7921ee8b1..f93dd6fec 100644 --- a/samples/endpoints/local-module/webpack.config.js +++ b/samples/endpoints/local-module/webpack.config.js @@ -9,7 +9,7 @@ export default defineDevConfig(swcConfig, { entry: path.resolve("./src/dev/index.tsx"), overlay: false, environmentVariables: { - "LOCAL": process.env.LOCAL === "true", + "ISOLATED": process.env.ISOLATED === "true", "USE_MSW": process.env.USE_MSW === "true" } }); diff --git a/samples/endpoints/remote-module/package.json b/samples/endpoints/remote-module/package.json index 8a03cee30..2f102f9f1 100644 --- a/samples/endpoints/remote-module/package.json +++ b/samples/endpoints/remote-module/package.json @@ -8,7 +8,7 @@ "type": "module", "scripts": { "dev": "cross-env USE_MSW=true nodemon", - "dev-local": "cross-env USE_MSW=true LOCAL=true pnpm dev", + "dev-isolated": "cross-env USE_MSW=true ISOLATED=true pnpm dev", "build": "cross-env USE_MSW=true webpack --config webpack.build.js", "serve-build": "pnpm build && pnpm http-server dist -p 8081 -P http://localhost:8081? -c-1" }, diff --git a/samples/endpoints/remote-module/src/Providers.tsx b/samples/endpoints/remote-module/src/Providers.tsx index f2bdbf360..358cc6560 100644 --- a/samples/endpoints/remote-module/src/Providers.tsx +++ b/samples/endpoints/remote-module/src/Providers.tsx @@ -17,7 +17,7 @@ export function Providers({ children }: { children: ReactNode }) { return ( {children} - {process.env.LOCAL && ( + {process.env.ISOLATED && ( )} diff --git a/samples/endpoints/remote-module/webpack.dev.js b/samples/endpoints/remote-module/webpack.dev.js index be01c7a89..8e9a76c0d 100644 --- a/samples/endpoints/remote-module/webpack.dev.js +++ b/samples/endpoints/remote-module/webpack.dev.js @@ -7,7 +7,7 @@ import { swcConfig } from "./swc.dev.js"; let config; -if (!process.env.LOCAL) { +if (!process.env.ISOLATED) { config = defineDevRemoteModuleConfig(swcConfig, "remote1", 8081, { sharedDependencies: { "@endpoints/shared": { @@ -16,7 +16,7 @@ if (!process.env.LOCAL) { }, environmentVariables: { "NETLIFY": process.env.NETLIFY === "true", - "LOCAL": process.env.LOCAL === "true", + "ISOLATED": process.env.ISOLATED === "true", "USE_MSW": process.env.USE_MSW === "true" } }); @@ -26,7 +26,7 @@ if (!process.env.LOCAL) { entry: path.resolve("./src/dev/index.tsx"), overlay: false, environmentVariables: { - "LOCAL": process.env.LOCAL === "true", + "ISOLATED": process.env.ISOLATED === "true", "USE_MSW": process.env.USE_MSW === "true" } }); From 4cd578f9c1d842e2b2576588b3fcaef416603774 Mon Sep 17 00:00:00 2001 From: patricklafrance Date: Mon, 16 Oct 2023 14:57:05 -0400 Subject: [PATCH 27/51] Improved the sample pages --- packages/msw/src/useIsMswReady.ts | 1 - .../another-remote-module/src/register.tsx | 8 ++++- samples/basic/host/src/HomePage.tsx | 9 +++--- samples/basic/local-module/src/AboutPage.tsx | 13 --------- .../basic/local-module/src/MessagePage.tsx | 20 +++++++++---- .../basic/local-module/src/WorkleapTab.tsx | 13 +++++++-- samples/basic/local-module/src/register.tsx | 10 ------- .../basic/remote-module/src/ColoredPage.tsx | 20 +++++++++++-- .../basic/remote-module/src/CustomLayout.tsx | 2 ++ .../basic/remote-module/src/HoistedPage.tsx | 14 +++++++-- .../basic/remote-module/src/OfficevibeTab.tsx | 7 +++-- .../basic/remote-module/src/RemotePage.tsx | 13 --------- samples/basic/remote-module/src/SkillsTab.tsx | 13 +++++++-- samples/basic/remote-module/src/register.tsx | 15 +++------- .../basic/shared/src/FederatedTabsLayout.tsx | 22 +++++++++++--- samples/endpoints/host/src/HomePage.tsx | 2 +- samples/endpoints/host/src/bootstrap.tsx | 2 +- .../local-module/src/CharactersTab.tsx | 3 +- .../local-module/src/SubscriptionPage.tsx | 7 +++-- .../endpoints/local-module/src/register.tsx | 2 +- .../remote-module/src/EpisodesTab.tsx | 3 +- .../remote-module/src/FailingTab.tsx | 5 +++- .../remote-module/src/LocationsTab.tsx | 3 +- .../shared/src/FederatedTabsLayout.tsx | 14 +++++---- .../shell/src/AuthenticatedLayout.tsx | 2 ++ samples/endpoints/shell/src/LoginPage.tsx | 11 +++++-- samples/endpoints/shell/src/LogoutPage.tsx | 11 +++++-- samples/endpoints/shell/src/NoMatchPage.tsx | 10 +++++-- samples/endpoints/shell/src/register.tsx | 29 +++++++++++++++---- 29 files changed, 177 insertions(+), 107 deletions(-) delete mode 100644 samples/basic/local-module/src/AboutPage.tsx delete mode 100644 samples/basic/remote-module/src/RemotePage.tsx diff --git a/packages/msw/src/useIsMswReady.ts b/packages/msw/src/useIsMswReady.ts index 309189b9b..b5e79c11a 100644 --- a/packages/msw/src/useIsMswReady.ts +++ b/packages/msw/src/useIsMswReady.ts @@ -23,7 +23,6 @@ export function useIsMswStarted(enabled: boolean, { interval = 10 }: UseIsMswSta if (mswPlugin.isStarted) { logger.debug("[squide] %cMSW is ready%c.", "color: white; background-color: green;", ""); - // Must clear interval before calling "_completeRegistration" in case there's an error. clearInterval(intervalId); setIsStarted(true); } diff --git a/samples/basic/another-remote-module/src/register.tsx b/samples/basic/another-remote-module/src/register.tsx index 039b15e7f..3cef24975 100644 --- a/samples/basic/another-remote-module/src/register.tsx +++ b/samples/basic/another-remote-module/src/register.tsx @@ -3,7 +3,13 @@ import type { ModuleRegisterFunction, Runtime } from "@squide/react-router"; function registerRoutes(runtime: Runtime) { runtime.registerRoute({ path: "/federated-tabs", - lazy: () => import("@basic/shared/FederatedTabsLayout.tsx") + lazy: async () => { + const { FederatedTabsLayout } = await import("@basic/shared/FederatedTabsLayout.tsx"); + + return { + element: + }; + } }); runtime.registerNavigationItem({ diff --git a/samples/basic/host/src/HomePage.tsx b/samples/basic/host/src/HomePage.tsx index cb6e827dc..67e69ac92 100644 --- a/samples/basic/host/src/HomePage.tsx +++ b/samples/basic/host/src/HomePage.tsx @@ -1,9 +1,10 @@ export function HomePage() { return ( -
    -

    Home Page

    -

    Hey!

    -
    + <> +

    Home Page

    +

    This page is served by @basic/host

    +

    Hey! Welcome to this basic sample showcasing a few features of Squide.

    + ); } diff --git a/samples/basic/local-module/src/AboutPage.tsx b/samples/basic/local-module/src/AboutPage.tsx deleted file mode 100644 index 46998bace..000000000 --- a/samples/basic/local-module/src/AboutPage.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { Link } from "react-router-dom"; - -export function AboutPage() { - return ( -
    -

    About

    -

    Hey again!

    - Go back to home -
    - ); -} - -export const Component = AboutPage; diff --git a/samples/basic/local-module/src/MessagePage.tsx b/samples/basic/local-module/src/MessagePage.tsx index 81d53dc8a..5a982f451 100644 --- a/samples/basic/local-module/src/MessagePage.tsx +++ b/samples/basic/local-module/src/MessagePage.tsx @@ -17,15 +17,25 @@ export function MessagePage() { }, [dispatch, setMessage, message]); return ( -
    -

    Messaging

    -

    Send a message to the host application:

    + <> +

    Messaging

    +

    This page is served by @basic/local-module

    +

    +

    There are a few distinctive features that are showcased with this pages:

    +
      +
    • The navigation item for this page has a priority of 999, which renders it as the first item of the navbar.
    • +
    • The navigation item for this page use custom additional props to highlight the item in the navbar.
    • +
    • The "Send a message" feature showcase how Squide's event bus works.
    • +
    +

    +

    Send a message

    +

    Send the following to the host application: