diff --git a/.changeset/component-and-error-boundary.md b/.changeset/component-and-error-boundary.md new file mode 100644 index 0000000000..d6a94f683e --- /dev/null +++ b/.changeset/component-and-error-boundary.md @@ -0,0 +1,42 @@ +--- +"react-router": minor +"react-router-dom": minor +--- + +React Router now supports an alternative way to define your route `element` and `errorElement` fields as React Components instead of React Elements. You can instead pass a React Component to the new `Component` and `ErrorBoundary` fields if you choose. There is no functional difference between the two, so use whichever approach you prefer 😀. You shouldn't be defining both, but if you do `Component`/`ErrorBoundary` will "win". + +**Example JSON Syntax** + +```jsx +// Both of these work the same: +const elementRoutes = [{ + path: '/', + element: , + errorElement: , +}] + +const componentRoutes = [{ + path: '/', + Component: Home, + ErrorBoundary: HomeError, +}] + +function Home() { ... } +function HomeError() { ... } +``` + +**Example JSX Syntax** + +```jsx +// Both of these work the same: +const elementRoutes = createRoutesFromElements( + } errorElement={ } /> +); + +const elementRoutes = createRoutesFromElements( + +); + +function Home() { ... } +function HomeError() { ... } +``` diff --git a/.changeset/lazy-route-modules.md b/.changeset/lazy-route-modules.md new file mode 100644 index 0000000000..67deeafaa8 --- /dev/null +++ b/.changeset/lazy-route-modules.md @@ -0,0 +1,63 @@ +--- +"react-router": minor +"react-router-dom": minor +"@remix-run/router": minor +--- + +**Introducing Lazy Route Modules!** + +In order to keep your application bundles small and support code-splitting of your routes, we've introduced a new `lazy()` route property. This is an async function that resolves the non-route-matching portions of your route definition (`loader`, `action`, `element`/`Component`, `errorElement`/`ErrorBoundary`, `shouldRevalidate`, `handle`). + +Lazy routes are resolved on initial load and during the `loading` or `submitting` phase of a navigation or fetcher call. You cannot lazily define route-matching properties (`path`, `index`, `children`) since we only execute your lazy route functions after we've matched known routes. + +Your `lazy` functions will typically return the result of a dynamic import. + +```jsx +// In this example, we assume most folks land on the homepage so we include that +// in our critical-path bundle, but then we lazily load modules for /a and /b so +// they don't load until the user navigates to those routes +let routes = createRoutesFromElements( + }> + } /> + import("./a")} /> + import("./b")} /> + +); +``` + +Then in your lazy route modules, export the properties you want defined for the route: + +```jsx +export async function loader({ request }) { + let data = await fetchData(request); + return json(data); +} + +// Export a `Component` directly instead of needing to create a React Element from it +export function Component() { + let data = useLoaderData(); + + return ( + <> +

You made it!

+

{data}

+ + ); +} + +// Export an `ErrorBoundary` directly instead of needing to create a React Element from it +export function ErrorBoundary() { + let error = useRouteError(); + return isRouteErrorResponse(error) ? ( +

+ {error.status} {error.statusText} +

+ ) : ( +

{error.message || error}

+ ); +} +``` + +An example of this in action can be found in the [`examples/lazy-loading-router-provider`](https://github.com/remix-run/react-router/tree/main/examples/lazy-loading-router-provider) directory of the repository. + +🙌 Huge thanks to @rossipedia for the [Initial Proposal](https://github.com/remix-run/react-router/discussions/9826) and [POC Implementation](https://github.com/remix-run/react-router/pull/9830). diff --git a/contributors.yml b/contributors.yml index 9ac55415e0..3747ddbf93 100644 --- a/contributors.yml +++ b/contributors.yml @@ -118,6 +118,7 @@ - Manc - manzano78 - marc2332 +- markdalgleish - markivancho - maruffahmed - marvinruder diff --git a/decisions/0002-lazy-route-modules.md b/decisions/0002-lazy-route-modules.md new file mode 100644 index 0000000000..2978903c82 --- /dev/null +++ b/decisions/0002-lazy-route-modules.md @@ -0,0 +1,248 @@ +# Lazy Route Modules + +Date: 2023-02-21 + +Status: accepted + +## Context + +In a data-aware React Router application (``), the router needs to be aware of the route tree ahead of time so it can match routes and execute loaders/actions _prior_ to rendering the destination route. This is different than in non-data-aware React Router applications (``) where you could nest `` sub-tree anywhere in your application, and compose together `` and `React.lazy()` to dynamically load "new" portions of your routing tree as the user navigated through the application. The downside of this approach in `BrowserRouter` is that it's a render-then-fetch cycle which produces network waterfalls and nested spinners, two things that we're aiming to eliminate in `RouterProvider` applications. + +There were ways to [manually code-split][manually-code-split] in a `RouterProvider` application but they can be a bit verbose and tedious to do manually. As a result of this DX, we received a [Remix Route Modules Proposal][proposal] from the community along with a [POC implementation][poc] (thanks `@rossipedia` 🙌). + +## Original POC + +The original POC idea was to implement this in user-land where `element`/`errorElement` would be transformed into `React.lazy()` calls and `loader`/`action` would load the module and then execute the `loader`/`action`: + +```js +// Assuming route.module is a function returning a Remix-style route module +let Component = React.lazy(route.module); +route.element = ; +route.loader = async (args) => { + const { loader } = await route.module(); + return typeof loader === "function" ? loader(args) : null; +}; +``` + +This approach got us pretty far but suffered from some limitations being done in user-land since it did not have access to some router internals to make for a more seamless integration. Namely, it _had_ to put every possible property onto a route since it couldn't know ahead of time whether the route module would resolve with the matching property. For example, will `import('./route')` return an `errorElement`? Who knows! + +To combat this, a `route.use` property was considered which would allow the user to define the exports of the module: + +```js +const route = { + path: "/", + module: () => import("./route"), + use: ["loader", "element"], +}; +``` + +This wasn't ideal since it introduced a tight coupling of the file contents and the route definitions. + +Furthermore, since the goal of `RouterProvider` is to reduce spinners, it felt incorrect to automatically introduce `React.lazy` and thus expect Suspense boundaries for elements that we expected to be fully fetched _prior_ to rendering the destination route. + +## Decision + +Given what we learned from the original POC, we felt we could do this a bit leaner with an implementation inside the router. Data router apps already have an asynchronous pre-render flow where we could hook in and run this logic. A few advantages of doing this inside of the router include: + +- We can load at a more specific spot internal to the router +- We can access the navigation `AbortSignal` in case the `lazy()` call gets interrupted +- We can also load once and update the internal route definition so subsequent navigations don't have a repeated `lazy()` call +- We don't have issue with knowing whether or not an `errorElement` exists since we will have updated the route prior to updating any UI state + +This proved to work out quite well as we did our own POC so we went with this approach in the end. Now, any time we enter a `submitting`/`loading` state we first check for a `route.lazy` definition and resolve that promise first and update the internal route definition with the result. + +The resulting API looks like this, assuming you want to load your homepage in the main bundle, but lazily load the code for the `/about` route. Note we're using the new `Component` API introduced along with this work. + +```jsx +// app.jsx +const router = createBrowserRouter([ + { + path: "/", + Component: Layout, + children: [ + { + index: true, + Component: Home, + }, + { + path: "about", + lazy: () => import("./about"), + }, + ], + }, +]); +``` + +And then your `about.jsx` file would export the properties to be lazily defined on the route: + +```jsx +// about.jsx +export function loader() { ... } + +export function Component() { ... } +``` + +## Choices + +Here's a few choices we made along the way: + +### Immutable Route Properties + +A route has 3 types of fields defined on it: + +- Path matching properties: `path`, `index`, `caseSensitive` and `children` + - While not strictly used for matching, `id` is also considered static since it is needed up-front to uniquely identify all defined routes +- Data loading properties: `loader`, `action`, `hasErrorBoundary`, `shouldRevalidate` +- Rendering properties: `handle` and the framework-aware `element`/`errorElement`/`Component`/`ErrorBoundary` + +The `route.lazy()` method is focused on lazy-loading the data loading and rendering properties, but cannot update the path matching properties because we have to path match _first_ before we can even identify which matched routes include a `lazy()` function. Therefore, we do not allow path matching route keys to be updated by `lazy()`, and will log a warning if you return one of those properties from your lazy() method. + +## Static Route Properties + +Similar to how you cannot override any immutable path-matching properties, you also cannot override any statically defined data-loading or rendering properties (and will log the a console warning if you attempt to). This allows you to statically define aspects that you don't need (or wish) to lazy load. Two potential use-cases her might be: + +1. Using a small statically-defined `loader`/`action` which just hits an API endpoint to load/submit data. + - In fact this is an interesting option we've optimized React Router to detect this and call any statically defined loader/action handlers in parallel with `lazy` (since `lazy` will be unable to update the `loader`/`action` anyway!). This will provide the ability to obtain the most-optimal parallelization of loading your component in parallel with your data fetches. +2. Re-using a common statically-defined `ErrorBoundary` across multiple routes + +### Addition of route `Component` and `ErrorBoundary` fields + +In React Router v6, routes define `element` properties because it allows static prop passing as well as fitting nicely in the JSX render-tree-defined route trees: + +```jsx + + + } /> + + +``` + +However, in a React Router 6.4+ landscape when using `RouterProvider`, routes are defined statically up-front to enable data-loading, so using element feels arguably a bit awkward outside of a JSX tree: + +```js +const routes = [ + { + path: "/", + element: , + }, +]; +``` + +It also means that you cannot easily use hooks inline, and have to add a level of indirection to access hooks. + +This gets a bit more awkward with the introduction of `lazy()` since your file now has to export a root-level JSX element: + +```jsx +// home.jsx +export const element = + +function Homepage() { ... } +``` + +In reality, what we want in this "static route definition" landscape is just the component for the Route: + +```js +const routes = [ + { + path: "/", + Component: Homepage, + }, +]; +``` + +This has a number of advantages in that we can now use inline component functions to access hooks, provide props, etc. And we also simplify the exports of a `lazy()` route module: + +```jsx +const routes = [ + { + path: "/", + // You can include just the component + Component: Homepage, + }, + { + path: "/a", + // Or you can inline your component and pass props + Component: () => , + }, + { + path: "/b", + // And even use use hooks without indirection 💥 + Component: () => { + let data = useLoaderData(); + return ; + }, + }, +]; +``` + +So in the end, the work for `lazy()` introduced support for `route.Component` and `route.ErrorBoundary`, which can be statically or lazily defined. They will take precedence over `element`/`errorElement` if both happen to be defined, but for now both are acceptable ways to define routes. We think we'll be expanding the `Component` API in the future for stronger type-safety since we can pass it inferred-type `loaderData` etc. so in the future that _may_ become the preferred API. + +### Interruptions + +Previously when a link was clicked or a form was submitted, since we had the `action`/`loader` defined statically up-front, they were immediately executed and there was no chance for an interruption _before calling the handler_. Now that we've introduced the concept of `lazy()` there is a period of time prior to executing the handler where the user could interrupt the navigation by clicking to a new location. In order to keep behavior consistent with lazily-loaded routes and statically defined routes, if a `lazy()` function is interrupted React Router _will still call the returned handler_. As always, the user can leverage `request.signal.aborted` inside the handler to short-circuit on interruption if desired. + +This is important because `lazy()` is only ever run once in an application session. Once lazy has completed it updates the route in place, and all subsequent navigations to that route use the now-statically-defined properties. Without this behavior, routes would behave differently on the _first_ navigation versus _subsequent_ navigations which could introduce subtle and hard-to-track-down bugs. + +Additionally, since `lazy()` functions are intended to return a static definition of route `loader`/`element`/etc. - if multiple navigations happen to the same route in parallel, the first `lazy()` call to resolve will "win" and update the route, and the returned values from any other `lazy()` executions will be ignored. This should not be much of an issue in practice though as modern bundlers latch onto the same promise for repeated calls to `import()` so in those cases the first call will still "win". + +### Error Handling + +If an error is thrown by `lazy()` we catch that in the same logic as if the error was thrown by the `action`/`loader` and bubble it to the nearest `errorElement`. + +## Consequences + +Not so much as a consequence, but more of limitation - we still require the routing tree up-front for the most efficient data-loading. This means that we can't _yet_ support quite the same nested `` use-cases as before (particularly with respect to microfrontends), but we have ideas for how to solve that as an extension of this concept in the future. + +Another slightly edge-case concept we discovered is that in DIY SSR applications using `createStaticHandler` and `StaticRouterProvider`, it's possible to server-render a lazy route and send up its hydration data. But then we may _not_ have those routes loaded in our client-side hydration: + +```jsx +const routes = [{ + path: '/', + lazy: () => import("./route"), +}] +let router = createBrowserRouter(routes, { + hydrationData: window.__hydrationData, +}); + +// ⚠️ At this point, the router has the data but not the route definition! + +ReactDOM.hydrateRoot( + document.getElementById("app")!, + +); +``` + +In the above example, we've server-rendered our `/` route and therefore we _don't_ want to render a `fallbackElement` since we already have the SSR'd content, and the router doesn't need to "initialize" because we've provided the data in `hydrationData`. However, if we're hydrating into a route that includes `lazy`, then we _do_ need to initialize that lazy route. + +The real solution for this is to do what Remix does and know your matched routes and preload their modules ahead of time and hydrate with synchronous route definitions. This is a non-trivial process through so it's not expected that every DIY SSR use-case will handle it. Instead, the router will not be initialized until any initially matched lazy routes are loaded, and therefore we need to delay the hydration or our `RouterProvider`. + +The recommended way to do this is to manually match routes against the initial location and load/update any lazy routes before creating your router: + +```jsx +// Determine if any of the initial routes are lazy +let lazyMatches = matchRoutes(routes, window.location)?.filter( + (m) => m.route.lazy +); + +// Load the lazy matches and update the routes before creating your router +// so we can hydrate the SSR-rendered content synchronously +if (lazyMatches && lazyMatches.length > 0) { + await Promise.all( + lazyMatches.map(async (m) => { + let routeModule = await m.route.lazy!(); + Object.assign(m.route, { ...routeModule, lazy: undefined }); + }) + ); +} + +// Create router and hydrate +let router = createBrowserRouter(routes) +ReactDOM.hydrateRoot( + document.getElementById("app")!, + +); +``` + +[manually-code-split]: https://www.infoxicator.com/en/react-router-6-4-code-splitting +[proposal]: https://github.com/remix-run/react-router/discussions/9826 +[poc]: https://github.com/remix-run/react-router/pull/9830 diff --git a/docs/route/error-element.md b/docs/route/error-element.md index 276586cf2e..0da98ba438 100644 --- a/docs/route/error-element.md +++ b/docs/route/error-element.md @@ -7,6 +7,8 @@ new: true When exceptions are thrown in [loaders][loader], [actions][action], or component rendering, instead of the normal render path for your Routes (``), the error path will be rendered (``) and the error made available with [`useRouteError`][userouteerror]. +If you do not wish to specify a React element (i.e., `errorElement={}`) you may specify an `ErrorBoundary` component instead (i.e., `ErrorBoundary={MyErrorBoundary}`) and React Router will call `createElement` for you internally. + This feature only works if using a data router like [`createBrowserRouter`][createbrowserrouter] ```tsx diff --git a/docs/route/lazy.md b/docs/route/lazy.md new file mode 100644 index 0000000000..1453d6b17e --- /dev/null +++ b/docs/route/lazy.md @@ -0,0 +1,62 @@ +--- +title: lazy +new: true +--- + +# `lazy` + +In order to keep your application bundles small and support code-splitting of your routes, each route can provide an async function that resolves the non-route-matching portions of your route definition (`loader`, `action`, `Component`/`element`, `ErrorBoundary`/`errorElement`, etc.). + +Lazy routes are resolved on initial load and during the `loading` or `submitting` phase of a navigation or fetcher call. You cannot lazily define route-matching properties (`path`, `index`, `children`, `caseSensitive`) since we only execute your lazy route functions after we've matched known routes. + +This feature only works if using a data router, see [Picking a Router][pickingarouter] + +Each `lazy` function will typically return the result of a dynamic import. + +```jsx +let routes = createRoutesFromElements( + }> + import("./a")} /> + import("./b")} /> + +); +``` + +Then in your lazy route modules, export the properties you want defined for the route: + +```jsx +export async function loader({ request }) { + let data = await fetchData(request); + return json(data); +} + +export function Component() { + let data = useLoaderData(); + + return ( + <> +

You made it!

+

{data}

+ + ); +} + +// If you want to customize the component display name in React dev tools: +Component.displayName = "SampleLazyRoute"; + +export function ErrorBoundary() { + let error = useRouteError(); + return isRouteErrorResponse(error) ? ( +

+ {error.status} {error.statusText} +

+ ) : ( +

{error.message || error}

+ ); +} + +// If you want to customize the component display name in React dev tools: +ErrorBoundary.displayName = "SampleErrorBoundary"; +``` + +[pickingarouter]: ../routers/picking-a-router diff --git a/docs/route/route.md b/docs/route/route.md index f4752b12b4..dacf411466 100644 --- a/docs/route/route.md +++ b/docs/route/route.md @@ -62,6 +62,8 @@ const router = createBrowserRouter( Neither style is discouraged and behavior is identical. For the majority of this doc we will use the JSX style because that's what most people are accustomed to in the context of React Router. +If you do not wish to specify a React element (i.e., `element={}`) you may specify a `Component` instead (i.e., `Component={MyComponent}`) and React Router will call `createElement` for you internally. + ## Type declaration ```tsx @@ -74,9 +76,12 @@ interface RouteObject { loader?: LoaderFunction; action?: ActionFunction; element?: React.ReactNode | null; + Component?: React.ComponentType | null; errorElement?: React.ReactNode | null; + ErrorBoundary?: React.ComponentType | null; handle?: RouteObject["handle"]; shouldRevalidate?: ShouldRevalidateFunction; + lazy?: LazyRouteFunction; } ``` @@ -327,6 +332,45 @@ Please see the [errorElement][errorelement] documentation for more details. Any application-specific data. Please see the [useMatches][usematches] documentation for details and examples. +## `lazy` + +In order to keep your application bundles small and support code-splitting of your routes, each route can provide an async function that resolves the non-route-matching portions of your route definition (`loader`, `action`, `Component`/`element`, `ErrorBoundary`/`errorElement`, etc.). + +Each `lazy` function will typically return the result of a dynamic import. + +```jsx +let routes = createRoutesFromElements( + }> + import("./a")} /> + import("./b")} /> + +); +``` + +Then in your lazy route modules, export the properties you want defined for the route: + +```jsx +export async function loader({ request }) { + let data = await fetchData(request); + return json(data); +} + +export function Component() { + let data = useLoaderData(); + + return ( + <> +

You made it!

+

{data}

+ + ); +} +``` + +If you are not using a data router like [`createBrowserRouter`][createbrowserrouter], this will do nothing + +Please see the [lazy][lazy] documentation for more details. + [remix]: https://remix.run [indexroute]: ../start/concepts#index-routes [outlet]: ../components/outlet @@ -340,3 +384,4 @@ Any application-specific data. Please see the [useMatches][usematches] documenta [createroutesfromelements]: ../utils/create-routes-from-elements [createbrowserrouter]: ../routers/create-browser-router [usematches]: ../hooks/use-matches +[lazy]: ./lazy diff --git a/examples/data-router/src/app.tsx b/examples/data-router/src/app.tsx index d6e9a89534..2e8ebee30b 100644 --- a/examples/data-router/src/app.tsx +++ b/examples/data-router/src/app.tsx @@ -26,27 +26,38 @@ import { addTodo, deleteTodo, getTodos } from "./todos"; import "./index.css"; -let router = createBrowserRouter( - createRoutesFromElements( - }> - } /> - } - errorElement={} - > - } /> - - } - /> - - ) -); +let router = createBrowserRouter([ + { + path: "/", + Component: Layout, + children: [ + { + index: true, + loader: homeLoader, + Component: Home, + }, + { + path: "todos", + action: todosAction, + loader: todosLoader, + Component: TodosList, + ErrorBoundary: TodosBoundary, + children: [ + { + path: ":id", + loader: todoLoader, + Component: Todo, + }, + ], + }, + { + path: "deferred", + loader: deferredLoader, + Component: DeferredPage, + }, + ], + }, +]); if (import.meta.hot) { import.meta.hot.dispose(() => router.dispose()); @@ -72,6 +83,7 @@ export function Layout() { let fetcherInProgress = fetchers.some((f) => ["loading", "submitting"].includes(f.state) ); + return ( <>

Data Router Example

diff --git a/examples/lazy-loading-router-provider/.gitignore b/examples/lazy-loading-router-provider/.gitignore new file mode 100644 index 0000000000..d451ff16c1 --- /dev/null +++ b/examples/lazy-loading-router-provider/.gitignore @@ -0,0 +1,5 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local diff --git a/examples/lazy-loading-router-provider/.stackblitzrc b/examples/lazy-loading-router-provider/.stackblitzrc new file mode 100644 index 0000000000..d98146f4d0 --- /dev/null +++ b/examples/lazy-loading-router-provider/.stackblitzrc @@ -0,0 +1,4 @@ +{ + "installDependencies": true, + "startCommand": "npm run dev" +} diff --git a/examples/lazy-loading-router-provider/README.md b/examples/lazy-loading-router-provider/README.md new file mode 100644 index 0000000000..795885fe38 --- /dev/null +++ b/examples/lazy-loading-router-provider/README.md @@ -0,0 +1,14 @@ +--- +title: Lazy Loading with RouterProvider +toc: false +--- + +# Lazy Loading Example using `RouterProvider` + +This example demonstrates how to lazily load individual route elements on demand `route.lazy()` and dynamic `import()`. Using this technique, pages that are not required on the home page can be split out into separate bundles, thereby decreasing load time on the initial page and improving performance. + +## Preview + +Open this example on [StackBlitz](https://stackblitz.com): + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/remix-run/react-router/tree/main/examples/lazy-loading-router-provider?file=src/App.tsx) diff --git a/examples/lazy-loading-router-provider/index.html b/examples/lazy-loading-router-provider/index.html new file mode 100644 index 0000000000..3b9fb14a37 --- /dev/null +++ b/examples/lazy-loading-router-provider/index.html @@ -0,0 +1,12 @@ + + + + + + React Router - Lazy Loading Example using RouterProvider + + +
+ + + diff --git a/examples/lazy-loading-router-provider/package-lock.json b/examples/lazy-loading-router-provider/package-lock.json new file mode 100644 index 0000000000..627f7ad50d --- /dev/null +++ b/examples/lazy-loading-router-provider/package-lock.json @@ -0,0 +1,1095 @@ +{ + "name": "lazy-loading", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "lazy-loading", + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.8.0" + }, + "devDependencies": { + "@rollup/plugin-replace": "^5.0.2", + "@types/node": "18.x", + "@types/react": "^18.0.27", + "@types/react-dom": "^18.0.10", + "@vitejs/plugin-react": "^3.0.1", + "typescript": "^4.9.5", + "vite": "^4.0.4" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.20.14", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.20.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helpers": "^7.20.7", + "@babel/parser": "^7.20.7", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.12", + "@babel/types": "^7.20.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.20.14", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.20.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.19.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.20.11", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.10", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.20.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.20.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.20.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.13", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.20.13", + "dev": true, + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.18.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.19.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.20.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.20.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.13", + "@babel/types": "^7.20.7", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.20.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.16.17", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.17", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@remix-run/router": { + "version": "1.3.1", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "5.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "magic-string": "^0.27.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@types/estree": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "18.11.18", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.0.27", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-react": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.20.7", + "@babel/plugin-transform-react-jsx-self": "^7.18.6", + "@babel/plugin-transform-react-jsx-source": "^7.19.6", + "magic-string": "^0.27.0", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.0.0" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/browserslist": { + "version": "4.21.5", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001450", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.284", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.16.17", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.16.17", + "@esbuild/android-arm64": "0.16.17", + "@esbuild/android-x64": "0.16.17", + "@esbuild/darwin-arm64": "0.16.17", + "@esbuild/darwin-x64": "0.16.17", + "@esbuild/freebsd-arm64": "0.16.17", + "@esbuild/freebsd-x64": "0.16.17", + "@esbuild/linux-arm": "0.16.17", + "@esbuild/linux-arm64": "0.16.17", + "@esbuild/linux-ia32": "0.16.17", + "@esbuild/linux-loong64": "0.16.17", + "@esbuild/linux-mips64el": "0.16.17", + "@esbuild/linux-ppc64": "0.16.17", + "@esbuild/linux-riscv64": "0.16.17", + "@esbuild/linux-s390x": "0.16.17", + "@esbuild/linux-x64": "0.16.17", + "@esbuild/netbsd-x64": "0.16.17", + "@esbuild/openbsd-x64": "0.16.17", + "@esbuild/sunos-x64": "0.16.17", + "@esbuild/win32-arm64": "0.16.17", + "@esbuild/win32-ia32": "0.16.17", + "@esbuild/win32-x64": "0.16.17" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "2.5.2", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.27.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.4", + "dev": true, + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.9", + "dev": true, + "license": "MIT" + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.4.21", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "18.2.0", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-refresh": { + "version": "0.14.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.8.0", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.3.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.8.0", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.3.1", + "react-router": "6.8.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rollup": { + "version": "3.12.1", + "dev": true, + "license": "MIT", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "4.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.16.3", + "postcss": "^8.4.20", + "resolve": "^1.22.1", + "rollup": "^3.7.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "dev": true, + "license": "ISC" + } + } +} diff --git a/examples/lazy-loading-router-provider/package.json b/examples/lazy-loading-router-provider/package.json new file mode 100644 index 0000000000..91a45834f3 --- /dev/null +++ b/examples/lazy-loading-router-provider/package.json @@ -0,0 +1,23 @@ +{ + "name": "lazy-loading-router-provider", + "private": true, + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "serve": "vite preview" + }, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.8.0" + }, + "devDependencies": { + "@rollup/plugin-replace": "^5.0.2", + "@types/node": "18.x", + "@types/react": "^18.0.27", + "@types/react-dom": "^18.0.10", + "@vitejs/plugin-react": "^3.0.1", + "typescript": "^4.9.5", + "vite": "^4.0.4" + } +} diff --git a/examples/lazy-loading-router-provider/src/App.tsx b/examples/lazy-loading-router-provider/src/App.tsx new file mode 100644 index 0000000000..37da473531 --- /dev/null +++ b/examples/lazy-loading-router-provider/src/App.tsx @@ -0,0 +1,138 @@ +import * as React from "react"; +import { + Outlet, + Link, + createBrowserRouter, + RouterProvider, + useNavigation, +} from "react-router-dom"; + +const router = createBrowserRouter([ + { + path: "/", + element: , + children: [ + { + index: true, + element: , + }, + { + path: "about", + // Single route in lazy file + lazy: () => import("./pages/About"), + }, + { + path: "dashboard", + async lazy() { + // Multiple routes in lazy file + let { DashboardLayout } = await import("./pages/Dashboard"); + return { Component: DashboardLayout }; + }, + children: [ + { + index: true, + async lazy() { + let { DashboardIndex } = await import("./pages/Dashboard"); + return { Component: DashboardIndex }; + }, + }, + { + path: "messages", + async lazy() { + let { dashboardMessagesLoader, DashboardMessages } = await import( + "./pages/Dashboard" + ); + return { + loader: dashboardMessagesLoader, + Component: DashboardMessages, + }; + }, + }, + ], + }, + { + path: "*", + element: , + }, + ], + }, +]); + +export default function App() { + return Loading...

} />; +} + +function Layout() { + let navigation = useNavigation(); + + return ( +
+

Lazy Loading Example using RouterProvider

+ +

+ This example demonstrates how to lazily load route definitions using{" "} + route.lazy(). To get the full effect of this demo, be sure + to open your Network tab and watch the new bundles load dynamically as + you navigate around. +

+ +

+ The "About" and "Dashboard" pages are not loaded until you click on the + link. When you do, the code is loaded via a dynamic{" "} + import() statement during the loading phase of + the navigation. Once the code loads, the route loader executes, and then + the element renders with the loader-provided data. +

+ +

+ This works for all data-loading/rendering related properties of a route, + including action, loader, element + , errorElement, and shouldRevalidate. You + cannot return path-matching properties from lazy() such as{" "} + path, index, children, and{" "} + caseSensitive. +

+ +
+ {navigation.state !== "idle" &&

Navigation in progress...

} +
+ + + +
+ + +
+ ); +} + +function Home() { + return ( +
+

Home

+
+ ); +} + +function NoMatch() { + return ( +
+

Nothing to see here!

+

+ Go to the home page +

+
+ ); +} diff --git a/examples/lazy-loading-router-provider/src/index.css b/examples/lazy-loading-router-provider/src/index.css new file mode 100644 index 0000000000..3e1f253f03 --- /dev/null +++ b/examples/lazy-loading-router-provider/src/index.css @@ -0,0 +1,12 @@ +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", + monospace; +} diff --git a/examples/lazy-loading-router-provider/src/main.tsx b/examples/lazy-loading-router-provider/src/main.tsx new file mode 100644 index 0000000000..c37d979194 --- /dev/null +++ b/examples/lazy-loading-router-provider/src/main.tsx @@ -0,0 +1,11 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; + +import "./index.css"; +import App from "./App"; + +ReactDOM.createRoot(document.getElementById("root")).render( + + + +); diff --git a/examples/lazy-loading-router-provider/src/pages/About.tsx b/examples/lazy-loading-router-provider/src/pages/About.tsx new file mode 100644 index 0000000000..9919f718c7 --- /dev/null +++ b/examples/lazy-loading-router-provider/src/pages/About.tsx @@ -0,0 +1,20 @@ +import * as React from "react"; +import { useLoaderData } from "react-router-dom"; + +export async function loader() { + await new Promise((r) => setTimeout(r, 500)); + return "I came from the About.tsx loader function!"; +} + +export function Component() { + let data = useLoaderData() as string; + + return ( +
+

About

+

{data}

+
+ ); +} + +Component.displayName = "AboutPage"; diff --git a/examples/lazy-loading-router-provider/src/pages/Dashboard.tsx b/examples/lazy-loading-router-provider/src/pages/Dashboard.tsx new file mode 100644 index 0000000000..6447a79abf --- /dev/null +++ b/examples/lazy-loading-router-provider/src/pages/Dashboard.tsx @@ -0,0 +1,61 @@ +import * as React from "react"; +import { Outlet, Link, useLoaderData } from "react-router-dom"; + +export function DashboardLayout() { + return ( +
+ + +
+ + +
+ ); +} + +export function DashboardIndex() { + return ( +
+

Dashboard Index

+
+ ); +} + +interface MessagesData { + messages: string[]; +} + +export async function dashboardMessagesLoader() { + await new Promise((r) => setTimeout(r, 500)); + return { + messages: [ + "Message 1 from Dashboard.tsx loader", + "Message 2 from Dashboard.tsx loader", + "Message 3 from Dashboard.tsx loader", + ], + } as MessagesData; +} + +export function DashboardMessages() { + let { messages } = useLoaderData() as MessagesData; + + return ( +
+

Messages

+
    + {messages.map((m) => ( +
  • {m}
  • + ))} +
+
+ ); +} diff --git a/examples/lazy-loading-router-provider/src/vite-env.d.ts b/examples/lazy-loading-router-provider/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/examples/lazy-loading-router-provider/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/lazy-loading-router-provider/tsconfig.json b/examples/lazy-loading-router-provider/tsconfig.json new file mode 100644 index 0000000000..8bdaabfe5d --- /dev/null +++ b/examples/lazy-loading-router-provider/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react", + "importsNotUsedAsValues": "error" + }, + "include": ["./src"] +} diff --git a/examples/lazy-loading-router-provider/vite.config.ts b/examples/lazy-loading-router-provider/vite.config.ts new file mode 100644 index 0000000000..b77eb48a30 --- /dev/null +++ b/examples/lazy-loading-router-provider/vite.config.ts @@ -0,0 +1,36 @@ +import * as path from "path"; +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import rollupReplace from "@rollup/plugin-replace"; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + rollupReplace({ + preventAssignment: true, + values: { + __DEV__: JSON.stringify(true), + "process.env.NODE_ENV": JSON.stringify("development"), + }, + }), + react(), + ], + resolve: process.env.USE_SOURCE + ? { + alias: { + "@remix-run/router": path.resolve( + __dirname, + "../../packages/router/index.ts" + ), + "react-router": path.resolve( + __dirname, + "../../packages/react-router/index.ts" + ), + "react-router-dom": path.resolve( + __dirname, + "../../packages/react-router-dom/index.tsx" + ), + }, + } + : {}, +}); diff --git a/examples/ssr-data-router/package-lock.json b/examples/ssr-data-router/package-lock.json index c264444bae..d8cf4e64a1 100644 --- a/examples/ssr-data-router/package-lock.json +++ b/examples/ssr-data-router/package-lock.json @@ -1,13 +1,13 @@ { "name": "ssr-data-router", - "lockfileVersion": 3, + "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ssr-data-router", "dependencies": { "@remix-run/node": "^1.12.0", - "@remix-run/router": "^1.3.1", + "@remix-run/router": "^1.3.2", "compression": "1.7.4", "cross-env": "^7.0.3", "express": "^4.18.2", @@ -29,8 +29,9 @@ }, "node_modules/@ampproject/remapping": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.1.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -41,8 +42,9 @@ }, "node_modules/@babel/code-frame": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "dev": true, - "license": "MIT", "dependencies": { "@babel/highlight": "^7.18.6" }, @@ -52,16 +54,18 @@ }, "node_modules/@babel/compat-data": { "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz", + "integrity": "sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", + "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", "dev": true, - "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", @@ -87,31 +91,11 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/debug": { - "version": "4.3.4", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/core/node_modules/ms": { - "version": "2.1.2", - "dev": true, - "license": "MIT" - }, "node_modules/@babel/generator": { "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", + "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/types": "^7.20.7", "@jridgewell/gen-mapping": "^0.3.2", @@ -123,8 +107,9 @@ }, "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -136,8 +121,9 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", "dev": true, - "license": "MIT", "dependencies": { "@babel/compat-data": "^7.20.5", "@babel/helper-validator-option": "^7.18.6", @@ -154,16 +140,18 @@ }, "node_modules/@babel/helper-environment-visitor": { "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dev": true, - "license": "MIT", "dependencies": { "@babel/template": "^7.18.10", "@babel/types": "^7.19.0" @@ -174,8 +162,9 @@ }, "node_modules/@babel/helper-hoist-variables": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "dev": true, - "license": "MIT", "dependencies": { "@babel/types": "^7.18.6" }, @@ -185,8 +174,9 @@ }, "node_modules/@babel/helper-module-imports": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/types": "^7.18.6" }, @@ -196,8 +186,9 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", + "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", @@ -214,16 +205,18 @@ }, "node_modules/@babel/helper-plugin-utils": { "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/types": "^7.20.2" }, @@ -233,8 +226,9 @@ }, "node_modules/@babel/helper-split-export-declaration": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/types": "^7.18.6" }, @@ -244,32 +238,36 @@ }, "node_modules/@babel/helper-string-parser": { "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz", + "integrity": "sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/template": "^7.20.7", "@babel/traverse": "^7.20.13", @@ -281,8 +279,9 @@ }, "node_modules/@babel/highlight": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", @@ -293,9 +292,10 @@ } }, "node_modules/@babel/parser": { - "version": "7.20.13", + "version": "7.20.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz", + "integrity": "sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==", "dev": true, - "license": "MIT", "bin": { "parser": "bin/babel-parser.js" }, @@ -305,8 +305,9 @@ }, "node_modules/@babel/plugin-transform-react-jsx-self": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.18.6.tgz", + "integrity": "sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.18.6" }, @@ -319,8 +320,9 @@ }, "node_modules/@babel/plugin-transform-react-jsx-source": { "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz", + "integrity": "sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.19.0" }, @@ -333,7 +335,8 @@ }, "node_modules/@babel/runtime": { "version": "7.20.13", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", + "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", "dependencies": { "regenerator-runtime": "^0.13.11" }, @@ -343,8 +346,9 @@ }, "node_modules/@babel/template": { "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/code-frame": "^7.18.6", "@babel/parser": "^7.20.7", @@ -356,8 +360,9 @@ }, "node_modules/@babel/traverse": { "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", + "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", "dev": true, - "license": "MIT", "dependencies": { "@babel/code-frame": "^7.18.6", "@babel/generator": "^7.20.7", @@ -374,31 +379,11 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.3.4", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/traverse/node_modules/ms": { - "version": "2.1.2", - "dev": true, - "license": "MIT" - }, "node_modules/@babel/types": { "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.19.4", "@babel/helper-validator-identifier": "^7.19.1", @@ -408,174 +393,512 @@ "node": ">=6.9.0" } }, - "node_modules/@esbuild/darwin-arm64": { + "node_modules/@esbuild/android-arm": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.17.tgz", + "integrity": "sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz", + "integrity": "sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ - "darwin" + "android" ], "engines": { "node": ">=12" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", + "node_modules/@esbuild/android-x64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.17.tgz", + "integrity": "sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=6.0.0" + "node": ">=12" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz", + "integrity": "sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.0.0" + "node": ">=12" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", + "node_modules/@esbuild/darwin-x64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz", + "integrity": "sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.0.0" + "node": ">=12" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz", + "integrity": "sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz", + "integrity": "sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@remix-run/node": { - "version": "1.12.0", - "license": "MIT", - "dependencies": { - "@remix-run/server-runtime": "1.12.0", - "@remix-run/web-fetch": "^4.3.2", - "@remix-run/web-file": "^3.0.2", - "@remix-run/web-stream": "^1.0.3", - "@web3-storage/multipart-parser": "^1.0.0", - "abort-controller": "^3.0.0", - "cookie-signature": "^1.1.0", - "source-map-support": "^0.5.21", - "stream-slice": "^0.1.2" - }, + "node_modules/@esbuild/linux-arm": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz", + "integrity": "sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=14" + "node": ">=12" } }, - "node_modules/@remix-run/node/node_modules/cookie-signature": { - "version": "1.2.0", - "license": "MIT", + "node_modules/@esbuild/linux-arm64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz", + "integrity": "sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.6.0" + "node": ">=12" } }, - "node_modules/@remix-run/router": { - "version": "1.3.1", - "license": "MIT", + "node_modules/@esbuild/linux-ia32": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz", + "integrity": "sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=14" + "node": ">=12" } }, - "node_modules/@remix-run/server-runtime": { - "version": "1.12.0", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.3.1", - "@types/cookie": "^0.4.0", - "@types/react": "^18.0.15", - "@web3-storage/multipart-parser": "^1.0.0", - "cookie": "^0.4.1", - "set-cookie-parser": "^2.4.8", - "source-map": "^0.7.3" - }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz", + "integrity": "sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=14" + "node": ">=12" } }, - "node_modules/@remix-run/server-runtime/node_modules/cookie": { - "version": "0.4.2", - "license": "MIT", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz", + "integrity": "sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.6" + "node": ">=12" } }, - "node_modules/@remix-run/server-runtime/node_modules/source-map": { - "version": "0.7.4", - "license": "BSD-3-Clause", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz", + "integrity": "sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 8" + "node": ">=12" } }, - "node_modules/@remix-run/web-blob": { - "version": "3.0.4", - "license": "MIT", - "dependencies": { - "@remix-run/web-stream": "^1.0.0", - "web-encoding": "1.1.5" + "node_modules/@esbuild/linux-riscv64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz", + "integrity": "sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@remix-run/web-fetch": { - "version": "4.3.2", - "license": "MIT", - "dependencies": { - "@remix-run/web-blob": "^3.0.4", - "@remix-run/web-form-data": "^3.0.3", - "@remix-run/web-stream": "^1.0.3", - "@web3-storage/multipart-parser": "^1.0.0", - "abort-controller": "^3.0.0", - "data-uri-to-buffer": "^3.0.1", - "mrmime": "^1.0.0" - }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz", + "integrity": "sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^10.17 || >=12.3" + "node": ">=12" } }, - "node_modules/@remix-run/web-file": { - "version": "3.0.2", - "license": "MIT", - "dependencies": { - "@remix-run/web-blob": "^3.0.3" + "node_modules/@esbuild/linux-x64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz", + "integrity": "sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@remix-run/web-form-data": { - "version": "3.0.4", - "license": "MIT", - "dependencies": { - "web-encoding": "1.1.5" + "node_modules/@esbuild/netbsd-x64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz", + "integrity": "sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@remix-run/web-stream": { + "node_modules/@esbuild/openbsd-x64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz", + "integrity": "sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz", + "integrity": "sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz", + "integrity": "sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz", + "integrity": "sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz", + "integrity": "sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@remix-run/node": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@remix-run/node/-/node-1.12.0.tgz", + "integrity": "sha512-WiyRTEQKTUTf3Z3ke5DOwx+fjCkeD8ilI9kbRws1bG3xfdugaDrV9ra76DOZcrYlmVwjwtKE3mVDSRFtiYTTMw==", + "dependencies": { + "@remix-run/server-runtime": "1.12.0", + "@remix-run/web-fetch": "^4.3.2", + "@remix-run/web-file": "^3.0.2", + "@remix-run/web-stream": "^1.0.3", + "@web3-storage/multipart-parser": "^1.0.0", + "abort-controller": "^3.0.0", + "cookie-signature": "^1.1.0", + "source-map-support": "^0.5.21", + "stream-slice": "^0.1.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@remix-run/router": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.2.tgz", + "integrity": "sha512-t54ONhl/h75X94SWsHGQ4G/ZrCEguKSRQr7DrjTciJXW0YU1QhlwYeycvK5JgkzlxmvrK7wq1NB/PLtHxoiDcA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@remix-run/server-runtime": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@remix-run/server-runtime/-/server-runtime-1.12.0.tgz", + "integrity": "sha512-7I0165Ns/ffPfCEfuiqD58lMderTn2s/sew1xJ34ONa21mG/7+5T7diHIgxKST8rS3816JPmlwSqUaHgwbmO6Q==", + "dependencies": { + "@remix-run/router": "1.3.1", + "@types/cookie": "^0.4.0", + "@types/react": "^18.0.15", + "@web3-storage/multipart-parser": "^1.0.0", + "cookie": "^0.4.1", + "set-cookie-parser": "^2.4.8", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@remix-run/server-runtime/node_modules/@remix-run/router": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.1.tgz", + "integrity": "sha512-+eun1Wtf72RNRSqgU7qM2AMX/oHp+dnx7BHk1qhK5ZHzdHTUU4LA1mGG1vT+jMc8sbhG3orvsfOmryjzx2PzQw==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@remix-run/web-blob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@remix-run/web-blob/-/web-blob-3.0.4.tgz", + "integrity": "sha512-AfegzZvSSDc+LwnXV+SwROTrDtoLiPxeFW+jxgvtDAnkuCX1rrzmVJ6CzqZ1Ai0bVfmJadkG5GxtAfYclpPmgw==", + "dependencies": { + "@remix-run/web-stream": "^1.0.0", + "web-encoding": "1.1.5" + } + }, + "node_modules/@remix-run/web-fetch": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@remix-run/web-fetch/-/web-fetch-4.3.2.tgz", + "integrity": "sha512-aRNaaa0Fhyegv/GkJ/qsxMhXvyWGjPNgCKrStCvAvV1XXphntZI0nQO/Fl02LIQg3cGL8lDiOXOS1gzqDOlG5w==", + "dependencies": { + "@remix-run/web-blob": "^3.0.4", + "@remix-run/web-form-data": "^3.0.3", + "@remix-run/web-stream": "^1.0.3", + "@web3-storage/multipart-parser": "^1.0.0", + "abort-controller": "^3.0.0", + "data-uri-to-buffer": "^3.0.1", + "mrmime": "^1.0.0" + }, + "engines": { + "node": "^10.17 || >=12.3" + } + }, + "node_modules/@remix-run/web-file": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@remix-run/web-file/-/web-file-3.0.2.tgz", + "integrity": "sha512-eFC93Onh/rZ5kUNpCQersmBtxedGpaXK2/gsUl49BYSGK/DvuPu3l06vmquEDdcPaEuXcsdGP0L7zrmUqrqo4A==", + "dependencies": { + "@remix-run/web-blob": "^3.0.3" + } + }, + "node_modules/@remix-run/web-form-data": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@remix-run/web-form-data/-/web-form-data-3.0.4.tgz", + "integrity": "sha512-UMF1jg9Vu9CLOf8iHBdY74Mm3PUvMW8G/XZRJE56SxKaOFWGSWlfxfG+/a3boAgHFLTkP7K4H1PxlRugy1iQtw==", + "dependencies": { + "web-encoding": "1.1.5" + } + }, + "node_modules/@remix-run/web-stream": { "version": "1.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@remix-run/web-stream/-/web-stream-1.0.3.tgz", + "integrity": "sha512-wlezlJaA5NF6SsNMiwQnnAW6tnPzQ5I8qk0Y0pSohm0eHKa2FQ1QhEKLVVcDDu02TmkfHgnux0igNfeYhDOXiA==", "dependencies": { "web-streams-polyfill": "^3.1.1" } }, "node_modules/@rollup/plugin-replace": { "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.2.tgz", + "integrity": "sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==", "dev": true, - "license": "MIT", "dependencies": { "@rollup/pluginutils": "^5.0.1", "magic-string": "^0.27.0" @@ -594,8 +917,9 @@ }, "node_modules/@rollup/pluginutils": { "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", + "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", "dev": true, - "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", @@ -615,8 +939,9 @@ }, "node_modules/@types/body-parser": { "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", "dev": true, - "license": "MIT", "dependencies": { "@types/connect": "*", "@types/node": "*" @@ -624,36 +949,41 @@ }, "node_modules/@types/connect": { "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/cookie": { "version": "0.4.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" }, "node_modules/@types/estree": { "version": "1.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "dev": true }, "node_modules/@types/express": { - "version": "4.17.16", + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", "dev": true, - "license": "MIT", "dependencies": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.31", + "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "*" } }, "node_modules/@types/express-serve-static-core": { "version": "4.17.33", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", + "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -662,31 +992,37 @@ }, "node_modules/@types/mime": { "version": "3.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true }, "node_modules/@types/node": { - "version": "18.11.18", - "dev": true, - "license": "MIT" + "version": "18.13.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz", + "integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==", + "dev": true }, "node_modules/@types/prop-types": { "version": "15.7.5", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "node_modules/@types/qs": { "version": "6.9.7", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true }, "node_modules/@types/range-parser": { "version": "1.2.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true }, "node_modules/@types/react": { "version": "18.0.27", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.27.tgz", + "integrity": "sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -695,31 +1031,35 @@ }, "node_modules/@types/react-dom": { "version": "18.0.10", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.10.tgz", + "integrity": "sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==", "dev": true, - "license": "MIT", "dependencies": { "@types/react": "*" } }, "node_modules/@types/scheduler": { "version": "0.16.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, "node_modules/@types/serve-static": { "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", "dev": true, - "license": "MIT", "dependencies": { "@types/mime": "*", "@types/node": "*" } }, "node_modules/@vitejs/plugin-react": { - "version": "3.0.1", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-3.1.0.tgz", + "integrity": "sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/core": "^7.20.7", + "@babel/core": "^7.20.12", "@babel/plugin-transform-react-jsx-self": "^7.18.6", "@babel/plugin-transform-react-jsx-source": "^7.19.6", "magic-string": "^0.27.0", @@ -729,21 +1069,24 @@ "node": "^14.18.0 || >=16.0.0" }, "peerDependencies": { - "vite": "^4.0.0" + "vite": "^4.1.0-beta.0" } }, "node_modules/@web3-storage/multipart-parser": { "version": "1.0.0", - "license": "(Apache-2.0 AND MIT)" + "resolved": "https://registry.npmjs.org/@web3-storage/multipart-parser/-/multipart-parser-1.0.0.tgz", + "integrity": "sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==" }, "node_modules/@zxing/text-encoding": { "version": "0.9.0", - "license": "(Unlicense OR Apache-2.0)", + "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", + "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", "optional": true }, "node_modules/abort-controller": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "dependencies": { "event-target-shim": "^5.0.0" }, @@ -753,7 +1096,8 @@ }, "node_modules/accepts": { "version": "1.3.8", - "license": "MIT", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -764,8 +1108,9 @@ }, "node_modules/ansi-styles": { "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -775,11 +1120,13 @@ }, "node_modules/array-flatten": { "version": "1.1.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "node_modules/available-typed-arrays": { "version": "1.0.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", "engines": { "node": ">= 0.4" }, @@ -789,7 +1136,8 @@ }, "node_modules/body-parser": { "version": "1.20.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.4", @@ -809,8 +1157,31 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/browserslist": { "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", "dev": true, "funding": [ { @@ -822,7 +1193,6 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], - "license": "MIT", "dependencies": { "caniuse-lite": "^1.0.30001449", "electron-to-chromium": "^1.4.284", @@ -838,18 +1208,21 @@ }, "node_modules/buffer-from": { "version": "1.1.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, "node_modules/bytes": { - "version": "3.1.2", - "license": "MIT", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", "engines": { "node": ">= 0.8" } }, "node_modules/call-bind": { "version": "1.0.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "dependencies": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -859,7 +1232,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001450", + "version": "1.0.30001451", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001451.tgz", + "integrity": "sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w==", "dev": true, "funding": [ { @@ -870,13 +1245,13 @@ "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" } - ], - "license": "CC-BY-4.0" + ] }, "node_modules/chalk": { "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -888,20 +1263,23 @@ }, "node_modules/color-convert": { "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "1.1.3" } }, "node_modules/color-name": { "version": "1.1.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/compressible": { "version": "2.0.18", - "license": "MIT", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dependencies": { "mime-db": ">= 1.43.0 < 2" }, @@ -911,7 +1289,8 @@ }, "node_modules/compression": { "version": "1.7.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", "dependencies": { "accepts": "~1.3.5", "bytes": "3.0.0", @@ -925,20 +1304,23 @@ "node": ">= 0.8.0" } }, - "node_modules/compression/node_modules/bytes": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.8" + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" } }, - "node_modules/compression/node_modules/safe-buffer": { - "version": "5.1.2", - "license": "MIT" + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/content-disposition": { "version": "0.5.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dependencies": { "safe-buffer": "5.2.1" }, @@ -946,32 +1328,59 @@ "node": ">= 0.6" } }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/content-type": { "version": "1.0.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { "node": ">= 0.6" } }, "node_modules/convert-source-map": { "version": "1.9.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true }, "node_modules/cookie": { - "version": "0.5.0", - "license": "MIT", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", "engines": { "node": ">= 0.6" } }, "node_modules/cookie-signature": { - "version": "1.0.6", - "license": "MIT" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.0.tgz", + "integrity": "sha512-R0BOPfLGTitaKhgKROKZQN6iyq2iDQcH1DOF8nJoaWapguX5bC2w+Q/I9NmmM5lfcvEarnLZr+cCvmEYYSXvYA==", + "engines": { + "node": ">=6.6.0" + } }, "node_modules/cross-env": { "version": "7.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", "dependencies": { "cross-spawn": "^7.0.1" }, @@ -987,7 +1396,8 @@ }, "node_modules/cross-spawn": { "version": "7.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -999,32 +1409,46 @@ }, "node_modules/csstype": { "version": "3.1.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, "node_modules/data-uri-to-buffer": { "version": "3.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", "engines": { "node": ">= 6" } }, "node_modules/debug": { - "version": "2.6.9", - "license": "MIT", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, "dependencies": { - "ms": "2.0.0" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/depd": { "version": "2.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { "node": ">= 0.8" } }, "node_modules/destroy": { "version": "1.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" @@ -1032,25 +1456,29 @@ }, "node_modules/ee-first": { "version": "1.1.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.284", - "dev": true, - "license": "ISC" + "version": "1.4.293", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.293.tgz", + "integrity": "sha512-h7vBlhC83NsgC9UO3LOZx91xgstIrHk5iqMbZgnEArL5rHTM6HfsUZhnwb3oRnNetXM1741kB9SO7x9jLshz5A==", + "dev": true }, "node_modules/encodeurl": { "version": "1.0.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "engines": { "node": ">= 0.8" } }, "node_modules/esbuild": { "version": "0.16.17", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.17.tgz", + "integrity": "sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==", "dev": true, "hasInstallScript": true, - "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -1084,46 +1512,53 @@ }, "node_modules/escalade": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/escape-html": { "version": "1.0.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "node_modules/escape-string-regexp": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/estree-walker": { "version": "2.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true }, "node_modules/etag": { "version": "1.8.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { "node": ">= 0.6" } }, "node_modules/event-target-shim": { "version": "5.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "engines": { "node": ">=6" } }, "node_modules/express": { "version": "4.18.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -1161,47 +1596,111 @@ "node": ">= 0.10.0" } }, - "node_modules/finalhandler": { - "version": "1.2.0", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, + "node_modules/express/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, - "node_modules/for-each": { - "version": "0.3.3", - "license": "MIT", - "dependencies": { - "is-callable": "^1.1.3" + "node_modules/express/node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" } }, "node_modules/forwarded": { "version": "0.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "engines": { "node": ">= 0.6" } }, "node_modules/fresh": { "version": "0.5.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "engines": { "node": ">= 0.6" } }, "node_modules/fsevents": { "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "license": "MIT", + "hasInstallScript": true, "optional": true, "os": [ "darwin" @@ -1212,19 +1711,22 @@ }, "node_modules/function-bind": { "version": "1.1.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "node_modules/gensync": { "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/get-intrinsic": { "version": "1.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -1236,15 +1738,17 @@ }, "node_modules/globals": { "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/gopd": { "version": "1.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -1254,7 +1758,8 @@ }, "node_modules/has": { "version": "1.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dependencies": { "function-bind": "^1.1.1" }, @@ -1264,15 +1769,17 @@ }, "node_modules/has-flag": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/has-symbols": { "version": "1.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "engines": { "node": ">= 0.4" }, @@ -1282,7 +1789,8 @@ }, "node_modules/has-tostringtag": { "version": "1.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "dependencies": { "has-symbols": "^1.0.2" }, @@ -1295,14 +1803,16 @@ }, "node_modules/history": { "version": "5.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", + "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", "dependencies": { "@babel/runtime": "^7.7.6" } }, "node_modules/http-errors": { "version": "2.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -1316,7 +1826,8 @@ }, "node_modules/iconv-lite": { "version": "0.4.24", - "license": "MIT", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -1326,18 +1837,21 @@ }, "node_modules/inherits": { "version": "2.0.4", - "license": "ISC" + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ipaddr.js": { "version": "1.9.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "engines": { "node": ">= 0.10" } }, "node_modules/is-arguments": { "version": "1.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -1351,7 +1865,8 @@ }, "node_modules/is-callable": { "version": "1.2.7", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "engines": { "node": ">= 0.4" }, @@ -1361,8 +1876,9 @@ }, "node_modules/is-core-module": { "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dev": true, - "license": "MIT", "dependencies": { "has": "^1.0.3" }, @@ -1372,7 +1888,8 @@ }, "node_modules/is-generator-function": { "version": "1.0.10", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -1385,7 +1902,8 @@ }, "node_modules/is-typed-array": { "version": "1.1.10", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", @@ -1402,16 +1920,19 @@ }, "node_modules/isexe": { "version": "2.0.0", - "license": "ISC" + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/js-tokens": { "version": "4.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/jsesc": { "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, - "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -1421,8 +1942,9 @@ }, "node_modules/json5": { "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -1432,7 +1954,8 @@ }, "node_modules/loose-envify": { "version": "1.4.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -1442,16 +1965,18 @@ }, "node_modules/lru-cache": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "license": "ISC", "dependencies": { "yallist": "^3.0.2" } }, "node_modules/magic-string": { "version": "0.27.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", + "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.13" }, @@ -1461,25 +1986,29 @@ }, "node_modules/media-typer": { "version": "0.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "engines": { "node": ">= 0.6" } }, "node_modules/merge-descriptors": { "version": "1.0.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, "node_modules/methods": { "version": "1.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "engines": { "node": ">= 0.6" } }, "node_modules/mime": { "version": "1.6.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "bin": { "mime": "cli.js" }, @@ -1489,14 +2018,16 @@ }, "node_modules/mime-db": { "version": "1.52.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { "version": "2.1.35", - "license": "MIT", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { "mime-db": "1.52.0" }, @@ -1506,19 +2037,23 @@ }, "node_modules/mrmime": { "version": "1.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", "engines": { "node": ">=10" } }, "node_modules/ms": { - "version": "2.0.0", - "license": "MIT" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/nanoid": { "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", "dev": true, - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -1528,26 +2063,30 @@ }, "node_modules/negotiator": { "version": "0.6.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "engines": { "node": ">= 0.6" } }, "node_modules/node-releases": { - "version": "2.0.9", - "dev": true, - "license": "MIT" + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "dev": true }, "node_modules/object-inspect": { "version": "1.12.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/on-finished": { "version": "2.4.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dependencies": { "ee-first": "1.1.1" }, @@ -1557,43 +2096,50 @@ }, "node_modules/on-headers": { "version": "1.0.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", "engines": { "node": ">= 0.8" } }, "node_modules/parseurl": { "version": "1.3.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "engines": { "node": ">= 0.8" } }, "node_modules/path-key": { "version": "3.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "engines": { "node": ">=8" } }, "node_modules/path-parse": { "version": "1.0.7", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/path-to-regexp": { "version": "0.1.7", - "license": "MIT" + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, "node_modules/picocolors": { "version": "1.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true }, "node_modules/picomatch": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.6" }, @@ -1603,6 +2149,8 @@ }, "node_modules/postcss": { "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", "dev": true, "funding": [ { @@ -1614,7 +2162,6 @@ "url": "https://tidelift.com/funding/github/npm/postcss" } ], - "license": "MIT", "dependencies": { "nanoid": "^3.3.4", "picocolors": "^1.0.0", @@ -1626,7 +2173,8 @@ }, "node_modules/proxy-addr": { "version": "2.0.7", - "license": "MIT", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -1637,7 +2185,8 @@ }, "node_modules/qs": { "version": "6.11.0", - "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dependencies": { "side-channel": "^1.0.4" }, @@ -1650,14 +2199,16 @@ }, "node_modules/range-parser": { "version": "1.2.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { "version": "2.5.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -1668,9 +2219,18 @@ "node": ">= 0.8" } }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/react": { "version": "18.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", "dependencies": { "loose-envify": "^1.1.0" }, @@ -1680,7 +2240,8 @@ }, "node_modules/react-dom": { "version": "18.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -1691,17 +2252,19 @@ }, "node_modules/react-refresh": { "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", + "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-router": { - "version": "6.8.0", - "license": "MIT", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.8.1.tgz", + "integrity": "sha512-Jgi8BzAJQ8MkPt8ipXnR73rnD7EmZ0HFFb7jdQU24TynGW1Ooqin2KVDN9voSC+7xhqbbCd2cjGUepb6RObnyg==", "dependencies": { - "@remix-run/router": "1.3.1" + "@remix-run/router": "1.3.2" }, "engines": { "node": ">=14" @@ -1711,11 +2274,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.8.0", - "license": "MIT", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.8.1.tgz", + "integrity": "sha512-67EXNfkQgf34P7+PSb6VlBuaacGhkKn3kpE51+P6zYSG2kiRoumXEL6e27zTa9+PGF2MNXbgIUHTVlleLbIcHQ==", "dependencies": { - "@remix-run/router": "1.3.1", - "react-router": "6.8.0" + "@remix-run/router": "1.3.2", + "react-router": "6.8.1" }, "engines": { "node": ">=14" @@ -1727,12 +2291,14 @@ }, "node_modules/regenerator-runtime": { "version": "0.13.11", - "license": "MIT" + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "node_modules/resolve": { "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, - "license": "MIT", "dependencies": { "is-core-module": "^2.9.0", "path-parse": "^1.0.7", @@ -1746,9 +2312,10 @@ } }, "node_modules/rollup": { - "version": "3.12.1", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.14.0.tgz", + "integrity": "sha512-o23sdgCLcLSe3zIplT9nQ1+r97okuaiR+vmAPZPTDYB7/f3tgWIYNyiQveMsZwshBT0is4eGax/HH83Q7CG+/Q==", "dev": true, - "license": "MIT", "bin": { "rollup": "dist/bin/rollup" }, @@ -1761,45 +2328,36 @@ } }, "node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/safer-buffer": { "version": "2.1.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/scheduler": { "version": "0.23.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", "dependencies": { "loose-envify": "^1.1.0" } }, "node_modules/semver": { "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/send": { "version": "0.18.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -1819,13 +2377,28 @@ "node": ">= 0.8.0" } }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/send/node_modules/ms": { "version": "2.1.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/serve-static": { "version": "1.15.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", @@ -1838,15 +2411,18 @@ }, "node_modules/set-cookie-parser": { "version": "2.5.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz", + "integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==" }, "node_modules/setprototypeof": { "version": "1.2.0", - "license": "ISC" + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/shebang-command": { "version": "2.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -1856,14 +2432,16 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "engines": { "node": ">=8" } }, "node_modules/side-channel": { "version": "1.0.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", "dependencies": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -1874,43 +2452,57 @@ } }, "node_modules/source-map": { - "version": "0.6.1", - "license": "BSD-3-Clause", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, "node_modules/source-map-js": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/source-map-support": { "version": "0.5.21", - "license": "MIT", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/statuses": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "engines": { "node": ">= 0.8" } }, "node_modules/stream-slice": { "version": "0.1.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/stream-slice/-/stream-slice-0.1.2.tgz", + "integrity": "sha512-QzQxpoacatkreL6jsxnVb7X5R/pGw9OUv2qWTYWnmLpg4NdN31snPy/f3TdQE1ZUXaThRvj1Zw4/OGg0ZkaLMA==" }, "node_modules/supports-color": { "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -1920,8 +2512,9 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1931,22 +2524,25 @@ }, "node_modules/to-fast-properties": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/toidentifier": { "version": "1.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "engines": { "node": ">=0.6" } }, "node_modules/type-is": { "version": "1.6.18", - "license": "MIT", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -1957,8 +2553,9 @@ }, "node_modules/typescript": { "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -1969,13 +2566,16 @@ }, "node_modules/unpipe": { "version": "1.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "engines": { "node": ">= 0.8" } }, "node_modules/update-browserslist-db": { "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, "funding": [ { @@ -1987,7 +2587,6 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], - "license": "MIT", "dependencies": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -2001,7 +2600,8 @@ }, "node_modules/util": { "version": "0.12.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", @@ -2012,27 +2612,30 @@ }, "node_modules/utils-merge": { "version": "1.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "engines": { "node": ">= 0.4.0" } }, "node_modules/vary": { "version": "1.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "engines": { "node": ">= 0.8" } }, "node_modules/vite": { - "version": "4.0.4", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.1.1.tgz", + "integrity": "sha512-LM9WWea8vsxhr782r9ntg+bhSFS06FJgCvvB0+8hf8UWtvaiDagKYWXndjfX6kGl74keHJUcpzrQliDXZlF5yg==", "dev": true, - "license": "MIT", "dependencies": { - "esbuild": "^0.16.3", - "postcss": "^8.4.20", + "esbuild": "^0.16.14", + "postcss": "^8.4.21", "resolve": "^1.22.1", - "rollup": "^3.7.0" + "rollup": "^3.10.0" }, "bin": { "vite": "bin/vite.js" @@ -2074,7 +2677,8 @@ }, "node_modules/web-encoding": { "version": "1.1.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz", + "integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==", "dependencies": { "util": "^0.12.3" }, @@ -2084,14 +2688,16 @@ }, "node_modules/web-streams-polyfill": { "version": "3.2.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", "engines": { "node": ">= 8" } }, "node_modules/which": { "version": "2.0.2", - "license": "ISC", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dependencies": { "isexe": "^2.0.0" }, @@ -2104,7 +2710,8 @@ }, "node_modules/which-typed-array": { "version": "1.1.9", - "license": "MIT", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", @@ -2122,8 +2729,1881 @@ }, "node_modules/yallist": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz", + "integrity": "sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==", + "dev": true + }, + "@babel/core": { + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", + "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helpers": "^7.20.7", + "@babel/parser": "^7.20.7", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.12", + "@babel/types": "^7.20.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + } + }, + "@babel/generator": { + "version": "7.20.14", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", + "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", + "dev": true, + "requires": { + "@babel/types": "^7.20.7", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-compilation-targets": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", + "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.10", + "@babel/types": "^7.20.7" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "requires": { + "@babel/types": "^7.20.2" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz", + "integrity": "sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==", + "dev": true, + "requires": { + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.13", + "@babel/types": "^7.20.7" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.20.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz", + "integrity": "sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==", + "dev": true + }, + "@babel/plugin-transform-react-jsx-self": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.18.6.tgz", + "integrity": "sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-react-jsx-source": { + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz", + "integrity": "sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/runtime": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", + "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", + "requires": { + "regenerator-runtime": "^0.13.11" + } + }, + "@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + } + }, + "@babel/traverse": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", + "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.13", + "@babel/types": "^7.20.7", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@esbuild/android-arm": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.17.tgz", + "integrity": "sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz", + "integrity": "sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==", "dev": true, - "license": "ISC" + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.17.tgz", + "integrity": "sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz", + "integrity": "sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz", + "integrity": "sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz", + "integrity": "sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz", + "integrity": "sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz", + "integrity": "sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz", + "integrity": "sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz", + "integrity": "sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz", + "integrity": "sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz", + "integrity": "sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz", + "integrity": "sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz", + "integrity": "sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz", + "integrity": "sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz", + "integrity": "sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz", + "integrity": "sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz", + "integrity": "sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz", + "integrity": "sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz", + "integrity": "sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz", + "integrity": "sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz", + "integrity": "sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==", + "dev": true, + "optional": true + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "@remix-run/node": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@remix-run/node/-/node-1.12.0.tgz", + "integrity": "sha512-WiyRTEQKTUTf3Z3ke5DOwx+fjCkeD8ilI9kbRws1bG3xfdugaDrV9ra76DOZcrYlmVwjwtKE3mVDSRFtiYTTMw==", + "requires": { + "@remix-run/server-runtime": "1.12.0", + "@remix-run/web-fetch": "^4.3.2", + "@remix-run/web-file": "^3.0.2", + "@remix-run/web-stream": "^1.0.3", + "@web3-storage/multipart-parser": "^1.0.0", + "abort-controller": "^3.0.0", + "cookie-signature": "^1.1.0", + "source-map-support": "^0.5.21", + "stream-slice": "^0.1.2" + } + }, + "@remix-run/router": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.2.tgz", + "integrity": "sha512-t54ONhl/h75X94SWsHGQ4G/ZrCEguKSRQr7DrjTciJXW0YU1QhlwYeycvK5JgkzlxmvrK7wq1NB/PLtHxoiDcA==" + }, + "@remix-run/server-runtime": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@remix-run/server-runtime/-/server-runtime-1.12.0.tgz", + "integrity": "sha512-7I0165Ns/ffPfCEfuiqD58lMderTn2s/sew1xJ34ONa21mG/7+5T7diHIgxKST8rS3816JPmlwSqUaHgwbmO6Q==", + "requires": { + "@remix-run/router": "1.3.1", + "@types/cookie": "^0.4.0", + "@types/react": "^18.0.15", + "@web3-storage/multipart-parser": "^1.0.0", + "cookie": "^0.4.1", + "set-cookie-parser": "^2.4.8", + "source-map": "^0.7.3" + }, + "dependencies": { + "@remix-run/router": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.1.tgz", + "integrity": "sha512-+eun1Wtf72RNRSqgU7qM2AMX/oHp+dnx7BHk1qhK5ZHzdHTUU4LA1mGG1vT+jMc8sbhG3orvsfOmryjzx2PzQw==" + } + } + }, + "@remix-run/web-blob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@remix-run/web-blob/-/web-blob-3.0.4.tgz", + "integrity": "sha512-AfegzZvSSDc+LwnXV+SwROTrDtoLiPxeFW+jxgvtDAnkuCX1rrzmVJ6CzqZ1Ai0bVfmJadkG5GxtAfYclpPmgw==", + "requires": { + "@remix-run/web-stream": "^1.0.0", + "web-encoding": "1.1.5" + } + }, + "@remix-run/web-fetch": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@remix-run/web-fetch/-/web-fetch-4.3.2.tgz", + "integrity": "sha512-aRNaaa0Fhyegv/GkJ/qsxMhXvyWGjPNgCKrStCvAvV1XXphntZI0nQO/Fl02LIQg3cGL8lDiOXOS1gzqDOlG5w==", + "requires": { + "@remix-run/web-blob": "^3.0.4", + "@remix-run/web-form-data": "^3.0.3", + "@remix-run/web-stream": "^1.0.3", + "@web3-storage/multipart-parser": "^1.0.0", + "abort-controller": "^3.0.0", + "data-uri-to-buffer": "^3.0.1", + "mrmime": "^1.0.0" + } + }, + "@remix-run/web-file": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@remix-run/web-file/-/web-file-3.0.2.tgz", + "integrity": "sha512-eFC93Onh/rZ5kUNpCQersmBtxedGpaXK2/gsUl49BYSGK/DvuPu3l06vmquEDdcPaEuXcsdGP0L7zrmUqrqo4A==", + "requires": { + "@remix-run/web-blob": "^3.0.3" + } + }, + "@remix-run/web-form-data": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@remix-run/web-form-data/-/web-form-data-3.0.4.tgz", + "integrity": "sha512-UMF1jg9Vu9CLOf8iHBdY74Mm3PUvMW8G/XZRJE56SxKaOFWGSWlfxfG+/a3boAgHFLTkP7K4H1PxlRugy1iQtw==", + "requires": { + "web-encoding": "1.1.5" + } + }, + "@remix-run/web-stream": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@remix-run/web-stream/-/web-stream-1.0.3.tgz", + "integrity": "sha512-wlezlJaA5NF6SsNMiwQnnAW6tnPzQ5I8qk0Y0pSohm0eHKa2FQ1QhEKLVVcDDu02TmkfHgnux0igNfeYhDOXiA==", + "requires": { + "web-streams-polyfill": "^3.1.1" + } + }, + "@rollup/plugin-replace": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.2.tgz", + "integrity": "sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^5.0.1", + "magic-string": "^0.27.0" + } + }, + "@rollup/pluginutils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", + "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + } + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "dev": true + }, + "@types/express": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.33", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", + "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true + }, + "@types/node": { + "version": "18.13.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz", + "integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "@types/react": { + "version": "18.0.27", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.27.tgz", + "integrity": "sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "18.0.10", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.10.tgz", + "integrity": "sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "@vitejs/plugin-react": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-3.1.0.tgz", + "integrity": "sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==", + "dev": true, + "requires": { + "@babel/core": "^7.20.12", + "@babel/plugin-transform-react-jsx-self": "^7.18.6", + "@babel/plugin-transform-react-jsx-source": "^7.19.6", + "magic-string": "^0.27.0", + "react-refresh": "^0.14.0" + } + }, + "@web3-storage/multipart-parser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@web3-storage/multipart-parser/-/multipart-parser-1.0.0.tgz", + "integrity": "sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==" + }, + "@zxing/text-encoding": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", + "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", + "optional": true + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + }, + "body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "caniuse-lite": { + "version": "1.0.30001451", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001451.tgz", + "integrity": "sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "requires": { + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + }, + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + }, + "cookie-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.0.tgz", + "integrity": "sha512-R0BOPfLGTitaKhgKROKZQN6iyq2iDQcH1DOF8nJoaWapguX5bC2w+Q/I9NmmM5lfcvEarnLZr+cCvmEYYSXvYA==" + }, + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "requires": { + "cross-spawn": "^7.0.1" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + }, + "data-uri-to-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "electron-to-chromium": { + "version": "1.4.293", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.293.tgz", + "integrity": "sha512-h7vBlhC83NsgC9UO3LOZx91xgstIrHk5iqMbZgnEArL5rHTM6HfsUZhnwb3oRnNetXM1741kB9SO7x9jLshz5A==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "esbuild": { + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.17.tgz", + "integrity": "sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==", + "dev": true, + "requires": { + "@esbuild/android-arm": "0.16.17", + "@esbuild/android-arm64": "0.16.17", + "@esbuild/android-x64": "0.16.17", + "@esbuild/darwin-arm64": "0.16.17", + "@esbuild/darwin-x64": "0.16.17", + "@esbuild/freebsd-arm64": "0.16.17", + "@esbuild/freebsd-x64": "0.16.17", + "@esbuild/linux-arm": "0.16.17", + "@esbuild/linux-arm64": "0.16.17", + "@esbuild/linux-ia32": "0.16.17", + "@esbuild/linux-loong64": "0.16.17", + "@esbuild/linux-mips64el": "0.16.17", + "@esbuild/linux-ppc64": "0.16.17", + "@esbuild/linux-riscv64": "0.16.17", + "@esbuild/linux-s390x": "0.16.17", + "@esbuild/linux-x64": "0.16.17", + "@esbuild/netbsd-x64": "0.16.17", + "@esbuild/openbsd-x64": "0.16.17", + "@esbuild/sunos-x64": "0.16.17", + "@esbuild/win32-arm64": "0.16.17", + "@esbuild/win32-ia32": "0.16.17", + "@esbuild/win32-x64": "0.16.17" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "history": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", + "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", + "requires": { + "@babel/runtime": "^7.7.6" + } + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" + }, + "is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "magic-string": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", + "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", + "dev": true, + "requires": { + "@jridgewell/sourcemap-codec": "^1.4.13" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "node-releases": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "dev": true + }, + "object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "postcss": { + "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "dev": true, + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + } + } + }, + "react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + } + }, + "react-refresh": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", + "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", + "dev": true + }, + "react-router": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.8.1.tgz", + "integrity": "sha512-Jgi8BzAJQ8MkPt8ipXnR73rnD7EmZ0HFFb7jdQU24TynGW1Ooqin2KVDN9voSC+7xhqbbCd2cjGUepb6RObnyg==", + "requires": { + "@remix-run/router": "1.3.2" + } + }, + "react-router-dom": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.8.1.tgz", + "integrity": "sha512-67EXNfkQgf34P7+PSb6VlBuaacGhkKn3kpE51+P6zYSG2kiRoumXEL6e27zTa9+PGF2MNXbgIUHTVlleLbIcHQ==", + "requires": { + "@remix-run/router": "1.3.2", + "react-router": "6.8.1" + } + }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "rollup": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.14.0.tgz", + "integrity": "sha512-o23sdgCLcLSe3zIplT9nQ1+r97okuaiR+vmAPZPTDYB7/f3tgWIYNyiQveMsZwshBT0is4eGax/HH83Q7CG+/Q==", + "dev": true, + "requires": { + "fsevents": "~2.3.2" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "set-cookie-parser": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz", + "integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==" + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "stream-slice": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/stream-slice/-/stream-slice-0.1.2.tgz", + "integrity": "sha512-QzQxpoacatkreL6jsxnVb7X5R/pGw9OUv2qWTYWnmLpg4NdN31snPy/f3TdQE1ZUXaThRvj1Zw4/OGg0ZkaLMA==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "vite": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.1.1.tgz", + "integrity": "sha512-LM9WWea8vsxhr782r9ntg+bhSFS06FJgCvvB0+8hf8UWtvaiDagKYWXndjfX6kGl74keHJUcpzrQliDXZlF5yg==", + "dev": true, + "requires": { + "esbuild": "^0.16.14", + "fsevents": "~2.3.2", + "postcss": "^8.4.21", + "resolve": "^1.22.1", + "rollup": "^3.10.0" + } + }, + "web-encoding": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz", + "integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==", + "requires": { + "@zxing/text-encoding": "0.9.0", + "util": "^0.12.3" + } + }, + "web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true } } } diff --git a/examples/ssr-data-router/package.json b/examples/ssr-data-router/package.json index 741e39ac8f..5fb41deca1 100644 --- a/examples/ssr-data-router/package.json +++ b/examples/ssr-data-router/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@remix-run/node": "^1.12.0", - "@remix-run/router": "^1.3.1", + "@remix-run/router": "^1.3.2", "compression": "1.7.4", "cross-env": "^7.0.3", "express": "^4.18.2", diff --git a/examples/ssr-data-router/src/App.tsx b/examples/ssr-data-router/src/App.tsx index 9ccd40bba8..470d9f4f7f 100644 --- a/examples/ssr-data-router/src/App.tsx +++ b/examples/ssr-data-router/src/App.tsx @@ -1,7 +1,8 @@ import * as React from "react"; +import type { RouteObject } from "react-router-dom"; import { Outlet, Link, useLoaderData, redirect } from "react-router-dom"; -export const routes = [ +export const routes: RouteObject[] = [ { path: "/", element: , @@ -20,6 +21,10 @@ export const routes = [ loader: dashboardLoader, element: , }, + { + path: "lazy", + lazy: () => import("./lazy"), + }, { path: "redirect", loader: redirectLoader, @@ -70,6 +75,9 @@ function Layout() {
  • Dashboard
  • +
  • + Lazy +
  • Redirect to Home
  • @@ -86,7 +94,7 @@ function Layout() { ); } -const sleep = () => new Promise((r) => setTimeout(r, 500)); +const sleep = (n = 500) => new Promise((r) => setTimeout(r, n)); const rand = () => Math.round(Math.random() * 100); async function homeLoader() { diff --git a/examples/ssr-data-router/src/entry.client.tsx b/examples/ssr-data-router/src/entry.client.tsx index 5a2f2ca2c1..384c8adc42 100644 --- a/examples/ssr-data-router/src/entry.client.tsx +++ b/examples/ssr-data-router/src/entry.client.tsx @@ -1,14 +1,38 @@ import * as React from "react"; import ReactDOM from "react-dom/client"; -import { createBrowserRouter, RouterProvider } from "react-router-dom"; +import { + createBrowserRouter, + matchRoutes, + RouterProvider, +} from "react-router-dom"; import { routes } from "./App"; -let router = createBrowserRouter(routes); +hydrate(); -ReactDOM.hydrateRoot( - document.getElementById("app"), - - - -); +async function hydrate() { + // Determine if any of the initial routes are lazy + let lazyMatches = matchRoutes(routes, window.location)?.filter( + (m) => m.route.lazy + ); + + // Load the lazy matches and update the routes before creating your router + // so we can hydrate the SSR-rendered content synchronously + if (lazyMatches && lazyMatches?.length > 0) { + await Promise.all( + lazyMatches.map(async (m) => { + let routeModule = await m.route.lazy!(); + Object.assign(m.route, { ...routeModule, lazy: undefined }); + }) + ); + } + + let router = createBrowserRouter(routes); + + ReactDOM.hydrateRoot( + document.getElementById("app")!, + + + + ); +} diff --git a/examples/ssr-data-router/src/entry.server.tsx b/examples/ssr-data-router/src/entry.server.tsx index 5fcf1b44d3..48da92f6df 100644 --- a/examples/ssr-data-router/src/entry.server.tsx +++ b/examples/ssr-data-router/src/entry.server.tsx @@ -1,15 +1,15 @@ import type * as express from "express"; -import { createStaticHandler } from "@remix-run/router"; import * as React from "react"; import ReactDOMServer from "react-dom/server"; import { + createStaticHandler, createStaticRouter, StaticRouterProvider, } from "react-router-dom/server"; import { routes } from "./App"; export async function render(request: express.Request) { - let { query } = createStaticHandler(routes); + let { query, dataRoutes } = createStaticHandler(routes); let remixRequest = createFetchRequest(request); let context = await query(remixRequest); @@ -17,7 +17,7 @@ export async function render(request: express.Request) { throw context; } - let router = createStaticRouter(routes, context); + let router = createStaticRouter(dataRoutes, context); return ReactDOMServer.renderToString( => { + await new Promise((r) => setTimeout(r, 500)); + return { + date: new Date().toISOString(), + }; +}; + +function LazyPage() { + let data = useLoaderData() as LazyLoaderData; + + return ( + <> +

    Lazy Route

    +

    Date from loader: {data.date}

    + + ); +} + +export const element = ; diff --git a/package.json b/package.json index 7ededba1ec..9523a77952 100644 --- a/package.json +++ b/package.json @@ -105,13 +105,13 @@ }, "filesize": { "packages/router/dist/router.umd.min.js": { - "none": "41.7 kB" + "none": "43.1 kB" }, "packages/react-router/dist/react-router.production.min.js": { "none": "13 kB" }, "packages/react-router/dist/umd/react-router.production.min.js": { - "none": "15 kB" + "none": "15.1 kB" }, "packages/react-router-dom/dist/react-router-dom.production.min.js": { "none": "11.6 kB" diff --git a/packages/react-router-dom/__tests__/data-browser-router-test.tsx b/packages/react-router-dom/__tests__/data-browser-router-test.tsx index b9a659ffec..5ec90eaa6f 100644 --- a/packages/react-router-dom/__tests__/data-browser-router-test.tsx +++ b/packages/react-router-dom/__tests__/data-browser-router-test.tsx @@ -20,6 +20,7 @@ import { createBrowserRouter, createHashRouter, isRouteErrorResponse, + matchRoutes, useLoaderData, useActionData, useRouteError, @@ -89,6 +90,17 @@ function testDomRouter( return ; } + async function waitForRouterInitialize(router) { + return await new Promise((resolve) => { + let unsubscribe = router.subscribe((updatedState) => { + if (updatedState.initialized) { + unsubscribe(); + resolve(router); + } + }); + }); + } + describe(`Router: ${name}`, () => { let consoleWarn: jest.SpyInstance; let consoleError: jest.SpyInstance; @@ -391,7 +403,62 @@ function testDomRouter( `); }); - it("does not render fallbackElement if no data fetch is required", async () => { + it("renders fallbackElement while first data fetch and lazy route load happens", async () => { + let fooDefer = createDeferred(); + let { container } = render( + } + > + }> + { + return { + loader: () => fooDefer.promise, + element: , + }; + }} + /> + } /> + + + ); + + function FallbackElement() { + return

    Loading...

    ; + } + + function Foo() { + let data = useLoaderData(); + return

    Foo:{data?.message}

    ; + } + + function Bar() { + return

    Bar Heading

    ; + } + + expect(getHtml(container)).toMatchInlineSnapshot(` + "
    +

    + Loading... +

    +
    " + `); + + fooDefer.resolve({ message: "From Lazy Foo Loader" }); + await waitFor(() => screen.getByText("Foo:From Lazy Foo Loader")); + expect(getHtml(container)).toMatchInlineSnapshot(` + "
    +

    + Foo: + From Lazy Foo Loader +

    +
    " + `); + }); + + it("does not render fallbackElement if no data fetch or lazy loading is required", async () => { let fooDefer = createDeferred(); let { container } = render( { + let barDefer = createDeferred(); + + let { container } = render( + + }> + } /> + ({ + loader: () => barDefer.promise, + element: , + })} + /> + + + ); + + function Layout() { + let navigation = useNavigation(); + return ( +
    + Link to Bar +
    +

    {navigation.state}

    + +
    +
    + ); + } + + function Foo() { + return

    Foo

    ; + } + function Bar() { + let data = useLoaderData(); + return

    {data?.message}

    ; + } + + expect(getHtml(container.querySelector("#output"))) + .toMatchInlineSnapshot(` + "
    +

    + idle +

    +

    + Foo +

    +
    " + `); + + fireEvent.click(screen.getByText("Link to Bar")); + expect(getHtml(container.querySelector("#output"))) + .toMatchInlineSnapshot(` + "
    +

    + loading +

    +

    + Foo +

    +
    " + `); + + barDefer.resolve({ message: "Bar Loader" }); + await waitFor(() => screen.getByText("idle")); + expect(getHtml(container.querySelector("#output"))) + .toMatchInlineSnapshot(` + "
    +

    + idle +

    +

    + Bar Loader +

    +
    " + `); + }); + it("handles link navigations with preventScrollReset", async () => { let { container } = render( @@ -825,6 +977,111 @@ function testDomRouter( `); }); + it("executes lazy route actions/loaders on useSubmit navigations", async () => { + let loaderDefer = createDeferred(); + let actionDefer = createDeferred(); + + let { container } = render( + + ({ + action: () => actionDefer.promise, + loader: () => loaderDefer.promise, + element: , + })} + /> + + ); + + function Home() { + let data = useLoaderData(); + let actionData = useActionData(); + let navigation = useNavigation(); + let submit = useSubmit(); + let formRef = React.useRef(); + return ( +
    +
    + +
    + +
    +

    {navigation.state}

    +

    {data}

    +

    {actionData}

    +
    + +
    + ); + } + + await waitFor(() => screen.getByText("idle")); + expect(getHtml(container.querySelector("#output"))) + .toMatchInlineSnapshot(` + "
    +

    + idle +

    +

    +

    +

    " + `); + + fireEvent.click(screen.getByText("Submit Form")); + await waitFor(() => screen.getByText("submitting")); + expect(getHtml(container.querySelector("#output"))) + .toMatchInlineSnapshot(` + "
    +

    + submitting +

    +

    +

    +

    " + `); + + actionDefer.resolve("Action Data"); + await waitFor(() => screen.getByText("loading")); + expect(getHtml(container.querySelector("#output"))) + .toMatchInlineSnapshot(` + "
    +

    + loading +

    +

    +

    + Action Data +

    +
    " + `); + + loaderDefer.resolve("Loader Data"); + await waitFor(() => screen.getByText("idle")); + expect(getHtml(container.querySelector("#output"))) + .toMatchInlineSnapshot(` + "
    +

    + idle +

    +

    + Loader Data +

    +

    + Action Data +

    +
    " + `); + }); + it("executes route loaders on
    navigations", async () => { let loaderDefer = createDeferred(); let actionDefer = createDeferred(); @@ -879,7 +1136,197 @@ function testDomRouter( " `); - fireEvent.click(screen.getByText("Submit Form")); + fireEvent.click(screen.getByText("Submit Form")); + await waitFor(() => screen.getByText("loading")); + expect(getHtml(container.querySelector("#output"))) + .toMatchInlineSnapshot(` + "
    +

    + loading +

    +

    +

    +

    " + `); + + loaderDefer.resolve("Loader Data"); + await waitFor(() => screen.getByText("idle")); + expect(getHtml(container.querySelector("#output"))) + .toMatchInlineSnapshot(` + "
    +

    + idle +

    +

    + Loader Data:value +

    +

    +

    " + `); + }); + + it("executes lazy route loaders on navigations", async () => { + let loaderDefer = createDeferred(); + let actionDefer = createDeferred(); + + let { container } = render( + + ({ + action: () => actionDefer.promise, + loader: async ({ request }) => { + let resolvedValue = await loaderDefer.promise; + let urlParam = new URL( + `https://remix.run${request.url}` + ).searchParams.get("test"); + return `${resolvedValue}:${urlParam}`; + }, + element: , + })} + /> + + ); + + function Home() { + let data = useLoaderData(); + let actionData = useActionData(); + let navigation = useNavigation(); + return ( +
    + + + + +
    +

    {navigation.state}

    +

    {data}

    +

    {actionData}

    +
    + +
    + ); + } + + await waitFor(() => screen.getByText("idle")); + expect(getHtml(container.querySelector("#output"))) + .toMatchInlineSnapshot(` + "
    +

    + idle +

    +

    +

    +

    " + `); + + fireEvent.click(screen.getByText("Submit Form")); + await waitFor(() => screen.getByText("loading")); + expect(getHtml(container.querySelector("#output"))) + .toMatchInlineSnapshot(` + "
    +

    + loading +

    +

    +

    +

    " + `); + + loaderDefer.resolve("Loader Data"); + await waitFor(() => screen.getByText("idle")); + expect(getHtml(container.querySelector("#output"))) + .toMatchInlineSnapshot(` + "
    +

    + idle +

    +

    + Loader Data:value +

    +

    +

    " + `); + }); + + it("executes route actions/loaders on
    navigations", async () => { + let loaderDefer = createDeferred(); + let actionDefer = createDeferred(); + + let { container } = render( + + { + let resolvedValue = await actionDefer.promise; + let formData = await request.formData(); + return `${resolvedValue}:${formData.get("test")}`; + }} + loader={() => loaderDefer.promise} + element={} + /> + + ); + + function Home() { + let data = useLoaderData(); + let actionData = useActionData(); + let navigation = useNavigation(); + return ( +
    + + + + +
    +

    {navigation.state}

    +

    {data}

    +

    {actionData}

    +
    + +
    + ); + } + + expect(getHtml(container.querySelector("#output"))) + .toMatchInlineSnapshot(` + "
    +

    + idle +

    +

    +

    +

    " + `); + + fireEvent.click(screen.getByText("Submit Form")); + await waitFor(() => screen.getByText("submitting")); + expect(getHtml(container.querySelector("#output"))) + .toMatchInlineSnapshot(` + "
    +

    + submitting +

    +

    +

    +

    " + `); + + actionDefer.resolve("Action Data"); await waitFor(() => screen.getByText("loading")); expect(getHtml(container.querySelector("#output"))) .toMatchInlineSnapshot(` @@ -890,7 +1337,9 @@ function testDomRouter( loading

    -

    +

    + Action Data:value +

    " `); @@ -905,14 +1354,16 @@ function testDomRouter( idle

    - Loader Data:value + Loader Data +

    +

    + Action Data:value

    -

    " `); }); - it("executes route actions/loaders on

    navigations", async () => { + it("executes lazy route actions/loaders on navigations", async () => { let loaderDefer = createDeferred(); let actionDefer = createDeferred(); @@ -920,13 +1371,15 @@ function testDomRouter( { - let resolvedValue = await actionDefer.promise; - let formData = await request.formData(); - return `${resolvedValue}:${formData.get("test")}`; - }} - loader={() => loaderDefer.promise} - element={} + lazy={async () => ({ + action: async ({ request }) => { + let resolvedValue = await actionDefer.promise; + let formData = await request.formData(); + return `${resolvedValue}:${formData.get("test")}`; + }, + loader: () => loaderDefer.promise, + element: , + })} /> ); @@ -951,6 +1404,7 @@ function testDomRouter( ); } + await waitFor(() => screen.getByText("idle")); expect(getHtml(container.querySelector("#output"))) .toMatchInlineSnapshot(` "
    { + let router = createTestRouter( + createRoutesFromElements( + }> + ({ + element: , + errorElement: , + })} + /> + + ), + { + window: getWindow("/child"), + hydrationData: { + loaderData: { + "0": "parent data", + }, + actionData: { + "0": "parent action", + }, + errors: { + "0-0": new Error("Kaboom 💥"), + }, + }, + } + ); + + await waitForRouterInitialize(router); + + let { container } = render(); + + function Comp() { + let data = useLoaderData(); + let actionData = useActionData(); + let navigation = useNavigation(); + return ( +
    + {data} + {actionData} + {navigation.state} + +
    + ); + } + + function ErrorBoundary() { + let error = useRouteError(); + return

    {error.message}

    ; + } + + expect(getHtml(container)).toMatchInlineSnapshot(` + "
    +
    + parent data + parent action + idle +

    + Kaboom 💥 +

    +
    +
    " + `); + }); + + it("renders hydration errors on lazy leaf elements with preloading", async () => { + let routes = createRoutesFromElements( + }> + ({ + element: , + errorElement: , + })} + /> + + ); + + let lazyMatches = matchRoutes(routes, { pathname: "/child" })?.filter( + (m) => m.route.lazy + ); + + if (lazyMatches && lazyMatches?.length > 0) { + await Promise.all( + lazyMatches.map(async (m) => { + let routeModule = await m.route.lazy!(); + Object.assign(m.route, { ...routeModule, lazy: undefined }); + }) + ); + } + + let router = createTestRouter(routes, { + window: getWindow("/child"), + hydrationData: { + loaderData: { + "0": "parent data", + }, + actionData: { + "0": "parent action", + }, + errors: { + "0-0": new Error("Kaboom 💥"), + }, + }, + }); + + let { container } = render(); + + function Comp() { + let data = useLoaderData(); + let actionData = useActionData(); + let navigation = useNavigation(); + return ( +
    + {data} + {actionData} + {navigation.state} + +
    + ); + } + + function ErrorBoundary() { + let error = useRouteError(); + return

    {error.message}

    ; + } + + expect(getHtml(container)).toMatchInlineSnapshot(` + "
    +
    + parent data + parent action + idle +

    + Kaboom 💥 +

    +
    +
    " + `); + }); + it("renders hydration errors on parent elements", async () => { let { container } = render( { + let router = createTestRouter( + createRoutesFromElements( + ({ + element: , + errorElement: , + })} + > + } /> + + ), + { + window: getWindow("/child"), + hydrationData: { + loaderData: {}, + actionData: null, + errors: { + "0": new Error("Kaboom 💥"), + }, + }, + } + ); + + // Wait for lazy() to load + await new Promise((resolve) => { + let unsubscribe = router.subscribe((updatedState) => { + if (updatedState.initialized) { + unsubscribe(); + resolve(router); + } + }); + }); + + let { container } = render(); + + function Comp() { + let data = useLoaderData(); + let actionData = useActionData(); + let navigation = useNavigation(); + return ( +
    + {data} + {actionData} + {navigation.state} + +
    + ); + } + + function ErrorBoundary() { + let error = useRouteError(); + return

    {error.message}

    ; + } + + expect(getHtml(container)).toMatchInlineSnapshot(` + "
    +

    + Kaboom 💥 +

    +
    " + `); + }); + it("renders navigation errors on leaf elements", async () => { let fooDefer = createDeferred(); let barDefer = createDeferred(); @@ -4164,6 +4825,91 @@ function testDomRouter(
    " `); }); + + // This test ensures that when manual routes are used, we add hasErrorBoundary + it("renders navigation errors on lazy leaf elements (when using manual route objects)", async () => { + let lazyDefer = createDeferred(); + let barDefer = createDeferred(); + + let routes = [ + { + path: "/", + element: , + children: [ + { + path: "foo", + element:

    Foo

    , + }, + { + path: "bar", + lazy: async () => lazyDefer.promise, + }, + ], + }, + ]; + + router = createTestRouter(routes, { window: getWindow("/foo") }); + let { container } = render(); + + function Layout() { + let navigation = useNavigation(); + return ( +
    + Link to Bar +
    +

    {navigation.state}

    + +
    +
    + ); + } + + function Bar() { + let data = useLoaderData(); + return

    Bar:{data?.message}

    ; + } + function BarError() { + let error = useRouteError(); + return

    Bar Error:{error.message}

    ; + } + + expect(getHtml(container.querySelector("#output"))) + .toMatchInlineSnapshot(` + "
    +

    + idle +

    +

    + Foo +

    +
    " + `); + + fireEvent.click(screen.getByText("Link to Bar")); + await lazyDefer.resolve({ + loader: () => barDefer.promise, + element: , + errorElement: , + }); + barDefer.reject(new Error("Kaboom!")); + await waitFor(() => screen.getByText("idle")); + expect(getHtml(container.querySelector("#output"))) + .toMatchInlineSnapshot(` + "
    +

    + idle +

    +

    + Bar Error: + Kaboom! +

    +
    " + `); + }); }); }); } diff --git a/packages/react-router-dom/__tests__/data-static-router-test.tsx b/packages/react-router-dom/__tests__/data-static-router-test.tsx index 9c6856a62b..301e6685a6 100644 --- a/packages/react-router-dom/__tests__/data-static-router-test.tsx +++ b/packages/react-router-dom/__tests__/data-static-router-test.tsx @@ -4,8 +4,7 @@ import * as React from "react"; import * as ReactDOMServer from "react-dom/server"; -import type { StaticHandlerContext } from "@remix-run/router"; -import { json, createStaticHandler } from "@remix-run/router"; +import { json } from "@remix-run/router"; import { Link, Outlet, @@ -13,7 +12,9 @@ import { useLocation, useMatches, } from "react-router-dom"; +import type { StaticHandlerContext } from "react-router-dom/server"; import { + createStaticHandler, createStaticRouter, StaticRouterProvider, } from "react-router-dom/server"; @@ -166,6 +167,153 @@ describe("A ", () => { ]); }); + it("renders an initialized router with lazy routes", async () => { + let hooksData1: { + location: ReturnType; + loaderData: ReturnType; + matches: ReturnType; + }; + let hooksData2: { + location: ReturnType; + loaderData: ReturnType; + matches: ReturnType; + }; + + function HooksChecker1() { + hooksData1 = { + location: useLocation(), + loaderData: useLoaderData(), + matches: useMatches(), + }; + return ; + } + + function HooksChecker2() { + hooksData2 = { + location: useLocation(), + loaderData: useLoaderData(), + matches: useMatches(), + }; + return ( + <> +

    👋

    + Other + + ); + } + + let routes = [ + { + path: "the", + lazy: async () => ({ + loader: () => ({ + key1: "value1", + }), + element: , + handle: "1", + }), + children: [ + { + path: "path", + lazy: async () => ({ + loader: () => ({ + key2: "value2", + }), + element: , + handle: "2", + }), + }, + ], + }, + ]; + let { query, dataRoutes } = createStaticHandler(routes); + + let context = (await query( + new Request("http://localhost/the/path?the=query#the-hash", { + signal: new AbortController().signal, + }) + )) as StaticHandlerContext; + + let html = ReactDOMServer.renderToStaticMarkup( + + + + ); + expect(html).toMatch("

    👋

    "); + expect(html).toMatch(''); + + // @ts-expect-error + expect(hooksData1.location).toEqual({ + pathname: "/the/path", + search: "?the=query", + hash: "#the-hash", + state: null, + key: expect.any(String), + }); + // @ts-expect-error + expect(hooksData1.loaderData).toEqual({ + key1: "value1", + }); + // @ts-expect-error + expect(hooksData1.matches).toEqual([ + { + data: { + key1: "value1", + }, + handle: "1", + id: "0", + params: {}, + pathname: "/the", + }, + { + data: { + key2: "value2", + }, + handle: "2", + id: "0-0", + params: {}, + pathname: "/the/path", + }, + ]); + + // @ts-expect-error + expect(hooksData2.location).toEqual({ + pathname: "/the/path", + search: "?the=query", + hash: "#the-hash", + state: null, + key: expect.any(String), + }); + // @ts-expect-error + expect(hooksData2.loaderData).toEqual({ + key2: "value2", + }); + // @ts-expect-error + expect(hooksData2.matches).toEqual([ + { + data: { + key1: "value1", + }, + handle: "1", + id: "0", + params: {}, + pathname: "/the", + }, + { + data: { + key2: "value2", + }, + handle: "2", + id: "0-0", + params: {}, + pathname: "/the/path", + }, + ]); + }); + it("renders an initialized router with a basename", async () => { let location: ReturnType; @@ -273,6 +421,64 @@ describe("A ", () => { ); }); + it("renders hydration data from lazy routes by default", async () => { + let routes = [ + { + // provide unique id here but not below, to ensure we add where needed + id: "the", + path: "the", + lazy: async () => ({ + loader: () => ({ + key1: "value1", + }), + element: , + }), + children: [ + { + path: "path", + lazy: async () => ({ + loader: () => ({ + key2: "value2", + }), + element:

    👋

    , + }), + }, + ], + }, + ]; + let { query, dataRoutes } = createStaticHandler(routes); + + let context = (await query( + new Request("http://localhost/the/path", { + signal: new AbortController().signal, + }) + )) as StaticHandlerContext; + + let html = ReactDOMServer.renderToStaticMarkup( + + + + ); + expect(html).toMatch("

    👋

    "); + + let expectedJsonString = JSON.stringify( + JSON.stringify({ + loaderData: { + the: { key1: "value1" }, + "0-0": { key2: "value2" }, + }, + actionData: null, + errors: null, + }) + ); + expect(html).toMatch( + `` + ); + }); + it("escapes HTML tags in serialized hydration data", async () => { let routes = [ { @@ -353,6 +559,57 @@ describe("A ", () => { ); }); + it("serializes ErrorResponse instances from lazy routes", async () => { + let routes = [ + { + path: "/", + lazy: async () => ({ + loader: () => { + throw json( + { not: "found" }, + { status: 404, statusText: "Not Found" } + ); + }, + }), + }, + ]; + let { query, dataRoutes } = createStaticHandler(routes); + + let context = (await query( + new Request("http://localhost/", { + signal: new AbortController().signal, + }) + )) as StaticHandlerContext; + + let html = ReactDOMServer.renderToStaticMarkup( + + + + ); + + let expectedJsonString = JSON.stringify( + JSON.stringify({ + loaderData: {}, + actionData: null, + errors: { + "0": { + status: 404, + statusText: "Not Found", + internal: false, + data: { not: "found" }, + __type: "RouteErrorResponse", + }, + }, + }) + ); + expect(html).toMatch( + `` + ); + }); + it("serializes Error instances", async () => { let routes = [ { @@ -397,6 +654,52 @@ describe("A ", () => { ); }); + it("serializes Error instances from lazy routes", async () => { + let routes = [ + { + path: "/", + lazy: async () => ({ + loader: () => { + throw new Error("oh no"); + }, + }), + }, + ]; + let { query, dataRoutes } = createStaticHandler(routes); + + let context = (await query( + new Request("http://localhost/", { + signal: new AbortController().signal, + }) + )) as StaticHandlerContext; + + let html = ReactDOMServer.renderToStaticMarkup( + + + + ); + + // stack is stripped by default from SSR errors + let expectedJsonString = JSON.stringify( + JSON.stringify({ + loaderData: {}, + actionData: null, + errors: { + "0": { + message: "oh no", + __type: "Error", + }, + }, + }) + ); + expect(html).toMatch( + `` + ); + }); + it("supports a nonce prop", async () => { let routes = [ { @@ -720,6 +1023,46 @@ describe("A ", () => { expect(context._deepestRenderedBoundaryId).toBe("0-0"); }); + it("tracks the deepest boundary during render with lazy routes", async () => { + let routes = [ + { + path: "/", + lazy: async () => ({ + element: , + errorElement:

    Error

    , + }), + children: [ + { + index: true, + lazy: async () => ({ + element:

    👋

    , + errorElement:

    Error

    , + }), + }, + ], + }, + ]; + + let { query, dataRoutes } = createStaticHandler(routes); + let context = (await query( + new Request("http://localhost/", { + signal: new AbortController().signal, + }) + )) as StaticHandlerContext; + + let html = ReactDOMServer.renderToStaticMarkup( + + + + ); + expect(html).toMatchInlineSnapshot(`"

    👋

    "`); + expect(context._deepestRenderedBoundaryId).toBe("0-0"); + }); + it("tracks only boundaries that expose an errorElement", async () => { let routes = [ { @@ -753,5 +1096,42 @@ describe("A ", () => { expect(html).toMatchInlineSnapshot(`"

    👋

    "`); expect(context._deepestRenderedBoundaryId).toBe("0"); }); + + it("tracks only boundaries that expose an errorElement with lazy routes", async () => { + let routes = [ + { + path: "/", + lazy: async () => ({ + element: , + errorElement:

    Error

    , + }), + children: [ + { + index: true, + element:

    👋

    , + }, + ], + }, + ]; + + let { query, dataRoutes } = createStaticHandler(routes); + let context = (await query( + new Request("http://localhost/", { + signal: new AbortController().signal, + }) + )) as StaticHandlerContext; + + let html = ReactDOMServer.renderToStaticMarkup( + + + + ); + expect(html).toMatchInlineSnapshot(`"

    👋

    "`); + expect(context._deepestRenderedBoundaryId).toBe("0"); + }); }); }); diff --git a/packages/react-router-dom/__tests__/exports-test.tsx b/packages/react-router-dom/__tests__/exports-test.tsx index bfffbf208f..91392a2f85 100644 --- a/packages/react-router-dom/__tests__/exports-test.tsx +++ b/packages/react-router-dom/__tests__/exports-test.tsx @@ -1,10 +1,18 @@ import * as ReactRouter from "react-router"; import * as ReactRouterDOM from "react-router-dom"; +let nonReExportedKeys = new Set(["UNSAFE_detectErrorBoundary"]); + describe("react-router-dom", () => { for (let key in ReactRouter) { - it(`re-exports ${key} from react-router`, () => { - expect(ReactRouterDOM[key]).toBe(ReactRouter[key]); - }); + if (!nonReExportedKeys.has(key)) { + it(`re-exports ${key} from react-router`, () => { + expect(ReactRouterDOM[key]).toBe(ReactRouter[key]); + }); + } else { + it(`does not re-export ${key} from react-router`, () => { + expect(ReactRouterDOM[key]).toBe(undefined); + }); + } } }); diff --git a/packages/react-router-dom/index.tsx b/packages/react-router-dom/index.tsx index e7884bc18b..3bb811ca74 100644 --- a/packages/react-router-dom/index.tsx +++ b/packages/react-router-dom/index.tsx @@ -23,7 +23,7 @@ import { UNSAFE_DataRouterStateContext as DataRouterStateContext, UNSAFE_NavigationContext as NavigationContext, UNSAFE_RouteContext as RouteContext, - UNSAFE_enhanceManualRouteObjects as enhanceManualRouteObjects, + UNSAFE_detectErrorBoundary as detectErrorBoundary, } from "react-router"; import type { BrowserHistory, @@ -40,10 +40,11 @@ import { createRouter, createBrowserHistory, createHashHistory, - UNSAFE_invariant as invariant, joinPaths, stripBasename, ErrorResponse, + UNSAFE_invariant as invariant, + UNSAFE_warning as warning, } from "@remix-run/router"; import type { @@ -87,6 +88,7 @@ export type { IndexRouteObject, IndexRouteProps, JsonFunction, + LazyRouteFunction, LayoutRouteProps, LoaderFunction, LoaderFunctionArgs, @@ -186,7 +188,6 @@ export { UNSAFE_NavigationContext, UNSAFE_LocationContext, UNSAFE_RouteContext, - UNSAFE_enhanceManualRouteObjects, } from "react-router"; //#endregion @@ -210,7 +211,8 @@ export function createBrowserRouter( basename: opts?.basename, history: createBrowserHistory({ window: opts?.window }), hydrationData: opts?.hydrationData || parseHydrationData(), - routes: enhanceManualRouteObjects(routes), + routes, + detectErrorBoundary, }).initialize(); } @@ -226,7 +228,8 @@ export function createHashRouter( basename: opts?.basename, history: createHashHistory({ window: opts?.window }), hydrationData: opts?.hydrationData || parseHydrationData(), - routes: enhanceManualRouteObjects(routes), + routes, + detectErrorBoundary, }).initialize(); } @@ -1232,6 +1235,8 @@ function useScrollRestoration({ } } +export { useScrollRestoration as UNSAFE_useScrollRestoration }; + /** * Setup a callback to be fired on the window's `beforeunload` event. This is * useful for saving some data to `window.localStorage` just before the page @@ -1308,27 +1313,3 @@ function usePrompt({ when, message }: { when: boolean; message: string }) { export { usePrompt as unstable_usePrompt }; //#endregion - -//////////////////////////////////////////////////////////////////////////////// -//#region Utils -//////////////////////////////////////////////////////////////////////////////// - -function warning(cond: boolean, message: string): void { - if (!cond) { - // eslint-disable-next-line no-console - if (typeof console !== "undefined") console.warn(message); - - try { - // Welcome to debugging React Router! - // - // This error is thrown as a convenience so you can more easily - // find the source for a warning that appears in the console by - // enabling "pause on exceptions" in your JavaScript debugger. - throw new Error(message); - // eslint-disable-next-line no-empty - } catch (e) {} - } -} -//#endregion - -export { useScrollRestoration as UNSAFE_useScrollRestoration }; diff --git a/packages/react-router-dom/server.tsx b/packages/react-router-dom/server.tsx index 6de29148e2..ea2d5f0497 100644 --- a/packages/react-router-dom/server.tsx +++ b/packages/react-router-dom/server.tsx @@ -1,10 +1,11 @@ import * as React from "react"; import type { - AgnosticDataRouteObject, Path, RevalidationState, Router as RemixRouter, StaticHandlerContext, + CreateStaticHandlerOptions as RouterCreateStaticHandlerOptions, + UNSAFE_RouteManifest as RouteManifest, } from "@remix-run/router"; import { IDLE_BLOCKER, @@ -13,14 +14,10 @@ import { Action, UNSAFE_invariant as invariant, isRouteErrorResponse, + createStaticHandler as routerCreateStaticHandler, UNSAFE_convertRoutesToDataRoutes as convertRoutesToDataRoutes, } from "@remix-run/router"; -import type { - DataRouteObject, - Location, - RouteObject, - To, -} from "react-router-dom"; +import type { Location, RouteObject, To } from "react-router-dom"; import { Routes } from "react-router-dom"; import { createPath, @@ -28,7 +25,6 @@ import { Router, UNSAFE_DataRouterContext as DataRouterContext, UNSAFE_DataRouterStateContext as DataRouterStateContext, - UNSAFE_enhanceManualRouteObjects as enhanceManualRouteObjects, } from "react-router-dom"; export interface StaticRouterProps { @@ -72,6 +68,8 @@ export function StaticRouter({ ); } +export { StaticHandlerContext }; + export interface StaticRouterProviderProps { context: StaticHandlerContext; router: RemixRouter; @@ -208,38 +206,43 @@ function getStatelessNavigator() { }; } -// Temporary manifest generation - we should optimize this by combining the -// tree-walks between convertRoutesToDataRoutes, enhanceManualRouteObjects, -// and generateManifest. -// Also look into getting rid of `route as AgnosticDataRouteObject` down below? -function generateManifest( - routes: DataRouteObject[], - manifest: Map = new Map() -): Map { - routes.forEach((route) => { - manifest.set(route.id, route); - if (route.children) { - generateManifest(route.children, manifest); - } +let detectErrorBoundary = (route: RouteObject) => Boolean(route.errorElement); + +type CreateStaticHandlerOptions = Omit< + RouterCreateStaticHandlerOptions, + "detectErrorBoundary" +>; + +export function createStaticHandler( + routes: RouteObject[], + opts?: CreateStaticHandlerOptions +) { + return routerCreateStaticHandler(routes, { + ...opts, + detectErrorBoundary, }); - return manifest; } export function createStaticRouter( routes: RouteObject[], context: StaticHandlerContext ): RemixRouter { - let dataRoutes = convertRoutesToDataRoutes(enhanceManualRouteObjects(routes)); - let manifest = generateManifest(dataRoutes); + let manifest: RouteManifest = {}; + let dataRoutes = convertRoutesToDataRoutes( + routes, + detectErrorBoundary, + undefined, + manifest + ); // Because our context matches may be from a framework-agnostic set of // routes passed to createStaticHandler(), we update them here with our // newly created/enhanced data routes let matches = context.matches.map((match) => { - let route = manifest.get(match.route.id) || match.route; + let route = manifest[match.route.id] || match.route; return { ...match, - route: route as AgnosticDataRouteObject, + route, }; }); diff --git a/packages/react-router-native/__tests__/exports-test.tsx b/packages/react-router-native/__tests__/exports-test.tsx index 62915c62ef..0c219c4205 100644 --- a/packages/react-router-native/__tests__/exports-test.tsx +++ b/packages/react-router-native/__tests__/exports-test.tsx @@ -1,10 +1,18 @@ import * as ReactRouter from "react-router"; import * as ReactRouterNative from "react-router-native"; +let nonReExportedKeys = new Set(["UNSAFE_detectErrorBoundary"]); + describe("react-router-native", () => { for (let key in ReactRouter) { - it(`re-exports ${key} from react-router`, () => { - expect(ReactRouterNative[key]).toBe(ReactRouter[key]); - }); + if (!nonReExportedKeys.has(key)) { + it(`re-exports ${key} from react-router`, () => { + expect(ReactRouterNative[key]).toBe(ReactRouter[key]); + }); + } else { + it(`does not re-export ${key} from react-router`, () => { + expect(ReactRouterNative[key]).toBe(undefined); + }); + } } }); diff --git a/packages/react-router-native/index.tsx b/packages/react-router-native/index.tsx index 07335e33ef..d3a890d6d8 100644 --- a/packages/react-router-native/index.tsx +++ b/packages/react-router-native/index.tsx @@ -32,6 +32,7 @@ export type { IndexRouteObject, IndexRouteProps, JsonFunction, + LazyRouteFunction, LayoutRouteProps, LoaderFunction, LoaderFunctionArgs, @@ -131,7 +132,6 @@ export { UNSAFE_NavigationContext, UNSAFE_LocationContext, UNSAFE_RouteContext, - UNSAFE_enhanceManualRouteObjects, } from "react-router"; //////////////////////////////////////////////////////////////////////////////// diff --git a/packages/react-router/__tests__/createRoutesFromChildren-test.tsx b/packages/react-router/__tests__/createRoutesFromChildren-test.tsx index fafd02c141..c1ab7fc109 100644 --- a/packages/react-router/__tests__/createRoutesFromChildren-test.tsx +++ b/packages/react-router/__tests__/createRoutesFromChildren-test.tsx @@ -17,10 +17,14 @@ describe("creating routes from JSX", () => { ).toMatchInlineSnapshot(` [ { + "Component": undefined, + "ErrorBoundary": undefined, "action": undefined, "caseSensitive": undefined, "children": [ { + "Component": undefined, + "ErrorBoundary": undefined, "action": undefined, "caseSensitive": undefined, "element":

    @@ -31,11 +35,14 @@ describe("creating routes from JSX", () => { "hasErrorBoundary": false, "id": "0-0", "index": undefined, + "lazy": undefined, "loader": undefined, "path": "home", "shouldRevalidate": undefined, }, { + "Component": undefined, + "ErrorBoundary": undefined, "action": undefined, "caseSensitive": undefined, "element":

    @@ -46,15 +53,20 @@ describe("creating routes from JSX", () => { "hasErrorBoundary": false, "id": "0-1", "index": undefined, + "lazy": undefined, "loader": undefined, "path": "about", "shouldRevalidate": undefined, }, { + "Component": undefined, + "ErrorBoundary": undefined, "action": undefined, "caseSensitive": undefined, "children": [ { + "Component": undefined, + "ErrorBoundary": undefined, "action": undefined, "caseSensitive": undefined, "element":

    @@ -65,11 +77,14 @@ describe("creating routes from JSX", () => { "hasErrorBoundary": false, "id": "0-2-0", "index": true, + "lazy": undefined, "loader": undefined, "path": undefined, "shouldRevalidate": undefined, }, { + "Component": undefined, + "ErrorBoundary": undefined, "action": undefined, "caseSensitive": undefined, "element":

    @@ -80,6 +95,7 @@ describe("creating routes from JSX", () => { "hasErrorBoundary": false, "id": "0-2-1", "index": undefined, + "lazy": undefined, "loader": undefined, "path": ":id", "shouldRevalidate": undefined, @@ -91,6 +107,7 @@ describe("creating routes from JSX", () => { "hasErrorBoundary": false, "id": "0-2", "index": undefined, + "lazy": undefined, "loader": undefined, "path": "users", "shouldRevalidate": undefined, @@ -102,6 +119,7 @@ describe("creating routes from JSX", () => { "hasErrorBoundary": false, "id": "0", "index": undefined, + "lazy": undefined, "loader": undefined, "path": "/", "shouldRevalidate": undefined, @@ -133,10 +151,14 @@ describe("creating routes from JSX", () => { ).toMatchInlineSnapshot(` [ { + "Component": undefined, + "ErrorBoundary": undefined, "action": undefined, "caseSensitive": undefined, "children": [ { + "Component": undefined, + "ErrorBoundary": undefined, "action": undefined, "caseSensitive": undefined, "element":

    @@ -147,15 +169,20 @@ describe("creating routes from JSX", () => { "hasErrorBoundary": false, "id": "0-0", "index": undefined, + "lazy": undefined, "loader": [Function], "path": "home", "shouldRevalidate": [Function], }, { + "Component": undefined, + "ErrorBoundary": undefined, "action": undefined, "caseSensitive": undefined, "children": [ { + "Component": undefined, + "ErrorBoundary": undefined, "action": [Function], "caseSensitive": undefined, "element":

    @@ -166,6 +193,7 @@ describe("creating routes from JSX", () => { "hasErrorBoundary": false, "id": "0-1-0", "index": true, + "lazy": undefined, "loader": undefined, "path": undefined, "shouldRevalidate": undefined, @@ -177,6 +205,7 @@ describe("creating routes from JSX", () => { "hasErrorBoundary": false, "id": "0-1", "index": undefined, + "lazy": undefined, "loader": undefined, "path": "users", "shouldRevalidate": undefined, @@ -190,6 +219,7 @@ describe("creating routes from JSX", () => { "hasErrorBoundary": true, "id": "0", "index": undefined, + "lazy": undefined, "loader": undefined, "path": "/", "shouldRevalidate": undefined, diff --git a/packages/react-router/__tests__/data-memory-router-test.tsx b/packages/react-router/__tests__/data-memory-router-test.tsx index b641de6df1..1d47ae7400 100644 --- a/packages/react-router/__tests__/data-memory-router-test.tsx +++ b/packages/react-router/__tests__/data-memory-router-test.tsx @@ -79,7 +79,7 @@ describe("", () => { router = null; }); - it("renders the first route that matches the URL", () => { + it("renders the first route that matches the URL (element)", () => { let { container } = render( Home

    } /> @@ -95,6 +95,22 @@ describe("", () => { `); }); + it("renders the first route that matches the URL (Component)", () => { + let { container } = render( + +

    Home

    } /> +
    + ); + + expect(getHtml(container)).toMatchInlineSnapshot(` + "
    +

    + Home +

    +
    " + `); + }); + it("supports a `routes` prop instead of children", () => { let routes = [ { @@ -1045,7 +1061,7 @@ describe("", () => { }); describe("errors", () => { - it("renders hydration errors on leaf elements", async () => { + it("renders hydration errors on leaf elements using errorElement", async () => { let { container } = render( ", () => { `); }); + it("renders hydration errors on leaf elements using ErrorBoundary", async () => { + let { container } = render( + + }> + } + ErrorBoundary={() =>

    {useRouteError()?.message}

    } + /> +
    +
    + ); + + function Comp() { + let data = useLoaderData(); + let actionData = useActionData(); + let navigation = useNavigation(); + return ( +
    + {data} + {actionData} + {navigation.state} + +
    + ); + } + + expect(getHtml(container)).toMatchInlineSnapshot(` + "
    +
    + parent data + parent action + idle +

    + Kaboom 💥 +

    +
    +
    " + `); + }); + it("renders hydration errors on parent elements", async () => { let { container } = render( ", () => { `); }); - it("renders navigation errors on leaf elements", async () => { + it("renders navigation errors on leaf elements using errorElement", async () => { let fooDefer = createDeferred(); let barDefer = createDeferred(); @@ -1290,6 +1360,146 @@ describe("", () => { `); }); + it("renders navigation errors on leaf elements using ErrorBoundary", async () => { + let fooDefer = createDeferred(); + let barDefer = createDeferred(); + + let { container } = render( + + }> + fooDefer.promise} + element={} + ErrorBoundary={FooError} + /> + barDefer.promise} + element={} + ErrorBoundary={BarError} + /> + + + ); + + function Layout() { + let navigation = useNavigation(); + return ( +
    + Link to Foo + Link to Bar +

    {navigation.state}

    + +
    + ); + } + + function Foo() { + let data = useLoaderData(); + return

    Foo:{data?.message}

    ; + } + function FooError() { + let error = useRouteError(); + return

    Foo Error:{error.message}

    ; + } + function Bar() { + let data = useLoaderData(); + return

    Bar:{data?.message}

    ; + } + function BarError() { + let error = useRouteError(); + return

    Bar Error:{error.message}

    ; + } + + expect(getHtml(container)).toMatchInlineSnapshot(` + "
    +
    + + Link to Foo + + + Link to Bar + +

    + idle +

    +

    + Foo: + hydrated from foo +

    +
    +
    " + `); + + fireEvent.click(screen.getByText("Link to Bar")); + barDefer.reject(new Error("Kaboom!")); + await waitFor(() => screen.getByText("idle")); + expect(getHtml(container)).toMatchInlineSnapshot(` + "
    +
    + + Link to Foo + + + Link to Bar + +

    + idle +

    +

    + Bar Error: + Kaboom! +

    +
    +
    " + `); + + fireEvent.click(screen.getByText("Link to Foo")); + fooDefer.reject(new Error("Kaboom!")); + await waitFor(() => screen.getByText("idle")); + expect(getHtml(container)).toMatchInlineSnapshot(` + "
    +
    + + Link to Foo + + + Link to Bar + +

    + idle +

    +

    + Foo Error: + Kaboom! +

    +
    +
    " + `); + }); + it("renders navigation errors on parent elements", async () => { let fooDefer = createDeferred(); let barDefer = createDeferred(); @@ -1533,9 +1743,9 @@ describe("", () => { - errorElement + ErrorBoundary - props on  + prop on  @@ -1648,9 +1858,9 @@ describe("", () => { - errorElement + ErrorBoundary - props on  + prop on  @@ -1893,9 +2103,9 @@ describe("", () => { - errorElement + ErrorBoundary - props on  + prop on  @@ -2077,9 +2287,9 @@ describe("", () => { - errorElement + ErrorBoundary - props on  + prop on  diff --git a/packages/react-router/index.ts b/packages/react-router/index.ts index 3d55ed2e22..a0d2ca597a 100644 --- a/packages/react-router/index.ts +++ b/packages/react-router/index.ts @@ -20,6 +20,7 @@ import type { ShouldRevalidateFunction, To, InitialEntry, + LazyRouteFunction, } from "@remix-run/router"; import { AbortedDeferredError, @@ -36,6 +37,7 @@ import { parsePath, redirect, resolvePath, + UNSAFE_warning as warning, } from "@remix-run/router"; import type { @@ -52,7 +54,6 @@ import type { RouterProviderProps, } from "./lib/components"; import { - enhanceManualRouteObjects, createRoutesFromChildren, renderMatches, Await, @@ -126,6 +127,7 @@ export type { IndexRouteObject, IndexRouteProps, JsonFunction, + LazyRouteFunction, LayoutRouteProps, LoaderFunction, LoaderFunctionArgs, @@ -204,6 +206,29 @@ export { useRoutes, }; +function detectErrorBoundary(route: RouteObject) { + if (__DEV__) { + if (route.Component && route.element) { + warning( + false, + "You should not include both `Component` and `element` on your route - " + + "`element` will be ignored." + ); + } + if (route.ErrorBoundary && route.errorElement) { + warning( + false, + "You should not include both `ErrorBoundary` and `errorElement` on your route - " + + "`errorElement` will be ignored." + ); + } + } + + // Note: this check also occurs in createRoutesFromChildren so update + // there if you change this + return Boolean(route.ErrorBoundary) || Boolean(route.errorElement); +} + export function createMemoryRouter( routes: RouteObject[], opts?: { @@ -220,7 +245,8 @@ export function createMemoryRouter( initialIndex: opts?.initialIndex, }), hydrationData: opts?.hydrationData, - routes: enhanceManualRouteObjects(routes), + routes, + detectErrorBoundary, }).initialize(); } @@ -244,5 +270,5 @@ export { RouteContext as UNSAFE_RouteContext, DataRouterContext as UNSAFE_DataRouterContext, DataRouterStateContext as UNSAFE_DataRouterStateContext, - enhanceManualRouteObjects as UNSAFE_enhanceManualRouteObjects, + detectErrorBoundary as UNSAFE_detectErrorBoundary, }; diff --git a/packages/react-router/lib/components.tsx b/packages/react-router/lib/components.tsx index 1b5daa76f1..ca34e2494a 100644 --- a/packages/react-router/lib/components.tsx +++ b/packages/react-router/lib/components.tsx @@ -7,6 +7,7 @@ import type { Router as RemixRouter, RouterState, To, + LazyRouteFunction, } from "@remix-run/router"; import { Action as NavigationType, @@ -15,7 +16,7 @@ import { UNSAFE_invariant as invariant, parsePath, stripBasename, - warning, + UNSAFE_warning as warning, } from "@remix-run/router"; import { useSyncExternalStore as useSyncExternalStoreShim } from "./use-sync-external-store-shim"; @@ -235,6 +236,7 @@ export interface PathRouteProps { caseSensitive?: NonIndexRouteObject["caseSensitive"]; path?: NonIndexRouteObject["path"]; id?: NonIndexRouteObject["id"]; + lazy?: LazyRouteFunction; loader?: NonIndexRouteObject["loader"]; action?: NonIndexRouteObject["action"]; hasErrorBoundary?: NonIndexRouteObject["hasErrorBoundary"]; @@ -244,6 +246,8 @@ export interface PathRouteProps { children?: React.ReactNode; element?: React.ReactNode | null; errorElement?: React.ReactNode | null; + Component?: React.ComponentType | null; + ErrorBoundary?: React.ComponentType | null; } export interface LayoutRouteProps extends PathRouteProps {} @@ -252,6 +256,7 @@ export interface IndexRouteProps { caseSensitive?: IndexRouteObject["caseSensitive"]; path?: IndexRouteObject["path"]; id?: IndexRouteObject["id"]; + lazy?: LazyRouteFunction; loader?: IndexRouteObject["loader"]; action?: IndexRouteObject["action"]; hasErrorBoundary?: IndexRouteObject["hasErrorBoundary"]; @@ -261,6 +266,8 @@ export interface IndexRouteProps { children?: undefined; element?: React.ReactNode | null; errorElement?: React.ReactNode | null; + Component?: React.ComponentType | null; + ErrorBoundary?: React.ComponentType | null; } export type RouteProps = PathRouteProps | LayoutRouteProps | IndexRouteProps; @@ -585,14 +592,19 @@ export function createRoutesFromChildren( id: element.props.id || treePath.join("-"), caseSensitive: element.props.caseSensitive, element: element.props.element, + Component: element.props.Component, index: element.props.index, path: element.props.path, loader: element.props.loader, action: element.props.action, errorElement: element.props.errorElement, - hasErrorBoundary: element.props.errorElement != null, + ErrorBoundary: element.props.ErrorBoundary, + hasErrorBoundary: + element.props.ErrorBoundary != null || + element.props.errorElement != null, shouldRevalidate: element.props.shouldRevalidate, handle: element.props.handle, + lazy: element.props.lazy, }; if (element.props.children) { @@ -616,23 +628,3 @@ export function renderMatches( ): React.ReactElement | null { return _renderMatches(matches); } - -/** - * @private - * Walk the route tree and add hasErrorBoundary if it's not provided, so that - * users providing manual route arrays can just specify errorElement - */ -export function enhanceManualRouteObjects( - routes: RouteObject[] -): RouteObject[] { - return routes.map((route) => { - let routeClone = { ...route }; - if (routeClone.hasErrorBoundary == null) { - routeClone.hasErrorBoundary = routeClone.errorElement != null; - } - if (routeClone.children) { - routeClone.children = enhanceManualRouteObjects(routeClone.children); - } - return routeClone; - }); -} diff --git a/packages/react-router/lib/context.ts b/packages/react-router/lib/context.ts index e29e01ef1b..da1340c7aa 100644 --- a/packages/react-router/lib/context.ts +++ b/packages/react-router/lib/context.ts @@ -9,6 +9,7 @@ import type { StaticHandlerContext, To, TrackedPromise, + LazyRouteFunction, } from "@remix-run/router"; import type { Action as NavigationType } from "@remix-run/router"; @@ -27,6 +28,9 @@ export interface IndexRouteObject { children?: undefined; element?: React.ReactNode | null; errorElement?: React.ReactNode | null; + Component?: React.ComponentType | null; + ErrorBoundary?: React.ComponentType | null; + lazy?: LazyRouteFunction; } export interface NonIndexRouteObject { @@ -42,6 +46,9 @@ export interface NonIndexRouteObject { children?: RouteObject[]; element?: React.ReactNode | null; errorElement?: React.ReactNode | null; + Component?: React.ComponentType | null; + ErrorBoundary?: React.ComponentType | null; + lazy?: LazyRouteFunction; } export type RouteObject = IndexRouteObject | NonIndexRouteObject; diff --git a/packages/react-router/lib/hooks.tsx b/packages/react-router/lib/hooks.tsx index 951b434670..b3588cd9c5 100644 --- a/packages/react-router/lib/hooks.tsx +++ b/packages/react-router/lib/hooks.tsx @@ -20,8 +20,8 @@ import { matchRoutes, parsePath, resolveTo, - warning, UNSAFE_getPathContributingMatches as getPathContributingMatches, + UNSAFE_warning as warning, } from "@remix-run/router"; import type { @@ -392,9 +392,11 @@ export function useRoutes( warning( matches == null || - matches[matches.length - 1].route.element !== undefined, - `Matched leaf route at location "${location.pathname}${location.search}${location.hash}" does not have an element. ` + - `This means it will render an with a null value by default resulting in an "empty" page.` + matches[matches.length - 1].route.element !== undefined || + matches[matches.length - 1].route.Component !== undefined, + `Matched leaf route at location "${location.pathname}${location.search}${location.hash}" ` + + `does not have an element or Component. This means it will render an with a ` + + `null value by default resulting in an "empty" page.` ); } @@ -452,7 +454,7 @@ export function useRoutes( return renderedMatches; } -function DefaultErrorElement() { +function DefaultErrorComponent() { let error = useRouteError(); let message = isRouteErrorResponse(error) ? `${error.status} ${error.statusText}` @@ -472,7 +474,7 @@ function DefaultErrorElement() {

    You can provide a way better UX than this when your app throws errors by providing your own  - errorElement props on  + ErrorBoundary prop on  <Route>

    @@ -583,7 +585,7 @@ function RenderedRoute({ routeContext, match, children }: RenderedRouteProps) { dataRouterContext && dataRouterContext.static && dataRouterContext.staticContext && - match.route.errorElement + (match.route.errorElement || match.route.ErrorBoundary) ) { dataRouterContext.staticContext._deepestRenderedBoundaryId = match.route.id; } @@ -631,23 +633,39 @@ export function _renderMatches( return renderedMatches.reduceRight((outlet, match, index) => { let error = match.route.id ? errors?.[match.route.id] : null; // Only data routers handle errors - let errorElement = dataRouterState - ? match.route.errorElement || - : null; + let errorElement: React.ReactNode | null = null; + if (dataRouterState) { + if (match.route.ErrorBoundary) { + errorElement = ; + } else if (match.route.errorElement) { + errorElement = match.route.errorElement; + } else { + errorElement = ; + } + } let matches = parentMatches.concat(renderedMatches.slice(0, index + 1)); - let getChildren = () => ( - - {error - ? errorElement - : match.route.element !== undefined - ? match.route.element - : outlet} - - ); + let getChildren = () => { + let children: React.ReactNode = outlet; + if (error) { + children = errorElement; + } else if (match.route.Component) { + children = ; + } else if (match.route.element) { + children = match.route.element; + } + return ( + + ); + }; // Only wrap in an error boundary within data router usages when we have an - // errorElement on this route. Otherwise let it bubble up to an ancestor - // errorElement - return dataRouterState && (match.route.errorElement || index === 0) ? ( + // ErrorBoundary/errorElement on this route. Otherwise let it bubble up to + // an ancestor ErrorBoundary/errorElement + return dataRouterState && + (match.route.ErrorBoundary || match.route.errorElement || index === 0) ? ( { pathnameBase: "/", route: { id: "0", + hasErrorBoundary: false, path: "/", }, }, diff --git a/packages/router/__tests__/router-test.ts b/packages/router/__tests__/router-test.ts index 9d94824d7a..aeec20eaaa 100644 --- a/packages/router/__tests__/router-test.ts +++ b/packages/router/__tests__/router-test.ts @@ -51,6 +51,7 @@ type TestIndexRouteObject = Pick< AgnosticIndexRouteObject, "id" | "index" | "path" | "shouldRevalidate" > & { + lazy?: boolean; loader?: boolean; action?: boolean; hasErrorBoundary?: boolean; @@ -60,6 +61,7 @@ type TestNonIndexRouteObject = Pick< AgnosticNonIndexRouteObject, "id" | "index" | "path" | "shouldRevalidate" > & { + lazy?: boolean; loader?: boolean; action?: boolean; hasErrorBoundary?: boolean; @@ -99,6 +101,7 @@ type Helpers = InternalHelpers & { // control and assertions over the loaders/actions type NavigationHelpers = { navigationId: number; + lazy: Record; loaders: Record; actions: Record; }; @@ -159,6 +162,29 @@ function isRedirect(result: any) { ); } +function findRouteById( + routes: AgnosticDataRouteObject[], + id: string +): AgnosticDataRouteObject { + let foundRoute: AgnosticDataRouteObject | null = null; + for (const route of routes) { + if (route.id === id) { + foundRoute = route; + break; + } + if (route.children) { + foundRoute = findRouteById(route.children, id); + if (foundRoute) { + break; + } + } + } + + invariant(foundRoute, `Route not found with id "${id}".`); + + return foundRoute; +} + interface CustomMatchers { trackedPromise(data?: any, error?: any, aborted?: boolean): R; deferredData( @@ -296,11 +322,37 @@ function setup({ return _routes.map((r) => { let enhancedRoute: AgnosticDataRouteObject = { ...r, + lazy: undefined, loader: undefined, action: undefined, children: undefined, id: r.id || `route-${guid++}`, }; + if (r.lazy) { + // @ts-expect-error + enhancedRoute.lazy = (args) => { + // This is maybe not ideal, but works for now :). We don't really know + // which key to look for so we resolve the earliest one we find and + // remove it such that subsequent calls resolve the later ones. + const sortedIds = [ + activeLoaderNavigationId, + activeActionNavigationId, + activeLoaderFetchId, + activeActionFetchId, + ].sort(); + let helperKey = sortedIds + .map((id) => `${id}:lazy:${r.id}`) + .find((k) => activeHelpers.has(k)); + invariant(helperKey != null, `No helperKey found`); + let helpers = activeHelpers.get(helperKey); + invariant(helpers, `No helpers found for: ${helperKey}`); + helpers.stub(args); + return helpers.dfd.promise.then((def) => { + activeHelpers.delete(helperKey!); + return def; + }); + }; + } if (r.loader) { enhancedRoute.loader = (args) => { let navigationId = @@ -429,7 +481,7 @@ function setup({ let routeHelpers: Helpers = { get signal() { - return internalHelpers._signal; + return internalHelpers._signal!; }, // Note: This spread has to come _after_ the above getter, otherwise // we lose the getter nature of it somewhere in the babel/typescript @@ -486,6 +538,12 @@ function setup({ let matches = matchRoutes(inFlightRoutes || currentRouter.routes, href); // Generate helpers for all route matches that contain loaders + let lazyHelpers = getHelpers( + (matches || []).filter((m) => m.route.lazy), + navigationId, + (routeId, helpers) => + activeHelpers.set(`${navigationId}:lazy:${routeId}`, helpers) + ); let loaderHelpers = getHelpers( (matches || []).filter((m) => m.route.loader), navigationId, @@ -507,6 +565,7 @@ function setup({ return { navigationId, + lazy: lazyHelpers, loaders: loaderHelpers, actions: actionHelpers, }; @@ -538,6 +597,7 @@ function setup({ invariant(currentRouter, "No currentRouter available"); return currentRouter.getFetcher(key); }, + lazy: {}, loaders: {}, actions: {}, }; @@ -566,6 +626,12 @@ function setup({ } // Generate helpers for all route matches that contain loaders + let lazyHelpers = getHelpers( + match.route.lazy ? [match] : [], + navigationId, + (routeId, helpers) => + activeHelpers.set(`${navigationId}:lazy:${routeId}`, helpers) + ); let loaderHelpers = getHelpers( activeLoaderMatches.filter((m) => m.route.loader), navigationId, @@ -586,6 +652,7 @@ function setup({ invariant(currentRouter, "No currentRouter available"); return currentRouter.getFetcher(key); }, + lazy: lazyHelpers, loaders: loaderHelpers, actions: actionHelpers, }; @@ -786,6 +853,25 @@ function initializeTmTest(init?: { ...(init?.url ? { initialEntries: [init.url] } : {}), }); } + +function createRequest(path: string, opts?: RequestInit) { + return new Request(`http://localhost${path}`, { + signal: new AbortController().signal, + ...opts, + }); +} + +function createSubmitRequest(path: string, opts?: RequestInit) { + let searchParams = new URLSearchParams(); + searchParams.append("key", "value"); + + return createRequest(path, { + method: "post", + body: searchParams, + ...opts, + }); +} + //#endregion /////////////////////////////////////////////////////////////////////////////// @@ -11521,122 +11607,1171 @@ describe("a router", () => { }); }); - describe("ssr", () => { - const SSR_ROUTES = [ + describe("lazily loaded route modules", () => { + const LAZY_ROUTES: TestRouteObject[] = [ { - id: "index", + id: "root", path: "/", - loader: () => "INDEX LOADER", - }, - { - id: "parent", - path: "/parent", - loader: () => "PARENT LOADER", - action: () => "PARENT ACTION", children: [ { - id: "parentIndex", - index: true, - loader: () => "PARENT INDEX LOADER", - action: () => "PARENT INDEX ACTION", - }, - { - id: "child", - path: "child", - loader: () => "CHILD LOADER", - action: () => "CHILD ACTION", - }, - { - id: "json", - path: "json", - loader: () => json({ type: "loader" }), - action: () => json({ type: "action" }), + id: "lazy", + path: "/lazy", + lazy: true, }, + ], + }, + ]; + + describe("happy path", () => { + it("fetches lazy route modules on loading navigation", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let A = await t.navigate("/lazy"); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("loading"); + + let dfd = createDeferred(); + A.lazy.lazy.resolve({ + loader: () => dfd.promise, + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("loading"); + + await dfd.resolve("LAZY LOADER"); + + expect(t.router.state.location.pathname).toBe("/lazy"); + expect(t.router.state.navigation.state).toBe("idle"); + expect(t.router.state.loaderData).toEqual({ + lazy: "LAZY LOADER", + }); + }); + + it("fetches lazy route modules on submission navigation", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let A = await t.navigate("/lazy", { + formMethod: "post", + formData: createFormData({}), + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("submitting"); + + let actionDfd = createDeferred(); + let loaderDfd = createDeferred(); + A.lazy.lazy.resolve({ + action: () => actionDfd.promise, + loader: () => loaderDfd.promise, + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("submitting"); + + await actionDfd.resolve("LAZY ACTION"); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("loading"); + expect(t.router.state.actionData).toEqual({ + lazy: "LAZY ACTION", + }); + expect(t.router.state.loaderData).toEqual({}); + + await loaderDfd.resolve("LAZY LOADER"); + expect(t.router.state.location.pathname).toBe("/lazy"); + expect(t.router.state.navigation.state).toBe("idle"); + expect(t.router.state.actionData).toEqual({ + lazy: "LAZY ACTION", + }); + expect(t.router.state.loaderData).toEqual({ + lazy: "LAZY LOADER", + }); + }); + + it("fetches lazy route modules on fetcher.load", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let key = "key"; + let A = await t.fetch("/lazy", key); + expect(t.router.state.fetchers.get(key)?.state).toBe("loading"); + + let loaderDfd = createDeferred(); + await A.lazy.lazy.resolve({ + loader: () => loaderDfd.promise, + }); + expect(t.router.state.fetchers.get(key)?.state).toBe("loading"); + + await loaderDfd.resolve("LAZY LOADER"); + expect(t.router.state.fetchers.get(key)?.state).toBe("idle"); + expect(t.router.state.fetchers.get(key)?.data).toBe("LAZY LOADER"); + }); + + it("fetches lazy route modules on fetcher.submit", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let key = "key"; + let A = await t.fetch("/lazy", key, { + formMethod: "post", + formData: createFormData({}), + }); + expect(t.router.state.fetchers.get(key)?.state).toBe("submitting"); + + let actionDfd = createDeferred(); + await A.lazy.lazy.resolve({ + action: () => actionDfd.promise, + }); + expect(t.router.state.fetchers.get(key)?.state).toBe("submitting"); + + await actionDfd.resolve("LAZY ACTION"); + expect(t.router.state.fetchers.get(key)?.state).toBe("idle"); + expect(t.router.state.fetchers.get(key)?.data).toBe("LAZY ACTION"); + }); + + it("fetches lazy route modules on staticHandler.query()", async () => { + let { query } = createStaticHandler([ { - id: "deferred", - path: "deferred", - loader: ({ request }) => { - if (new URL(request.url).searchParams.has("reject")) { - return defer({ - critical: "loader", - lazy: new Promise((_, r) => - setTimeout(() => r(new Error("broken!")), 10) - ), - }); - } - if (new URL(request.url).searchParams.has("status")) { - return defer( - { - critical: "loader", - lazy: new Promise((r) => setTimeout(() => r("lazy"), 10)), - }, - { status: 201, headers: { "X-Custom": "yes" } } - ); - } - return defer({ - critical: "loader", - lazy: new Promise((r) => setTimeout(() => r("lazy"), 10)), - }); + id: "lazy", + path: "/lazy", + lazy: async () => { + await tick(); + return { + async loader() { + return json({ value: "LAZY LOADER" }); + }, + }; }, - action: () => - defer({ - critical: "critical", - lazy: new Promise((r) => setTimeout(() => r("lazy"), 10)), - }), - }, - { - id: "error", - path: "error", - loader: () => Promise.reject("ERROR LOADER ERROR"), - action: () => Promise.reject("ERROR ACTION ERROR"), }, + ]); + + let context = await query(createRequest("/lazy")); + invariant( + !(context instanceof Response), + "Expected a StaticContext instance" + ); + expect(context.loaderData).toEqual({ lazy: { value: "LAZY LOADER" } }); + }); + + it("fetches lazy route modules on staticHandler.queryRoute()", async () => { + let { queryRoute } = createStaticHandler([ { - id: "errorBoundary", - path: "error-boundary", - hasErrorBoundary: true, - loader: () => Promise.reject("ERROR BOUNDARY LOADER ERROR"), - action: () => Promise.reject("ERROR BOUNDARY ACTION ERROR"), + id: "lazy", + path: "/lazy", + lazy: async () => { + await tick(); + return { + async loader() { + return json({ value: "LAZY LOADER" }); + }, + }; + }, }, - ], - }, - { - id: "redirect", - path: "/redirect", - loader: () => redirect("/"), - }, - ]; + ]); - // Regardless of if the URL is internal or external - all absolute URL - // responses should return untouched during SSR so the browser can handle - // them - let ABSOLUTE_URLS = [ - "http://localhost/", - "https://localhost/about", - "http://remix.run/blog", - "https://remix.run/blog", - "//remix.run/blog", - "app://whatever", - "mailto:hello@remix.run", - "web+remix:whatever", - ]; + let response = await queryRoute(createRequest("/lazy")); + let data = await response.json(); + expect(data).toEqual({ value: "LAZY LOADER" }); + }); + }); + + describe("statically defined fields", () => { + it("prefers statically defined loader over lazily defined loader", async () => { + let consoleWarn = jest.spyOn(console, "warn"); + let t = setup({ + routes: [ + { + id: "lazy", + path: "/lazy", + loader: true, + lazy: true, + }, + ], + }); + + let A = await t.navigate("/lazy"); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("loading"); + // Execute in parallel + expect(A.loaders.lazy.stub).toHaveBeenCalled(); + expect(A.lazy.lazy.stub).toHaveBeenCalled(); + + let lazyLoaderStub = jest.fn(() => "LAZY LOADER"); + await A.lazy.lazy.resolve({ + loader: lazyLoaderStub, + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("loading"); + + await A.loaders.lazy.resolve("STATIC LOADER"); + expect(t.router.state.location.pathname).toBe("/lazy"); + expect(t.router.state.navigation.state).toBe("idle"); + expect(t.router.state.loaderData).toEqual({ + lazy: "STATIC LOADER", + }); + + let lazyRoute = findRouteById(t.router.routes, "lazy"); + expect(lazyRoute.lazy).toBeUndefined(); + expect(lazyRoute.loader).toEqual(expect.any(Function)); + expect(lazyRoute.loader).not.toBe(lazyLoaderStub); + expect(lazyLoaderStub).not.toHaveBeenCalled(); - function createRequest(path: string, opts?: RequestInit) { - return new Request(`http://localhost${path}`, { - signal: new AbortController().signal, - ...opts, + expect(consoleWarn).toHaveBeenCalledTimes(1); + expect(consoleWarn.mock.calls[0][0]).toMatchInlineSnapshot( + `"Route "lazy" has a static property "loader" defined but its lazy function is also returning a value for this property. The lazy route property "loader" will be ignored."` + ); + consoleWarn.mockReset(); }); - } - function createSubmitRequest(path: string, opts?: RequestInit) { - let searchParams = new URLSearchParams(); - searchParams.append("key", "value"); + it("prefers statically defined action over lazily loaded action", async () => { + let consoleWarn = jest.spyOn(console, "warn"); + let t = setup({ + routes: [ + { + id: "lazy", + path: "/lazy", + action: true, + lazy: true, + }, + ], + }); + + let A = await t.navigate("/lazy", { + formMethod: "post", + formData: createFormData({}), + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("submitting"); + // Execute in parallel + expect(A.actions.lazy.stub).toHaveBeenCalled(); + expect(A.lazy.lazy.stub).toHaveBeenCalled(); + + let lazyActionStub = jest.fn(() => "LAZY ACTION"); + let loaderDfd = createDeferred(); + await A.lazy.lazy.resolve({ + action: lazyActionStub, + loader: () => loaderDfd.promise, + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("submitting"); + + await A.actions.lazy.resolve("STATIC ACTION"); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("loading"); + expect(t.router.state.actionData).toEqual({ + lazy: "STATIC ACTION", + }); + expect(t.router.state.loaderData).toEqual({}); + + await loaderDfd.resolve("LAZY LOADER"); + expect(t.router.state.location.pathname).toBe("/lazy"); + expect(t.router.state.navigation.state).toBe("idle"); + expect(t.router.state.actionData).toEqual({ + lazy: "STATIC ACTION", + }); + expect(t.router.state.loaderData).toEqual({ + lazy: "LAZY LOADER", + }); - return createRequest(path, { - method: "post", - body: searchParams, - ...opts, + let lazyRoute = findRouteById(t.router.routes, "lazy"); + expect(lazyRoute.lazy).toBeUndefined(); + expect(lazyRoute.action).toEqual(expect.any(Function)); + expect(lazyRoute.action).not.toBe(lazyActionStub); + expect(lazyActionStub).not.toHaveBeenCalled(); + + expect(consoleWarn).toHaveBeenCalledTimes(1); + expect(consoleWarn.mock.calls[0][0]).toMatchInlineSnapshot( + `"Route "lazy" has a static property "action" defined but its lazy function is also returning a value for this property. The lazy route property "action" will be ignored."` + ); + consoleWarn.mockReset(); }); - } + + it("prefers statically defined action/loader over lazily defined action/loader", async () => { + let consoleWarn = jest.spyOn(console, "warn"); + let t = setup({ + routes: [ + { + id: "lazy", + path: "/lazy", + action: true, + loader: true, + lazy: true, + }, + ], + }); + + let A = await t.navigate("/lazy", { + formMethod: "post", + formData: createFormData({}), + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("submitting"); + + let lazyActionStub = jest.fn(() => "LAZY ACTION"); + let lazyLoaderStub = jest.fn(() => "LAZY LOADER"); + await A.lazy.lazy.resolve({ + action: lazyActionStub, + loader: lazyLoaderStub, + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("submitting"); + + await A.actions.lazy.resolve("STATIC ACTION"); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("loading"); + expect(t.router.state.actionData).toEqual({ + lazy: "STATIC ACTION", + }); + expect(t.router.state.loaderData).toEqual({}); + + await A.loaders.lazy.resolve("STATIC LOADER"); + expect(t.router.state.location.pathname).toBe("/lazy"); + expect(t.router.state.navigation.state).toBe("idle"); + expect(t.router.state.actionData).toEqual({ + lazy: "STATIC ACTION", + }); + expect(t.router.state.loaderData).toEqual({ + lazy: "STATIC LOADER", + }); + + let lazyRoute = findRouteById(t.router.routes, "lazy"); + expect(lazyRoute.lazy).toBeUndefined(); + expect(lazyRoute.action).toEqual(expect.any(Function)); + expect(lazyRoute.loader).toEqual(expect.any(Function)); + expect(lazyRoute.action).not.toBe(lazyActionStub); + expect(lazyRoute.loader).not.toBe(lazyLoaderStub); + expect(lazyActionStub).not.toHaveBeenCalled(); + expect(lazyLoaderStub).not.toHaveBeenCalled(); + + expect(consoleWarn).toHaveBeenCalledTimes(2); + expect(consoleWarn.mock.calls[0][0]).toMatchInlineSnapshot( + `"Route "lazy" has a static property "action" defined but its lazy function is also returning a value for this property. The lazy route property "action" will be ignored."` + ); + expect(consoleWarn.mock.calls[1][0]).toMatchInlineSnapshot( + `"Route "lazy" has a static property "loader" defined but its lazy function is also returning a value for this property. The lazy route property "loader" will be ignored."` + ); + consoleWarn.mockReset(); + }); + + it("prefers statically defined loader over lazily defined loader (staticHandler.query)", async () => { + let consoleWarn = jest.spyOn(console, "warn"); + let lazyLoaderStub = jest.fn(async () => { + await tick(); + return json({ value: "LAZY LOADER" }); + }); + + let { query } = createStaticHandler([ + { + id: "lazy", + path: "/lazy", + loader: async () => { + await tick(); + return json({ value: "STATIC LOADER" }); + }, + lazy: async () => { + await tick(); + return { + loader: lazyLoaderStub, + }; + }, + }, + ]); + + let context = await query(createRequest("/lazy")); + invariant( + !(context instanceof Response), + "Expected a StaticContext instance" + ); + expect(context.loaderData).toEqual({ + lazy: { value: "STATIC LOADER" }, + }); + expect(lazyLoaderStub).not.toHaveBeenCalled(); + + expect(consoleWarn).toHaveBeenCalledTimes(1); + expect(consoleWarn.mock.calls[0][0]).toMatchInlineSnapshot( + `"Route "lazy" has a static property "loader" defined but its lazy function is also returning a value for this property. The lazy route property "loader" will be ignored."` + ); + consoleWarn.mockReset(); + }); + + it("prefers statically defined loader over lazily defined loader (staticHandler.queryRoute)", async () => { + let consoleWarn = jest.spyOn(console, "warn"); + let lazyLoaderStub = jest.fn(async () => { + await tick(); + return json({ value: "LAZY LOADER" }); + }); + + let { query } = createStaticHandler([ + { + id: "lazy", + path: "/lazy", + loader: async () => { + await tick(); + return json({ value: "STATIC LOADER" }); + }, + lazy: async () => { + await tick(); + return { + loader: lazyLoaderStub, + }; + }, + }, + ]); + + let context = await query(createRequest("/lazy")); + invariant( + !(context instanceof Response), + "Expected a StaticContext instance" + ); + expect(context.loaderData).toEqual({ + lazy: { value: "STATIC LOADER" }, + }); + expect(lazyLoaderStub).not.toHaveBeenCalled(); + + expect(consoleWarn).toHaveBeenCalledTimes(1); + expect(consoleWarn.mock.calls[0][0]).toMatchInlineSnapshot( + `"Route "lazy" has a static property "loader" defined but its lazy function is also returning a value for this property. The lazy route property "loader" will be ignored."` + ); + consoleWarn.mockReset(); + }); + }); + + describe("interruptions", () => { + it("runs lazily loaded route loader even if lazy() is interrupted", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let A = await t.navigate("/lazy"); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("loading"); + + await t.navigate("/"); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("idle"); + + let lazyLoaderStub = jest.fn(() => "LAZY LOADER"); + await A.lazy.lazy.resolve({ + loader: lazyLoaderStub, + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("idle"); + expect(lazyLoaderStub).toHaveBeenCalledTimes(1); + + // Ensure the lazy route object update still happened + let lazyRoute = findRouteById(t.router.routes, "lazy"); + expect(lazyRoute.lazy).toBeUndefined(); + expect(lazyRoute.loader).toBe(lazyLoaderStub); + }); + + it("runs lazily loaded route action even if lazy() is interrupted", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let A = await t.navigate("/lazy", { + formMethod: "post", + formData: createFormData({}), + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("submitting"); + + await t.navigate("/"); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("idle"); + + let lazyActionStub = jest.fn(() => "LAZY ACTION"); + let lazyLoaderStub = jest.fn(() => "LAZY LOADER"); + await A.lazy.lazy.resolve({ + action: lazyActionStub, + loader: lazyLoaderStub, + }); + + let lazyRoute = findRouteById(t.router.routes, "lazy"); + expect(lazyActionStub).toHaveBeenCalledTimes(1); + expect(lazyLoaderStub).not.toHaveBeenCalled(); + expect(lazyRoute.lazy).toBeUndefined(); + expect(lazyRoute.action).toBe(lazyActionStub); + expect(lazyRoute.loader).toBe(lazyLoaderStub); + }); + + it("runs lazily loaded route loader on fetcher.load() even if lazy() is interrupted", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let key = "key"; + let A = await t.fetch("/lazy", key); + expect(t.router.state.fetchers.get(key)?.state).toBe("loading"); + + let B = await t.fetch("/lazy", key); + expect(t.router.state.fetchers.get(key)?.state).toBe("loading"); + + // Resolve B's lazy route first + let loaderDfdB = createDeferred(); + let lazyloaderStubB = jest.fn(() => loaderDfdB.promise); + await B.lazy.lazy.resolve({ + loader: lazyloaderStubB, + }); + expect(t.router.state.fetchers.get(key)?.state).toBe("loading"); + + // Resolve A's lazy route after B + let loaderDfdA = createDeferred(); + let lazyLoaderStubA = jest.fn(() => loaderDfdA.promise); + await A.lazy.lazy.resolve({ + loader: lazyLoaderStubA, + }); + expect(t.router.state.fetchers.get(key)?.state).toBe("loading"); + + await loaderDfdA.resolve("LAZY LOADER A"); + await loaderDfdB.resolve("LAZY LOADER B"); + + expect(t.router.state.fetchers.get(key)?.state).toBe("idle"); + expect(t.router.state.fetchers.get(key)?.data).toBe("LAZY LOADER B"); + expect(lazyLoaderStubA).not.toHaveBeenCalled(); + expect(lazyloaderStubB).toHaveBeenCalledTimes(2); + }); + + it("runs lazily loaded route action on fetcher.submit() even if lazy() is interrupted", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let key = "key"; + let A = await t.fetch("/lazy", key, { + formMethod: "post", + formData: createFormData({}), + }); + expect(t.router.state.fetchers.get(key)?.state).toBe("submitting"); + + let B = await t.fetch("/lazy", key, { + formMethod: "post", + formData: createFormData({}), + }); + expect(t.router.state.fetchers.get(key)?.state).toBe("submitting"); + + // Resolve B's lazy route first + let actionDfdB = createDeferred(); + let lazyActionStubB = jest.fn(() => actionDfdB.promise); + await B.lazy.lazy.resolve({ + action: lazyActionStubB, + }); + expect(t.router.state.fetchers.get(key)?.state).toBe("submitting"); + + // Resolve A's lazy route after B + let actionDfdA = createDeferred(); + let lazyActionStubA = jest.fn(() => actionDfdA.promise); + await A.lazy.lazy.resolve({ + action: lazyActionStubA, + }); + expect(t.router.state.fetchers.get(key)?.state).toBe("submitting"); + + await actionDfdA.resolve("LAZY ACTION A"); + await actionDfdB.resolve("LAZY ACTION B"); + + expect(t.router.state.fetchers.get(key)?.state).toBe("idle"); + expect(t.router.state.fetchers.get(key)?.data).toBe("LAZY ACTION B"); + expect(lazyActionStubA).not.toHaveBeenCalled(); + expect(lazyActionStubB).toHaveBeenCalledTimes(2); + }); + + it("uses the first-resolved lazy() execution on repeated loading navigations", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let A = await t.navigate("/lazy"); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("loading"); + + let B = await t.navigate("/lazy"); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("loading"); + + // Resolve B's lazy route first + let loaderDfdB = createDeferred(); + let lazyLoaderStubB = jest.fn(() => loaderDfdB.promise); + await B.lazy.lazy.resolve({ + loader: lazyLoaderStubB, + }); + + // Resolve A's lazy route after B + let loaderDfdA = createDeferred(); + let lazyLoaderStubA = jest.fn(() => loaderDfdA.promise); + await A.lazy.lazy.resolve({ + loader: lazyLoaderStubA, + }); + + await loaderDfdA.resolve("LAZY LOADER A"); + await loaderDfdB.resolve("LAZY LOADER B"); + + expect(t.router.state.location.pathname).toBe("/lazy"); + expect(t.router.state.navigation.state).toBe("idle"); + + expect(t.router.state.loaderData).toEqual({ lazy: "LAZY LOADER B" }); + + expect(lazyLoaderStubA).not.toHaveBeenCalled(); + expect(lazyLoaderStubB).toHaveBeenCalledTimes(2); + }); + + it("uses the first-resolved lazy() execution on repeated submission navigations", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let A = await t.navigate("/lazy", { + formMethod: "post", + formData: createFormData({}), + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("submitting"); + + let B = await t.navigate("/lazy", { + formMethod: "post", + formData: createFormData({}), + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("submitting"); + + // Resolve B's lazy route first + let loaderDfdB = createDeferred(); + let actionDfdB = createDeferred(); + let lazyLoaderStubB = jest.fn(() => loaderDfdB.promise); + let lazyActionStubB = jest.fn(() => actionDfdB.promise); + await B.lazy.lazy.resolve({ + action: lazyActionStubB, + loader: lazyLoaderStubB, + }); + + // Resolve A's lazy route after B + let loaderDfdA = createDeferred(); + let actionDfdA = createDeferred(); + let lazyLoaderStubA = jest.fn(() => loaderDfdA.promise); + let lazyActionStubA = jest.fn(() => actionDfdA.promise); + await A.lazy.lazy.resolve({ + action: lazyActionStubA, + loader: lazyLoaderStubA, + }); + + await actionDfdA.resolve("LAZY ACTION A"); + await loaderDfdA.resolve("LAZY LOADER A"); + await actionDfdB.resolve("LAZY ACTION B"); + await loaderDfdB.resolve("LAZY LOADER B"); + + expect(t.router.state.location.pathname).toBe("/lazy"); + expect(t.router.state.navigation.state).toBe("idle"); + + expect(t.router.state.actionData).toEqual({ lazy: "LAZY ACTION B" }); + expect(t.router.state.loaderData).toEqual({ lazy: "LAZY LOADER B" }); + + expect(lazyActionStubA).not.toHaveBeenCalled(); + expect(lazyLoaderStubA).not.toHaveBeenCalled(); + expect(lazyActionStubB).toHaveBeenCalledTimes(2); + expect(lazyLoaderStubB).toHaveBeenCalledTimes(1); + }); + + it("uses the first-resolved lazy() execution on repeated fetcher.load calls", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let key = "key"; + let A = await t.fetch("/lazy", key); + expect(t.router.state.fetchers.get(key)?.state).toBe("loading"); + + let B = await t.fetch("/lazy", key); + expect(t.router.state.fetchers.get(key)?.state).toBe("loading"); + + // Resolve B's lazy route first + let loaderDfdB = createDeferred(); + let lazyLoaderStubB = jest.fn(() => loaderDfdB.promise); + await B.lazy.lazy.resolve({ + loader: lazyLoaderStubB, + }); + + // Resolve A's lazy route after B + let loaderDfdA = createDeferred(); + let lazyLoaderStubA = jest.fn(() => loaderDfdA.promise); + await A.lazy.lazy.resolve({ + loader: lazyLoaderStubA, + }); + expect(t.router.state.fetchers.get(key)?.state).toBe("loading"); + + await loaderDfdA.resolve("LAZY LOADER A"); + await loaderDfdB.resolve("LAZY LOADER B"); + + expect(t.router.state.fetchers.get(key)?.state).toBe("idle"); + expect(t.router.state.fetchers.get(key)?.data).toBe("LAZY LOADER B"); + expect(lazyLoaderStubA).not.toHaveBeenCalled(); + expect(lazyLoaderStubB).toHaveBeenCalledTimes(2); + }); + }); + + describe("errors", () => { + it("handles errors when failing to load lazy route modules on loading navigation", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let A = await t.navigate("/lazy"); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("loading"); + + await A.lazy.lazy.reject(new Error("LAZY FUNCTION ERROR")); + expect(t.router.state.location.pathname).toBe("/lazy"); + expect(t.router.state.navigation.state).toBe("idle"); + + expect(t.router.state.loaderData).toEqual({}); + expect(t.router.state.errors).toEqual({ + root: new Error("LAZY FUNCTION ERROR"), + }); + }); + + it("handles loader errors from lazy route modules when the route has an error boundary", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let A = await t.navigate("/lazy"); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("loading"); + + let dfd = createDeferred(); + A.lazy.lazy.resolve({ + loader: () => dfd.promise, + hasErrorBoundary: true, + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("loading"); + + await dfd.reject(new Error("LAZY LOADER ERROR")); + + expect(t.router.state.location.pathname).toBe("/lazy"); + expect(t.router.state.navigation.state).toBe("idle"); + expect(t.router.state.errors).toEqual({ + lazy: new Error("LAZY LOADER ERROR"), + }); + }); + + it("bubbles loader errors from in lazy route modules when the route does not specify an error boundary", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let A = await t.navigate("/lazy"); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("loading"); + + let dfd = createDeferred(); + A.lazy.lazy.resolve({ + loader: () => dfd.promise, + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("loading"); + + await dfd.reject(new Error("LAZY LOADER ERROR")); + + expect(t.router.state.location.pathname).toBe("/lazy"); + expect(t.router.state.navigation.state).toBe("idle"); + expect(t.router.state.errors).toEqual({ + root: new Error("LAZY LOADER ERROR"), + }); + }); + + it("bubbles loader errors from lazy route modules when the route specifies hasErrorBoundary:false", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let A = await t.navigate("/lazy"); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("loading"); + + let dfd = createDeferred(); + A.lazy.lazy.resolve({ + loader: () => dfd.promise, + hasErrorBoundary: false, + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("loading"); + + await dfd.reject(new Error("LAZY LOADER ERROR")); + + expect(t.router.state.location.pathname).toBe("/lazy"); + expect(t.router.state.navigation.state).toBe("idle"); + expect(t.router.state.errors).toEqual({ + root: new Error("LAZY LOADER ERROR"), + }); + }); + + it("handles errors when failing to load lazy route modules on submission navigation", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let A = await t.navigate("/lazy", { + formMethod: "post", + formData: createFormData({}), + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("submitting"); + + await A.lazy.lazy.reject(new Error("LAZY FUNCTION ERROR")); + expect(t.router.state.location.pathname).toBe("/lazy"); + expect(t.router.state.navigation.state).toBe("idle"); + + expect(t.router.state.errors).toEqual({ + root: new Error("LAZY FUNCTION ERROR"), + }); + expect(t.router.state.actionData).toEqual(null); + expect(t.router.state.loaderData).toEqual({}); + }); + + it("handles action errors from lazy route modules on submission navigation", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let A = await t.navigate("/lazy", { + formMethod: "post", + formData: createFormData({}), + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("submitting"); + + let actionDfd = createDeferred(); + A.lazy.lazy.resolve({ + action: () => actionDfd.promise, + hasErrorBoundary: true, + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("submitting"); + + await actionDfd.reject(new Error("LAZY ACTION ERROR")); + expect(t.router.state.location.pathname).toBe("/lazy"); + expect(t.router.state.navigation.state).toBe("idle"); + expect(t.router.state.actionData).toEqual(null); + expect(t.router.state.errors).toEqual({ + lazy: new Error("LAZY ACTION ERROR"), + }); + }); + + it("bubbles action errors from lazy route modules when the route specifies hasErrorBoundary:false", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let A = await t.navigate("/lazy", { + formMethod: "post", + formData: createFormData({}), + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("submitting"); + + let actionDfd = createDeferred(); + A.lazy.lazy.resolve({ + action: () => actionDfd.promise, + hasErrorBoundary: false, + }); + expect(t.router.state.location.pathname).toBe("/"); + expect(t.router.state.navigation.state).toBe("submitting"); + + await actionDfd.reject(new Error("LAZY ACTION ERROR")); + expect(t.router.state.location.pathname).toBe("/lazy"); + expect(t.router.state.navigation.state).toBe("idle"); + expect(t.router.state.actionData).toEqual(null); + expect(t.router.state.errors).toEqual({ + root: new Error("LAZY ACTION ERROR"), + }); + }); + + it("handles errors when failing to load lazy route modules on fetcher.load", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let key = "key"; + let A = await t.fetch("/lazy", key); + expect(t.router.state.fetchers.get(key)?.state).toBe("loading"); + + await A.lazy.lazy.reject(new Error("LAZY FUNCTION ERROR")); + expect(t.router.state.fetchers.get(key)).toBeUndefined(); + expect(t.router.state.errors).toEqual({ + root: new Error("LAZY FUNCTION ERROR"), + }); + }); + + it("handles loader errors in lazy route modules on fetcher.load", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let key = "key"; + let A = await t.fetch("/lazy", key); + expect(t.router.state.fetchers.get(key)?.state).toBe("loading"); + + let loaderDfd = createDeferred(); + await A.lazy.lazy.resolve({ + loader: () => loaderDfd.promise, + }); + expect(t.router.state.fetchers.get(key)?.state).toBe("loading"); + + await loaderDfd.reject(new Error("LAZY LOADER ERROR")); + expect(t.router.state.fetchers.get(key)).toBeUndefined(); + expect(t.router.state.errors).toEqual({ + root: new Error("LAZY LOADER ERROR"), + }); + }); + + it("handles errors when failing to load lazy route modules on fetcher.submit", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let key = "key"; + let A = await t.fetch("/lazy", key, { + formMethod: "post", + formData: createFormData({}), + }); + expect(t.router.state.fetchers.get(key)?.state).toBe("submitting"); + + await A.lazy.lazy.reject(new Error("LAZY FUNCTION ERROR")); + expect(t.router.state.fetchers.get(key)).toBeUndefined(); + expect(t.router.state.errors).toEqual({ + root: new Error("LAZY FUNCTION ERROR"), + }); + }); + + it("handles action errors in lazy route modules on fetcher.submit", async () => { + let t = setup({ routes: LAZY_ROUTES }); + + let key = "key"; + let A = await t.fetch("/lazy", key, { + formMethod: "post", + formData: createFormData({}), + }); + expect(t.router.state.fetchers.get(key)?.state).toBe("submitting"); + + let actionDfd = createDeferred(); + await A.lazy.lazy.resolve({ + action: () => actionDfd.promise, + }); + expect(t.router.state.fetchers.get(key)?.state).toBe("submitting"); + + await actionDfd.reject(new Error("LAZY ACTION ERROR")); + await tick(); + expect(t.router.state.fetchers.get(key)).toBeUndefined(); + expect(t.router.state.errors).toEqual({ + root: new Error("LAZY ACTION ERROR"), + }); + }); + + it("throws when failing to load lazy route modules on staticHandler.query()", async () => { + let { query } = createStaticHandler([ + { + id: "root", + path: "/", + children: [ + { + id: "lazy", + path: "/lazy", + lazy: async () => { + throw new Error("LAZY FUNCTION ERROR"); + }, + }, + ], + }, + ]); + + let context = await query(createRequest("/lazy")); + invariant( + !(context instanceof Response), + "Expected a StaticContext instance" + ); + expect(context.errors).toEqual({ + root: new Error("LAZY FUNCTION ERROR"), + }); + }); + + it("handles loader errors from lazy route modules on staticHandler.query()", async () => { + let { query } = createStaticHandler([ + { + id: "root", + path: "/", + children: [ + { + id: "lazy", + path: "/lazy", + lazy: async () => { + await tick(); + return { + async loader() { + throw new Error("LAZY LOADER ERROR"); + }, + hasErrorBoundary: true, + }; + }, + }, + ], + }, + ]); + + let context = await query(createRequest("/lazy")); + invariant( + !(context instanceof Response), + "Expected a StaticContext instance" + ); + expect(context.loaderData).toEqual({ + root: null, + }); + expect(context.errors).toEqual({ + lazy: new Error("LAZY LOADER ERROR"), + }); + }); + + it("bubbles loader errors from lazy route modules on staticHandler.query() when hasErrorBoundary is resolved as false", async () => { + let { query } = createStaticHandler([ + { + id: "root", + path: "/", + children: [ + { + id: "lazy", + path: "/lazy", + lazy: async () => { + await tick(); + return { + async loader() { + throw new Error("LAZY LOADER ERROR"); + }, + hasErrorBoundary: false, + }; + }, + }, + ], + }, + ]); + + let context = await query(createRequest("/lazy")); + invariant( + !(context instanceof Response), + "Expected a StaticContext instance" + ); + expect(context.loaderData).toEqual({ + root: null, + }); + expect(context.errors).toEqual({ + root: new Error("LAZY LOADER ERROR"), + }); + }); + + it("throws when failing to load lazy route modules on staticHandler.queryRoute()", async () => { + let { queryRoute } = createStaticHandler([ + { + id: "lazy", + path: "/lazy", + lazy: async () => { + throw new Error("LAZY FUNCTION ERROR"); + }, + }, + ]); + + let err; + try { + await queryRoute(createRequest("/lazy")); + } catch (_err) { + err = _err; + } + + expect(err?.message).toBe("LAZY FUNCTION ERROR"); + }); + + it("handles loader errors in lazy route modules on staticHandler.queryRoute()", async () => { + let { queryRoute } = createStaticHandler([ + { + id: "lazy", + path: "/lazy", + lazy: async () => { + await tick(); + return { + async loader() { + throw new Error("LAZY LOADER ERROR"); + }, + }; + }, + }, + ]); + + let err; + try { + await queryRoute(createRequest("/lazy")); + } catch (_err) { + err = _err; + } + + expect(err?.message).toBe("LAZY LOADER ERROR"); + }); + }); + }); + + describe("ssr", () => { + const SSR_ROUTES = [ + { + id: "index", + path: "/", + loader: () => "INDEX LOADER", + }, + { + id: "parent", + path: "/parent", + loader: () => "PARENT LOADER", + action: () => "PARENT ACTION", + children: [ + { + id: "parentIndex", + index: true, + loader: () => "PARENT INDEX LOADER", + action: () => "PARENT INDEX ACTION", + }, + { + id: "child", + path: "child", + loader: () => "CHILD LOADER", + action: () => "CHILD ACTION", + }, + { + id: "json", + path: "json", + loader: () => json({ type: "loader" }), + action: () => json({ type: "action" }), + }, + { + id: "deferred", + path: "deferred", + loader: ({ request }) => { + if (new URL(request.url).searchParams.has("reject")) { + return defer({ + critical: "loader", + lazy: new Promise((_, r) => + setTimeout(() => r(new Error("broken!")), 10) + ), + }); + } + if (new URL(request.url).searchParams.has("status")) { + return defer( + { + critical: "loader", + lazy: new Promise((r) => setTimeout(() => r("lazy"), 10)), + }, + { status: 201, headers: { "X-Custom": "yes" } } + ); + } + return defer({ + critical: "loader", + lazy: new Promise((r) => setTimeout(() => r("lazy"), 10)), + }); + }, + action: () => + defer({ + critical: "critical", + lazy: new Promise((r) => setTimeout(() => r("lazy"), 10)), + }), + }, + { + id: "error", + path: "error", + loader: () => Promise.reject("ERROR LOADER ERROR"), + action: () => Promise.reject("ERROR ACTION ERROR"), + }, + { + id: "errorBoundary", + path: "error-boundary", + hasErrorBoundary: true, + loader: () => Promise.reject("ERROR BOUNDARY LOADER ERROR"), + action: () => Promise.reject("ERROR BOUNDARY ACTION ERROR"), + }, + ], + }, + { + id: "redirect", + path: "/redirect", + loader: () => redirect("/"), + }, + ]; + + // Regardless of if the URL is internal or external - all absolute URL + // responses should return untouched during SSR so the browser can handle + // them + let ABSOLUTE_URLS = [ + "http://localhost/", + "https://localhost/about", + "http://remix.run/blog", + "https://remix.run/blog", + "//remix.run/blog", + "app://whatever", + "mailto:hello@remix.run", + "web+remix:whatever", + ]; describe("document requests", () => { it("should support document load navigations", async () => { @@ -13641,12 +14776,14 @@ describe("a router", () => { id: "index", loader: true, action: true, + hasErrorBoundary: false, }, { path: "/foo", id: "foo", loader: false, action: true, + hasErrorBoundary: false, }, ], }, @@ -13695,6 +14832,7 @@ describe("a router", () => { id: "noLoader", loader: true, action: true, + hasErrorBoundary: false, }, ], }, @@ -13747,12 +14885,14 @@ describe("a router", () => { id: "index", loader: false, action: true, + hasErrorBoundary: false, }, { path: "/foo", id: "foo", loader: false, action: true, + hasErrorBoundary: false, }, ], }, @@ -13806,12 +14946,14 @@ describe("a router", () => { id: "index", loader: false, action: true, + hasErrorBoundary: false, }, { path: "/foo", id: "foo", loader: false, action: true, + hasErrorBoundary: false, }, ], }, @@ -13859,12 +15001,14 @@ describe("a router", () => { { index: true, id: "index", + hasErrorBoundary: false, }, { path: "foo", id: "foo", loader: () => fooDfd.promise, children: undefined, + hasErrorBoundary: false, }, ], }, @@ -13896,11 +15040,13 @@ describe("a router", () => { { index: true, id: "index", + hasErrorBoundary: false, }, { path: "foo", id: "foo", children: undefined, + hasErrorBoundary: false, }, ], }, @@ -13949,12 +15095,14 @@ describe("a router", () => { { index: true, id: "index", + hasErrorBoundary: false, }, { path: "foo", id: "foo", loader: () => fooDfd.promise, children: undefined, + hasErrorBoundary: false, }, ], }, diff --git a/packages/router/history.ts b/packages/router/history.ts index 0a36fcdfd9..9b5a01b370 100644 --- a/packages/router/history.ts +++ b/packages/router/history.ts @@ -481,7 +481,7 @@ export function invariant(value: any, message?: string) { } } -function warning(cond: any, message: string) { +export function warning(cond: any, message: string) { if (!cond) { // eslint-disable-next-line no-console if (typeof console !== "undefined") console.warn(message); diff --git a/packages/router/index.ts b/packages/router/index.ts index 323f02ca68..542c0b1211 100644 --- a/packages/router/index.ts +++ b/packages/router/index.ts @@ -9,6 +9,7 @@ export type { AgnosticNonIndexRouteObject, AgnosticRouteMatch, AgnosticRouteObject, + LazyRouteFunction, TrackedPromise, FormEncType, FormMethod, @@ -40,7 +41,6 @@ export { resolvePath, resolveTo, stripBasename, - warning, } from "./utils"; export type { @@ -76,10 +76,14 @@ export * from "./router"; /////////////////////////////////////////////////////////////////////////////// /** @internal */ +export type { RouteManifest as UNSAFE_RouteManifest } from "./utils"; export { DeferredData as UNSAFE_DeferredData, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getPathContributingMatches as UNSAFE_getPathContributingMatches, } from "./utils"; -export { invariant as UNSAFE_invariant } from "./history"; +export { + invariant as UNSAFE_invariant, + warning as UNSAFE_warning, +} from "./history"; diff --git a/packages/router/router.ts b/packages/router/router.ts index 9767a3f328..3e49d4b4a7 100644 --- a/packages/router/router.ts +++ b/packages/router/router.ts @@ -5,6 +5,7 @@ import { createPath, invariant, parsePath, + warning, } from "./history"; import type { DataResult, @@ -14,6 +15,7 @@ import type { ErrorResult, FormEncType, FormMethod, + DetectErrorBoundaryFunction, RedirectResult, RouteData, AgnosticRouteObject, @@ -22,6 +24,10 @@ import type { AgnosticRouteMatch, MutationFormMethod, ShouldRevalidateFunction, + RouteManifest, + ImmutableRouteKey, + ActionFunction, + LoaderFunction, } from "./utils"; import { DeferredData, @@ -29,12 +35,12 @@ import { ResultType, convertRoutesToDataRoutes, getPathContributingMatches, + immutableRouteKeys, isRouteErrorResponse, joinPaths, matchRoutes, resolveTo, stripBasename, - warning, } from "./utils"; //////////////////////////////////////////////////////////////////////////////// @@ -328,6 +334,7 @@ export interface RouterInit { routes: AgnosticRouteObject[]; history: History; hydrationData?: HydrationState; + detectErrorBoundary?: DetectErrorBoundaryFunction; } /** @@ -638,6 +645,9 @@ const isBrowser = typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined"; const isServer = !isBrowser; + +const defaultDetectErrorBoundary = (route: AgnosticRouteObject) => + Boolean(route.hasErrorBoundary); //#endregion //////////////////////////////////////////////////////////////////////////////// @@ -653,7 +663,18 @@ export function createRouter(init: RouterInit): Router { "You must provide a non-empty routes array to createRouter" ); - let dataRoutes = convertRoutesToDataRoutes(init.routes); + let detectErrorBoundary = + init.detectErrorBoundary || defaultDetectErrorBoundary; + + // Routes keyed by ID + let manifest: RouteManifest = {}; + // Routes in tree format for matching + let dataRoutes = convertRoutesToDataRoutes( + init.routes, + detectErrorBoundary, + undefined, + manifest + ); let inFlightDataRoutes: AgnosticDataRouteObject[] | undefined; // Cleanup function for history let unlistenHistory: (() => void) | null = null; @@ -692,7 +713,11 @@ export function createRouter(init: RouterInit): Router { } let initialized = - !initialMatches.some((m) => m.route.loader) || init.hydrationData != null; + // All initialMatches need to be loaded before we're ready. If we have lazy + // functions around still then we'll need to run them in initialize() + !initialMatches.some((m) => m.route.lazy) && + // And we have to either have no loaders or have been provided hydrationData + (!initialMatches.some((m) => m.route.loader) || init.hydrationData != null); let router: Router; let state: RouterState = { @@ -837,11 +862,35 @@ export function createRouter(init: RouterInit): Router { } ); - // Kick off initial data load if needed. Use Pop to avoid modifying history - if (!state.initialized) { + if (state.initialized) { + return router; + } + + let lazyMatches = state.matches.filter((m) => m.route.lazy); + + if (lazyMatches.length === 0) { + // Kick off initial data load if needed. Use Pop to avoid modifying history startNavigation(HistoryAction.Pop, state.location); + return router; } + // Load lazy modules, then kick off initial data load if needed + let lazyPromises = lazyMatches.map((m) => + loadLazyRouteModule(m.route, detectErrorBoundary, manifest) + ); + Promise.all(lazyPromises).then(() => { + let initialized = + !state.matches.some((m) => m.route.loader) || + init.hydrationData != null; + if (initialized) { + // We already have required loaderData so we can just set initialized + updateState({ initialized: true }); + } else { + // We still need to kick off initial data loads + startNavigation(HistoryAction.Pop, state.location); + } + }); + return router; } @@ -1259,7 +1308,7 @@ export function createRouter(init: RouterInit): Router { let result: DataResult; let actionMatch = getTargetMatch(matches, location); - if (!actionMatch.route.action) { + if (!actionMatch.route.action && !actionMatch.route.lazy) { result = { type: ResultType.error, error: getInternalRouterError(405, { @@ -1274,6 +1323,8 @@ export function createRouter(init: RouterInit): Router { request, actionMatch, matches, + manifest, + detectErrorBoundary, router.basename ); @@ -1566,7 +1617,7 @@ export function createRouter(init: RouterInit): Router { interruptActiveLoads(); fetchLoadMatches.delete(key); - if (!match.route.action) { + if (!match.route.action && !match.route.lazy) { let error = getInternalRouterError(405, { method: submission.formMethod, pathname: path, @@ -1602,6 +1653,8 @@ export function createRouter(init: RouterInit): Router { fetchRequest, match, requestMatches, + manifest, + detectErrorBoundary, router.basename ); @@ -1821,11 +1874,14 @@ export function createRouter(init: RouterInit): Router { abortController.signal ); fetchControllers.set(key, abortController); + let result: DataResult = await callLoaderOrAction( "loader", fetchRequest, match, matches, + manifest, + detectErrorBoundary, router.basename ); @@ -2021,7 +2077,15 @@ export function createRouter(init: RouterInit): Router { // accordingly let results = await Promise.all([ ...matchesToLoad.map((match) => - callLoaderOrAction("loader", request, match, matches, router.basename) + callLoaderOrAction( + "loader", + request, + match, + matches, + manifest, + detectErrorBoundary, + router.basename + ) ), ...fetchersToLoad.map((f) => { if (f.matches && f.match) { @@ -2030,6 +2094,8 @@ export function createRouter(init: RouterInit): Router { createClientSideRequest(init.history, f.path, request.signal), f.match, f.matches, + manifest, + detectErrorBoundary, router.basename ); } else { @@ -2346,18 +2412,29 @@ export function createRouter(init: RouterInit): Router { export const UNSAFE_DEFERRED_SYMBOL = Symbol("deferred"); +export interface CreateStaticHandlerOptions { + basename?: string; + detectErrorBoundary?: DetectErrorBoundaryFunction; +} + export function createStaticHandler( routes: AgnosticRouteObject[], - opts?: { - basename?: string; - } + opts?: CreateStaticHandlerOptions ): StaticHandler { invariant( routes.length > 0, "You must provide a non-empty routes array to createStaticHandler" ); - let dataRoutes = convertRoutesToDataRoutes(routes); + let manifest: RouteManifest = {}; + let detectErrorBoundary = + opts?.detectErrorBoundary || defaultDetectErrorBoundary; + let dataRoutes = convertRoutesToDataRoutes( + routes, + detectErrorBoundary, + undefined, + manifest + ); let basename = (opts ? opts.basename : null) || "/"; /** @@ -2592,7 +2669,7 @@ export function createStaticHandler( ): Promise | Response> { let result: DataResult; - if (!actionMatch.route.action) { + if (!actionMatch.route.action && !actionMatch.route.lazy) { let error = getInternalRouterError(405, { method: request.method, pathname: new URL(request.url).pathname, @@ -2611,6 +2688,8 @@ export function createStaticHandler( request, actionMatch, matches, + manifest, + detectErrorBoundary, basename, true, isRouteRequest, @@ -2732,7 +2811,11 @@ export function createStaticHandler( let isRouteRequest = routeMatch != null; // Short circuit if we have no loaders to run (queryRoute()) - if (isRouteRequest && !routeMatch?.route.loader) { + if ( + isRouteRequest && + !routeMatch?.route.loader && + !routeMatch?.route.lazy + ) { throw getInternalRouterError(400, { method: request.method, pathname: new URL(request.url).pathname, @@ -2746,7 +2829,9 @@ export function createStaticHandler( matches, Object.keys(pendingActionError || {})[0] ); - let matchesToLoad = requestMatches.filter((m) => m.route.loader); + let matchesToLoad = requestMatches.filter( + (m) => m.route.loader || m.route.lazy + ); // Short circuit if we have no loaders to run (query()) if (matchesToLoad.length === 0) { @@ -2771,6 +2856,8 @@ export function createStaticHandler( request, match, matches, + manifest, + detectErrorBoundary, basename, true, isRouteRequest, @@ -2960,6 +3047,10 @@ function getMatchesToLoad( let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId); let navigationMatches = boundaryMatches.filter((match, index) => { + if (match.route.lazy) { + // We haven't loaded this route yet so we don't know if it's got a loader! + return true; + } if (match.route.loader == null) { return false; } @@ -3096,11 +3187,90 @@ function shouldRevalidateLoader( return arg.defaultShouldRevalidate; } +/** + * Execute route.lazy() methods to lazily load route modules (loader, action, + * shouldRevalidate) and update the routeManifest in place which shares objects + * with dataRoutes so those get updated as well. + */ +async function loadLazyRouteModule( + route: AgnosticDataRouteObject, + detectErrorBoundary: DetectErrorBoundaryFunction, + manifest: RouteManifest +) { + if (!route.lazy) { + return; + } + + let lazyRoute = await route.lazy(); + + // If the lazy route function was executed and removed by another parallel + // call then we can return - first lazy() to finish wins because the return + // value of lazy is expected to be static + if (!route.lazy) { + return; + } + + let routeToUpdate = manifest[route.id]; + invariant(routeToUpdate, "No route found in manifest"); + + // Update the route in place. This should be safe because there's no way + // we could yet be sitting on this route as we can't get there without + // resolving lazy() first. + // + // This is different than the HMR "update" use-case where we may actively be + // on the route being updated. The main concern boils down to "does this + // mutation affect any ongoing navigations or any current state.matches + // values?". If not, it should be safe to update in place. + let routeUpdates: Record = {}; + for (let lazyRouteProperty in lazyRoute) { + let staticRouteValue = + routeToUpdate[lazyRouteProperty as keyof typeof routeToUpdate]; + + let isPropertyStaticallyDefined = + staticRouteValue !== undefined && + // This property isn't static since it should always be updated based + // on the route updates + lazyRouteProperty !== "hasErrorBoundary"; + + warning( + !isPropertyStaticallyDefined, + `Route "${routeToUpdate.id}" has a static property "${lazyRouteProperty}" ` + + `defined but its lazy function is also returning a value for this property. ` + + `The lazy route property "${lazyRouteProperty}" will be ignored.` + ); + + if ( + !isPropertyStaticallyDefined && + !immutableRouteKeys.has(lazyRouteProperty as ImmutableRouteKey) + ) { + routeUpdates[lazyRouteProperty] = + lazyRoute[lazyRouteProperty as keyof typeof lazyRoute]; + } + } + + // Mutate the route with the provided updates. Do this first so we pass + // the updated version to detectErrorBoundary + Object.assign(routeToUpdate, routeUpdates); + + // Mutate the `hasErrorBoundary` property on the route based on the route + // updates and remove the `lazy` function so we don't resolve the lazy + // route again. + Object.assign(routeToUpdate, { + // To keep things framework agnostic, we use the provided + // `detectErrorBoundary` function to set the `hasErrorBoundary` route + // property since the logic will differ between frameworks. + hasErrorBoundary: detectErrorBoundary({ ...routeToUpdate }), + lazy: undefined, + }); +} + async function callLoaderOrAction( type: "loader" | "action", request: Request, match: AgnosticDataRouteMatch, matches: AgnosticDataRouteMatch[], + manifest: RouteManifest, + detectErrorBoundary: DetectErrorBoundaryFunction, basename = "/", isStaticRequest: boolean = false, isRouteRequest: boolean = false, @@ -3108,24 +3278,61 @@ async function callLoaderOrAction( ): Promise { let resultType; let result; - - // Setup a promise we can race against so that abort signals short circuit - let reject: () => void; - let abortPromise = new Promise((_, r) => (reject = r)); - let onReject = () => reject(); - request.signal.addEventListener("abort", onReject); + let onReject: (() => void) | undefined; + + let runHandler = (handler: ActionFunction | LoaderFunction) => { + // Setup a promise we can race against so that abort signals short circuit + let reject: () => void; + let abortPromise = new Promise((_, r) => (reject = r)); + onReject = () => reject(); + request.signal.addEventListener("abort", onReject); + return Promise.race([ + handler({ request, params: match.params, context: requestContext }), + abortPromise, + ]); + }; try { let handler = match.route[type]; - invariant( - handler, - `Could not find the ${type} to run on the "${match.route.id}" route` - ); - result = await Promise.race([ - handler({ request, params: match.params, context: requestContext }), - abortPromise, - ]); + if (match.route.lazy) { + if (handler) { + // Run statically defined handler in parallel with lazy() + let values = await Promise.all([ + runHandler(handler), + loadLazyRouteModule(match.route, detectErrorBoundary, manifest), + ]); + result = values[0]; + } else { + // Load lazy route module, then run any returned handler + await loadLazyRouteModule(match.route, detectErrorBoundary, manifest); + + handler = match.route[type]; + if (handler) { + // Handler still run even if we got interrupted to maintain consistency + // with un-abortable behavior of handler execution on non-lazy or + // previously-lazy-loaded routes + result = await runHandler(handler); + } else if (type === "action") { + throw getInternalRouterError(405, { + method: request.method, + pathname: new URL(request.url).pathname, + routeId: match.route.id, + }); + } else { + // lazy() route has no loader to run. Short circuit here so we don't + // hit the invariant below that errors on returning undefined. + return { type: ResultType.data, data: undefined }; + } + } + } else { + invariant( + handler, + `Could not find the ${type} to run on the "${match.route.id}" route` + ); + + result = await runHandler(handler); + } invariant( result !== undefined, @@ -3137,7 +3344,9 @@ async function callLoaderOrAction( resultType = ResultType.error; result = e; } finally { - request.signal.removeEventListener("abort", onReject); + if (onReject) { + request.signal.removeEventListener("abort", onReject); + } } if (isResponse(result)) { diff --git a/packages/router/utils.ts b/packages/router/utils.ts index 172d52c36a..2dda732610 100644 --- a/packages/router/utils.ts +++ b/packages/router/utils.ts @@ -1,5 +1,5 @@ import type { Location, Path, To } from "./history"; -import { invariant, parsePath } from "./history"; +import { warning, invariant, parsePath } from "./history"; /** * Map of routeId -> data returned from a loader/action/error @@ -139,6 +139,44 @@ export interface ShouldRevalidateFunction { }): boolean; } +/** + * Function provided by the framework-aware layers to set `hasErrorBoundary` + * from the framework-aware `errorElement` prop + */ +export interface DetectErrorBoundaryFunction { + (route: AgnosticRouteObject): boolean; +} + +/** + * Keys we cannot change from within a lazy() function. We spread all other keys + * onto the route. Either they're meaningful to the router, or they'll get + * ignored. + */ +export type ImmutableRouteKey = + | "lazy" + | "caseSensitive" + | "path" + | "id" + | "index" + | "children"; + +export const immutableRouteKeys = new Set([ + "lazy", + "caseSensitive", + "path", + "id", + "index", + "children", +]); + +/** + * lazy() function to load a route definition, which can add non-matching + * related properties to a route + */ +export interface LazyRouteFunction { + (): Promise>; +} + /** * Base RouteObject with common props shared by all types of routes */ @@ -151,6 +189,7 @@ type AgnosticBaseRouteObject = { hasErrorBoundary?: boolean; shouldRevalidate?: ShouldRevalidateFunction; handle?: any; + lazy?: LazyRouteFunction; }; /** @@ -193,6 +232,8 @@ export type AgnosticDataRouteObject = | AgnosticDataIndexRouteObject | AgnosticDataNonIndexRouteObject; +export type RouteManifest = Record; + // Recursive helper for finding path parameters in the absence of wildcards type _PathParam = // split path into individual path segments @@ -277,8 +318,9 @@ function isIndexRoute( // solely with AgnosticDataRouteObject's within the Router export function convertRoutesToDataRoutes( routes: AgnosticRouteObject[], + detectErrorBoundary: DetectErrorBoundaryFunction, parentPath: number[] = [], - allIds: Set = new Set() + manifest: RouteManifest = {} ): AgnosticDataRouteObject[] { return routes.map((route, index) => { let treePath = [...parentPath, index]; @@ -288,23 +330,37 @@ export function convertRoutesToDataRoutes( `Cannot specify children on an index route` ); invariant( - !allIds.has(id), + !manifest[id], `Found a route id collision on id "${id}". Route ` + "id's must be globally unique within Data Router usages" ); - allIds.add(id); if (isIndexRoute(route)) { - let indexRoute: AgnosticDataIndexRouteObject = { ...route, id }; + let indexRoute: AgnosticDataIndexRouteObject = { + ...route, + hasErrorBoundary: detectErrorBoundary(route), + id, + }; + manifest[id] = indexRoute; return indexRoute; } else { let pathOrLayoutRoute: AgnosticDataNonIndexRouteObject = { ...route, id, - children: route.children - ? convertRoutesToDataRoutes(route.children, treePath, allIds) - : undefined, + hasErrorBoundary: detectErrorBoundary(route), + children: undefined, }; + manifest[id] = pathOrLayoutRoute; + + if (route.children) { + pathOrLayoutRoute.children = convertRoutesToDataRoutes( + route.children, + detectErrorBoundary, + treePath, + manifest + ); + } + return pathOrLayoutRoute; } }); @@ -891,26 +947,6 @@ export function stripBasename( return pathname.slice(startIndex) || "/"; } -/** - * @private - */ -export function warning(cond: any, message: string): void { - if (!cond) { - // eslint-disable-next-line no-console - if (typeof console !== "undefined") console.warn(message); - - try { - // Welcome to debugging @remix-run/router! - // - // This error is thrown as a convenience so you can more easily - // find the source for a warning that appears in the console by - // enabling "pause on exceptions" in your JavaScript debugger. - throw new Error(message); - // eslint-disable-next-line no-empty - } catch (e) {} - } -} - /** * Returns a resolved path object relative to the given pathname. *