diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6221e906a..eb8631883 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,10 +2,14 @@ name: test on: push: + branches: + - main paths-ignore: - 'packages/docs/**' - 'packages/playground/**' pull_request: + branches: + - main paths-ignore: - 'packages/docs/**' - 'packages/playground/**' @@ -15,14 +19,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: pnpm/action-setup@v2 + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 with: - version: 8.5.0 - - uses: actions/setup-node@v3 - with: - node-version: '18' - cache: 'pnpm' + node-version: 'lts/*' + cache: pnpm - name: 'BrowserStack Env Setup' uses: 'browserstack/github-actions/setup-env@master' # forks do not have access to secrets so just skip this @@ -34,10 +36,9 @@ jobs: - run: pnpm install - run: pnpm run lint - run: pnpm run -r test:types - - run: pnpm run -r test:unit - run: pnpm run -r build - run: pnpm run -r build:dts - - run: pnpm run -r test:dts + - run: pnpm run -r test:unit # e2e tests that that run locally - run: pnpm run -r test:e2e:ci diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000..fa348a8f2 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,3 @@ +coverage: + status: + patch: off diff --git a/packages/docs/guide/advanced/navigation-guards.md b/packages/docs/guide/advanced/navigation-guards.md index 059328d30..12975df37 100644 --- a/packages/docs/guide/advanced/navigation-guards.md +++ b/packages/docs/guide/advanced/navigation-guards.md @@ -25,8 +25,8 @@ Global before guards are called in creation order, whenever a navigation is trig Every guard function receives two arguments: -- **`to`**: the target route location [in a normalized format](../../api/interfaces/RouteLocationNormalized.md) being navigated to. -- **`from`**: the current route location [in a normalized format](../../api/interfaces/RouteLocationNormalized.md) being navigated away from. +- **`to`**: the target route location [in a normalized format](../../api/#RouteLocationNormalized) being navigated to. +- **`from`**: the current route location [in a normalized format](../../api/#RouteLocationNormalized) being navigated away from. And can optionally return any of the following values: diff --git a/packages/docs/guide/advanced/typed-routes.md b/packages/docs/guide/advanced/typed-routes.md index 55fd7044e..b4665709b 100644 --- a/packages/docs/guide/advanced/typed-routes.md +++ b/packages/docs/guide/advanced/typed-routes.md @@ -1,10 +1,66 @@ -# Typed Routes (v4.1.0+) +# Typed Routes -::: danger ‼️ Experimental feature - -Starting from v4.1.0, we are introducing a new feature called Typed Routes. This **experimental** feature is enabled through a Vite/webpack/Rollup plugin. +::: danger +‼️ Experimental feature +::: ![RouterLink to autocomplete](https://user-images.githubusercontent.com/664177/176442066-c4e7fa31-4f06-4690-a49f-ed0fd880dfca.png) -[Check the v4.1 release notes](https://github.com/vuejs/router/releases/tag/v4.1.0) for more information about this feature. -[Check out the plugin](https://github.com/posva/unplugin-vue-router) GitHub repository for installation instructions and documentation. +It's possible to configure the router to have a _map_ of typed routes. While this can be done manually, it is recommended to use the [unplugin-vue-router](https://github.com/posva/unplugin-vue-router) plugin to generate the routes and the types automatically. + +## Manual Configuration + +Here is an example of how to manually configure typed routes: + +```ts +// import the `RouteRecordInfo` type from vue-router to type your routes +import type { RouteRecordInfo } from 'vue-router' + +// Define an interface of routes +export interface RouteNamedMap { + // each key is a name + home: RouteRecordInfo< + // here we have the same name + 'home', + // this is the path, it will appear in autocompletion + '/', + // these are the raw params. In this case, there are no params allowed + Record, + // these are the normalized params + Record + > + // repeat for each route.. + // Note you can name them whatever you want + 'named-param': RouteRecordInfo< + 'named-param', + '/:name', + { name: string | number }, // raw value + { name: string } // normalized value + > + 'article-details': RouteRecordInfo< + 'article-details', + '/articles/:id+', + { id: Array }, + { id: string[] } + > + 'not-found': RouteRecordInfo< + 'not-found', + '/:path(.*)', + { path: string }, + { path: string } + > +} + +// Last, you will need to augment the Vue Router types with this map of routes +declare module 'vue-router' { + interface TypesConfig { + RouteNamedMap: RouteNamedMap + } +} +``` + +::: tip + +This is indeed tedious and error-prone. That's why it's recommended to use [unplugin-vue-router](https://github.com/posva/unplugin-vue-router) to generate the routes and the types automatically. + +::: diff --git a/packages/docs/guide/essentials/active-links.md b/packages/docs/guide/essentials/active-links.md index d2a5398bf..3c5f23baa 100644 --- a/packages/docs/guide/essentials/active-links.md +++ b/packages/docs/guide/essentials/active-links.md @@ -6,14 +6,14 @@ The RouterLink component adds two CSS classes to active links, `router-link-acti ## When are links active? -A RouterLink is considered to be ***active*** if: +A RouterLink is considered to be **_active_** if: 1. It matches the same route record (i.e. configured route) as the current location. 2. It has the same values for the `params` as the current location. If you're using [nested routes](./nested-routes), any links to ancestor routes will also be considered active if the relevant `params` match. -Other route properties, such as the [`query`](../../api/interfaces/RouteLocationNormalized#query), are not taken into account. +Other route properties, such as the [`query`](../../api/interfaces/RouteLocationBase.html#query), are not taken into account. The path doesn't necessarily need to be a perfect match. For example, using an [`alias`](./redirect-and-alias#Alias) would still be considered a match, so long as it resolves to the same route record and `params`. @@ -21,7 +21,7 @@ If a route has a [`redirect`](./redirect-and-alias#Redirect), it won't be follow ## Exact active links -An ***exact*** match does not include ancestor routes. +An **_exact_** match does not include ancestor routes. Let's imagine we have the following routes: @@ -34,9 +34,9 @@ const routes = [ { path: 'role/:roleId', component: Role, - } - ] - } + }, + ], + }, ] ``` @@ -51,7 +51,7 @@ Then consider these two links: ``` -If the current location path is `/user/erina/role/admin` then these would both be considered _active_, so the class `router-link-active` would be applied to both links. But only the second link would be considered _exact_, so only that second link would have the class `router-link-exact-active`. +If the current location path is `/user/erina/role/admin` then these would both be considered _active_, so the class `router-link-active` would be applied to both links. But only the second link would be considered _exact_, so only that second link would have the class `router-link-exact-active`. ## Configuring the classes diff --git a/packages/docs/guide/essentials/dynamic-matching.md b/packages/docs/guide/essentials/dynamic-matching.md index 4e09cd210..9c9fd0b94 100644 --- a/packages/docs/guide/essentials/dynamic-matching.md +++ b/packages/docs/guide/essentials/dynamic-matching.md @@ -32,12 +32,12 @@ A _param_ is denoted by a colon `:`. When a route is matched, the value of its _ You can have multiple _params_ in the same route, and they will map to corresponding fields on `route.params`. Examples: -| pattern | matched path | route.params | -| ------------------------------ | ------------------------ | -------------------------------------- | -| /users/:username | /users/eduardo | `{ username: 'eduardo' }` | +| pattern | matched path | route.params | +| ------------------------------ | ------------------------ | ---------------------------------------- | +| /users/:username | /users/eduardo | `{ username: 'eduardo' }` | | /users/:username/posts/:postId | /users/eduardo/posts/123 | `{ username: 'eduardo', postId: '123' }` | -In addition to `route.params`, the `route` object also exposes other useful information such as `route.query` (if there is a query in the URL), `route.hash`, etc. You can check out the full details in the [API Reference](../../api/interfaces/RouteLocationNormalized.md). +In addition to `route.params`, the `route` object also exposes other useful information such as `route.query` (if there is a query in the URL), `route.hash`, etc. You can check out the full details in the [API Reference](../../api/#RouteLocationNormalized). A working demo of this example can be found [here](https://codesandbox.io/s/route-params-vue-router-examples-mlb14?from-embed&initialpath=%2Fusers%2Feduardo%2Fposts%2F1). @@ -69,9 +69,12 @@ import { useRoute } from 'vue-router' const route = useRoute() -watch(() => route.params.id, (newId, oldId) => { - // react to route changes... -}) +watch( + () => route.params.id, + (newId, oldId) => { + // react to route changes... + } +) ``` diff --git a/packages/docs/guide/migration/index.md b/packages/docs/guide/migration/index.md index 4e2760641..1f841c89e 100644 --- a/packages/docs/guide/migration/index.md +++ b/packages/docs/guide/migration/index.md @@ -434,7 +434,7 @@ Note this will work if `path` was `/parent/` as the relative location `home` to Decoded values in `params`, `query`, and `hash` are now consistent no matter where the navigation is initiated (older browsers will still produce unencoded `path` and `fullPath`). The initial navigation should yield the same results as in-app navigations. -Given any [normalized route location](/api/interfaces/RouteLocationNormalized.md): +Given any [normalized route location](/api/#RouteLocationNormalized): - Values in `path`, `fullPath` are not decoded anymore. They will appear as provided by the browser (most browsers provide them encoded). e.g. directly writing on the address bar `https://example.com/hello world` will yield the encoded version: `https://example.com/hello%20world` and both `path` and `fullPath` will be `/hello%20world`. - `hash` is now decoded, that way it can be copied over: `router.push({ hash: $route.hash })` and be used directly in [scrollBehavior](/api/interfaces/RouterOptions.md#scrollBehavior)'s `el` option. diff --git a/packages/docs/typedoc-markdown.mjs b/packages/docs/typedoc-markdown.mjs index 89876024c..f81533a92 100644 --- a/packages/docs/typedoc-markdown.mjs +++ b/packages/docs/typedoc-markdown.mjs @@ -8,7 +8,7 @@ const __dirname = path.dirname(new URL(import.meta.url).pathname) const DEFAULT_OPTIONS = { // disableOutputCheck: true, cleanOutputDir: true, - excludeInternal: true, + excludeInternal: false, readme: 'none', out: path.resolve(__dirname, './api'), entryDocument: 'index.md', diff --git a/packages/docs/zh/guide/migration/index.md b/packages/docs/zh/guide/migration/index.md index 96d446e0d..895c6b392 100644 --- a/packages/docs/zh/guide/migration/index.md +++ b/packages/docs/zh/guide/migration/index.md @@ -79,7 +79,7 @@ createRouter({ }) ``` -**原因**: Vue支持的所有浏览器都支持 [HTML5 History API](https://developer.mozilla.org/zh-CN/docs/Web/API/History_API),因此我们不再需要使用 `location.hash`,而可以直接使用 `history.pushState()`。 +**原因**: Vue 支持的所有浏览器都支持 [HTML5 History API](https://developer.mozilla.org/zh-CN/docs/Web/API/History_API),因此我们不再需要使用 `location.hash`,而可以直接使用 `history.pushState()`。 ### 删除了 `*`(星标或通配符)路由 @@ -436,7 +436,7 @@ const routes = [ -给定任何[规范化的路由地址](/zh/api/interfaces/RouteLocationNormalized.md): +给定任何[规范化的路由地址](/zh/api/#RouteLocationNormalized): - `path`, `fullPath`中的值不再被解码了。例如,直接在地址栏上写 " world",将得到编码后的版本:"https://example.com/hello%20world",而 "path "和 "fullPath "都是"/hello%20world"。 - `hash` 现在被解码了,这样就可以复制过来。`router.push({ hash: $route.hash })` 可以直接用于 [scrollBehavior](/zh/api/interfaces/RouterOptions.md#Properties-scrollBehavior) 的 `el` 配置中。 diff --git a/packages/playground/src/App.vue b/packages/playground/src/App.vue index b686c0c54..170d73919 100644 --- a/packages/playground/src/App.vue +++ b/packages/playground/src/App.vue @@ -183,51 +183,37 @@ - diff --git a/packages/playground/src/main.ts b/packages/playground/src/main.ts index ce131cb69..eb0f0e1af 100644 --- a/packages/playground/src/main.ts +++ b/packages/playground/src/main.ts @@ -4,6 +4,7 @@ import type { ComponentPublicInstance } from 'vue' import { router, routerHistory } from './router' import { globalState } from './store' import App from './App.vue' +import { useRoute, type ParamValue, type RouteRecordInfo } from 'vue-router' declare global { interface Window { @@ -29,3 +30,45 @@ app.provide('state', globalState) app.use(router) window.vm = app.mount('#app') + +export interface RouteNamedMap { + home: RouteRecordInfo<'home', '/', Record, Record> + '/[name]': RouteRecordInfo< + '/[name]', + '/:name', + { name: ParamValue }, + { name: ParamValue } + > + '/[...path]': RouteRecordInfo< + '/[...path]', + '/:path(.*)', + { path: ParamValue }, + { path: ParamValue } + > +} + +declare module 'vue-router' { + interface TypesConfig { + RouteNamedMap: RouteNamedMap + } +} + +function _ok() { + const r = useRoute() + + if (r.name === '/[name]') { + r.params.name.toUpperCase() + // @ts-expect-error: Not existing route + } else if (r.name === 'nope') { + console.log('nope') + } + + router.push({ + name: '/[name]', + params: { name: 'hey' }, + }) + + router + .resolve({ name: '/[name]', params: { name: 2 } }) + .params.name.toUpperCase() +} diff --git a/packages/router/CHANGELOG.md b/packages/router/CHANGELOG.md index c72f0ff4b..a7b18d192 100644 --- a/packages/router/CHANGELOG.md +++ b/packages/router/CHANGELOG.md @@ -1,3 +1,27 @@ +# [4.4.0-alpha.3](https://github.com/vuejs/router/compare/v4.4.0-alpha.2...v4.4.0-alpha.3) (2024-06-19) + +### Features + +- add a clearRoutes method ([abe223d](https://github.com/vuejs/router/commit/abe223dab44a092682eed5f77b0e231ff2392076)) + +# [4.4.0-alpha.2](https://github.com/vuejs/router/compare/v4.4.0-alpha.1...v4.4.0-alpha.2) (2024-06-12) + +### Bug Fixes + +- allow arbitrary strings in RouteLocationRaw ([a7a8452](https://github.com/vuejs/router/commit/a7a8452d137024d607d803a4009a5d55419259f2)) + +# [4.4.0-alpha.1](https://github.com/vuejs/router/compare/v4.4.0-alpha.0...v4.4.0-alpha.1) (2024-06-12) + +### Features + +- type useLink ([6a90774](https://github.com/vuejs/router/commit/6a907746db930161902d80e0d254c56e6a273482)) + +# [4.4.0-alpha.0](https://github.com/vuejs/router/compare/v4.3.3...v4.4.0-alpha.0) (2024-06-11) + +### Features + +- typed routes ([f92282b](https://github.com/vuejs/router/commit/f92282b896ccf92360d781832435e1ae86314e0e)) + ## [4.3.3](https://github.com/vuejs/router/compare/v4.3.2...v4.3.3) (2024-06-10) ### Performance Improvements diff --git a/packages/router/__tests__/RouterLink.spec.ts b/packages/router/__tests__/RouterLink.spec.ts index 1b9b8c0f6..bf87c2de7 100644 --- a/packages/router/__tests__/RouterLink.spec.ts +++ b/packages/router/__tests__/RouterLink.spec.ts @@ -1,20 +1,22 @@ /** - * @jest-environment jsdom + * @vitest-environment jsdom */ import { RouterLink } from '../src/RouterLink' +import { RouteQueryAndHash, MatcherLocationRaw } from '../src/types' +import { START_LOCATION_NORMALIZED } from '../src/location' import { - START_LOCATION_NORMALIZED, - RouteQueryAndHash, - MatcherLocationRaw, + createMemoryHistory, + RouterOptions, RouteLocationNormalized, -} from '../src/types' -import { createMemoryHistory, RouterOptions } from '../src' + RouteLocationResolved, +} from '../src' import { createMockedRoute } from './mount' import { defineComponent, PropType } from 'vue' import { RouteRecordNormalized } from '../src/matcher/types' import { routerKey } from '../src/injectionSymbols' import { tick } from './utils' import { mount } from '@vue/test-utils' +import { vi, describe, expect, it } from 'vitest' const records = { home: {} as RouteRecordNormalized, @@ -37,8 +39,6 @@ records.parentAlias = { records.childAlias = { aliasOf: records.child } as RouteRecordNormalized records.childEmptyAlias.aliasOf = records.childEmpty -type RouteLocationResolved = RouteLocationNormalized & { href: string } - function createLocations< T extends Record< string, @@ -367,9 +367,9 @@ async function factory( return this.history.base + to.fullPath }, options: {} as Partial, - resolve: jest.fn(), - push: jest.fn().mockResolvedValue(resolvedLocation), - replace: jest.fn().mockResolvedValue(resolvedLocation), + resolve: vi.fn(), + push: vi.fn().mockResolvedValue(resolvedLocation), + replace: vi.fn().mockResolvedValue(resolvedLocation), } router.resolve.mockReturnValueOnce(resolvedLocation) @@ -801,7 +801,7 @@ describe('RouterLink', () => { }) it('allows adding more click listeners', async () => { - const onClick = jest.fn() + const onClick = vi.fn() const { router, wrapper } = await factory( START_LOCATION_NORMALIZED, { to: locations.basic.string, onClick }, @@ -945,8 +945,8 @@ describe('RouterLink', () => { return this.history.base + to.fullPath }, options: {} as Partial, - resolve: jest.fn(), - push: jest.fn().mockResolvedValue(resolvedLocation), + resolve: vi.fn(), + push: vi.fn().mockResolvedValue(resolvedLocation), } router.resolve.mockReturnValueOnce(resolvedLocation) diff --git a/packages/router/__tests__/RouterView.spec.ts b/packages/router/__tests__/RouterView.spec.ts index 54fea992c..6f142b9f1 100644 --- a/packages/router/__tests__/RouterView.spec.ts +++ b/packages/router/__tests__/RouterView.spec.ts @@ -1,16 +1,15 @@ /** - * @jest-environment jsdom + * @vitest-environment jsdom */ import { RouterView } from '../src/RouterView' import { components, RouteLocationNormalizedLoose } from './utils' -import { - START_LOCATION_NORMALIZED, - RouteLocationNormalized, -} from '../src/types' +import { START_LOCATION_NORMALIZED } from '../src/location' import { markRaw } from 'vue' import { createMockedRoute } from './mount' -import { mockWarn } from 'jest-mock-warn' import { mount } from '@vue/test-utils' +import { RouteLocationNormalized } from '../src' +import { describe, expect, it } from 'vitest' +import { mockWarn } from './vitest-mock-warn' // to have autocompletion function createRoutes>( diff --git a/packages/router/__tests__/__snapshots__/RouterLink.spec.ts.snap b/packages/router/__tests__/__snapshots__/RouterLink.spec.ts.snap index ad84946d9..bdeda41b1 100644 --- a/packages/router/__tests__/__snapshots__/RouterLink.spec.ts.snap +++ b/packages/router/__tests__/__snapshots__/RouterLink.spec.ts.snap @@ -1,3 +1,3 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`RouterLink v-slot provides information on v-slot 1`] = `" route: {"href":"/home","fullPath":"/home","path":"/home","params":{},"meta":{},"query":{},"hash":"","matched":[{}],"name":"home"} href: "/home" isActive: "true" isExactActive: "true" "`; +exports[`RouterLink > v-slot > provides information on v-slot 1`] = `" route: {"href":"/home","fullPath":"/home","path":"/home","params":{},"meta":{},"query":{},"hash":"","matched":[{}],"name":"home"} href: "/home" isActive: "true" isExactActive: "true" "`; diff --git a/packages/router/__tests__/__snapshots__/RouterView.spec.ts.snap b/packages/router/__tests__/__snapshots__/RouterView.spec.ts.snap index 49f60569b..15c8bd203 100644 --- a/packages/router/__tests__/__snapshots__/RouterView.spec.ts.snap +++ b/packages/router/__tests__/__snapshots__/RouterView.spec.ts.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`RouterView displays deeply nested views 1`] = ` +exports[`RouterView > displays deeply nested views 1`] = ` "

Nested

@@ -10,19 +10,19 @@ exports[`RouterView displays deeply nested views 1`] = `
" `; -exports[`RouterView displays nested views 1`] = ` +exports[`RouterView > displays nested views 1`] = ` "

Nested

Foo
" `; -exports[`RouterView v-slot passes a Component and route 1`] = ` +exports[`RouterView > v-slot > passes a Component and route 1`] = ` "home
Home
" `; -exports[`RouterView warnings does not warn RouterView is wrapped 1`] = ` +exports[`RouterView > warnings > does not warn RouterView is wrapped 1`] = ` "
Home
" diff --git a/packages/router/__tests__/createRouter.test-d.ts b/packages/router/__tests__/createRouter.test-d.ts new file mode 100644 index 000000000..64e48ca3f --- /dev/null +++ b/packages/router/__tests__/createRouter.test-d.ts @@ -0,0 +1,47 @@ +import { describe, it } from 'vitest' +import { createRouter, createWebHistory } from '../src' +import { defineComponent, h } from 'vue' + +describe('createRouter', () => { + const component = defineComponent({}) + + const WithProps = defineComponent({ + props: { + id: { + type: String, + required: true, + }, + }, + }) + + const Foo = defineComponent({ + props: { + test: String, + }, + setup() { + return { + title: 'homepage', + } + }, + render() { + return h('div', `${this.title}: ${this.test}`) + }, + }) + + it('works', () => { + createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', component }, + { path: '/foo', component: Foo }, + { path: '/', component: WithProps }, + ], + parseQuery: search => ({}), + stringifyQuery: query => '', + strict: true, + end: true, + sensitive: true, + scrollBehavior(to, from, savedPosition) {}, + }) + }) +}) diff --git a/packages/router/__tests__/encoding.spec.ts b/packages/router/__tests__/encoding.spec.ts index d299ac3a8..e4aebb88d 100644 --- a/packages/router/__tests__/encoding.spec.ts +++ b/packages/router/__tests__/encoding.spec.ts @@ -5,6 +5,7 @@ import { encodeQueryValue, // decode, } from '../src/encoding' +import { describe, expect, it } from 'vitest' describe('Encoding', () => { // all ascii chars with a non ascii char at the beginning diff --git a/packages/router/__tests__/errors.spec.ts b/packages/router/__tests__/errors.spec.ts index c7219dc44..5678bf43f 100644 --- a/packages/router/__tests__/errors.spec.ts +++ b/packages/router/__tests__/errors.spec.ts @@ -8,14 +8,14 @@ import { ErrorTypes, } from '../src/errors' import { components, tick } from './utils' -import { - RouteRecordRaw, - NavigationGuard, +import type { RouteRecordRaw, NavigationGuard } from '../src' +import type { RouteLocationRaw, - START_LOCATION_NORMALIZED, RouteLocationNormalized, -} from '../src/types' -import { mockWarn } from 'jest-mock-warn' +} from '../src/typed-routes' +import { START_LOCATION_NORMALIZED } from '../src/location' +import { vi, describe, expect, it, beforeEach } from 'vitest' +import { mockWarn } from './vitest-mock-warn' const routes: Readonly[] = [ { path: '/', component: components.Home }, @@ -26,8 +26,8 @@ const routes: Readonly[] = [ { path: '/async', component: () => Promise.reject('failed') }, ] -const onError = jest.fn() -const afterEach = jest.fn() +const onError = vi.fn() +const afterEach = vi.fn() function createRouter() { const history = createMemoryHistory() const router = newRouter({ diff --git a/packages/router/__tests__/guards/afterEach.spec.ts b/packages/router/__tests__/guards/afterEach.spec.ts index d42004860..8709b76fc 100644 --- a/packages/router/__tests__/guards/afterEach.spec.ts +++ b/packages/router/__tests__/guards/afterEach.spec.ts @@ -1,5 +1,6 @@ import { createDom, newRouter as createRouter } from '../utils' -import { RouteRecordRaw } from 'src/types' +import { RouteRecordRaw } from '../../src/types' +import { vi, describe, expect, it, beforeAll } from 'vitest' const Home = { template: `
Home
` } const Foo = { template: `
Foo
` } @@ -24,7 +25,7 @@ describe('router.afterEach', () => { }) it('calls afterEach guards on push', async () => { - const spy = jest.fn() + const spy = vi.fn() const router = createRouter({ routes }) router.afterEach(spy) await router.push('/foo') @@ -37,7 +38,7 @@ describe('router.afterEach', () => { }) it('can be removed', async () => { - const spy = jest.fn() + const spy = vi.fn() const router = createRouter({ routes }) const remove = router.afterEach(spy) remove() @@ -46,7 +47,7 @@ describe('router.afterEach', () => { }) it('calls afterEach guards on multiple push', async () => { - const spy = jest.fn() + const spy = vi.fn() const router = createRouter({ routes }) await router.push('/nested') router.afterEach(spy) @@ -67,8 +68,8 @@ describe('router.afterEach', () => { }) it('removing an afterEach guard within one does not affect others', async () => { - const spy1 = jest.fn() - const spy2 = jest.fn() + const spy1 = vi.fn() + const spy2 = vi.fn() const router = createRouter({ routes }) router.afterEach(spy1) const remove = router.afterEach(spy2) diff --git a/packages/router/__tests__/guards/beforeEach.spec.ts b/packages/router/__tests__/guards/beforeEach.spec.ts index ddc15ef07..b4e373606 100644 --- a/packages/router/__tests__/guards/beforeEach.spec.ts +++ b/packages/router/__tests__/guards/beforeEach.spec.ts @@ -1,6 +1,8 @@ import fakePromise from 'faked-promise' import { createDom, tick, noGuard, newRouter as createRouter } from '../utils' -import { RouteRecordRaw, RouteLocationRaw } from '../../src/types' +import { RouteRecordRaw } from '../../src/types' +import { RouteLocationRaw } from '../../src' +import { vi, describe, expect, it, beforeAll } from 'vitest' const Home = { template: `
Home
` } const Foo = { template: `
Foo
` } @@ -31,7 +33,7 @@ describe('router.beforeEach', () => { }) it('calls beforeEach guards on navigation', async () => { - const spy = jest.fn() + const spy = vi.fn() const router = createRouter({ routes }) router.beforeEach(spy) spy.mockImplementationOnce(noGuard) @@ -40,7 +42,7 @@ describe('router.beforeEach', () => { }) it('can be removed', async () => { - const spy = jest.fn() + const spy = vi.fn() const router = createRouter({ routes }) const remove = router.beforeEach(spy) remove() @@ -50,7 +52,7 @@ describe('router.beforeEach', () => { }) it('does not call beforeEach guard if we were already on the page', async () => { - const spy = jest.fn() + const spy = vi.fn() const router = createRouter({ routes }) await router.push('/foo') router.beforeEach(spy) @@ -60,7 +62,7 @@ describe('router.beforeEach', () => { }) it('calls beforeEach guards on navigation between children routes', async () => { - const spy = jest.fn() + const spy = vi.fn() const router = createRouter({ routes }) await router.push('/nested') router.beforeEach(spy) @@ -82,7 +84,7 @@ describe('router.beforeEach', () => { }) it('can redirect to a different location', async () => { - const spy = jest.fn() + const spy = vi.fn() const router = createRouter({ routes }) await router.push('/foo') spy.mockImplementation((to, from, next) => { @@ -124,7 +126,7 @@ describe('router.beforeEach', () => { return }) - const spy = jest.spyOn(history, 'pushState') + const spy = vi.spyOn(history, 'pushState') await router.push({ path: '/', state: { a: 'a' } }) expect(spy).toHaveBeenCalledTimes(1) // called before redirect @@ -141,7 +143,7 @@ describe('router.beforeEach', () => { const router = createRouter({ routes }) await router.push('/foo') - const spy = jest.spyOn(history, 'pushState') + const spy = vi.spyOn(history, 'pushState') await router.push({ path: '/redirect', state: { a: 'a' } }) expect(spy).toHaveBeenCalledTimes(1) // called before redirect @@ -155,7 +157,7 @@ describe('router.beforeEach', () => { }) async function assertRedirect(redirectFn: (i: string) => RouteLocationRaw) { - const spy = jest.fn() + const spy = vi.fn() const router = createRouter({ routes }) await router.push('/') spy.mockImplementation((to, from, next) => { @@ -184,7 +186,7 @@ describe('router.beforeEach', () => { }) it('is called when changing params', async () => { - const spy = jest.fn() + const spy = vi.fn() const router = createRouter({ routes: [...routes] }) await router.push('/n/2') spy.mockImplementation(noGuard) @@ -195,7 +197,7 @@ describe('router.beforeEach', () => { }) it('is not called with same params', async () => { - const spy = jest.fn() + const spy = vi.fn() const router = createRouter({ routes: [...routes] }) await router.push('/n/2') spy.mockImplementation(noGuard) @@ -223,7 +225,7 @@ describe('router.beforeEach', () => { const [p1, r1] = fakePromise() const [p2, r2] = fakePromise() const router = createRouter({ routes }) - const guard1 = jest.fn() + const guard1 = vi.fn() let order = 0 guard1.mockImplementationOnce(async (to, from, next) => { expect(order++).toBe(0) @@ -231,7 +233,7 @@ describe('router.beforeEach', () => { next() }) router.beforeEach(guard1) - const guard2 = jest.fn() + const guard2 = vi.fn() guard2.mockImplementationOnce(async (to, from, next) => { expect(order++).toBe(1) await p2 @@ -255,7 +257,7 @@ describe('router.beforeEach', () => { }) it('adds meta information', async () => { - const spy = jest.fn() + const spy = vi.fn() const router = createRouter({ routes }) router.beforeEach(spy) spy.mockImplementationOnce(noGuard) diff --git a/packages/router/__tests__/guards/beforeEnter.spec.ts b/packages/router/__tests__/guards/beforeEnter.spec.ts index 913b0d041..1df476eee 100644 --- a/packages/router/__tests__/guards/beforeEnter.spec.ts +++ b/packages/router/__tests__/guards/beforeEnter.spec.ts @@ -1,20 +1,21 @@ import fakePromise from 'faked-promise' import { createDom, noGuard, tick, newRouter as createRouter } from '../utils' import { RouteRecordRaw } from '../../src/types' +import { vi, describe, expect, it, beforeAll, beforeEach } from 'vitest' const Home = { template: `
Home
` } const Foo = { template: `
Foo
` } -const beforeEnter = jest.fn() -const beforeEnters = [jest.fn(), jest.fn()] +const beforeEnter = vi.fn() +const beforeEnters = [vi.fn(), vi.fn()] const nested = { - parent: jest.fn(), - nestedEmpty: jest.fn(), - nestedA: jest.fn(), - nestedAbs: jest.fn(), - nestedNested: jest.fn(), - nestedNestedFoo: jest.fn(), - nestedNestedParam: jest.fn(), + parent: vi.fn(), + nestedEmpty: vi.fn(), + nestedA: vi.fn(), + nestedAbs: vi.fn(), + nestedNested: vi.fn(), + nestedNestedFoo: vi.fn(), + nestedNestedParam: vi.fn(), } const routes: RouteRecordRaw[] = [ diff --git a/packages/router/__tests__/guards/beforeResolve.spec.ts b/packages/router/__tests__/guards/beforeResolve.spec.ts index a0dbeaed4..9736b520e 100644 --- a/packages/router/__tests__/guards/beforeResolve.spec.ts +++ b/packages/router/__tests__/guards/beforeResolve.spec.ts @@ -1,5 +1,6 @@ import { createDom, noGuard, newRouter as createRouter } from '../utils' import { RouteRecordRaw } from '../../src/types' +import { vi, describe, expect, it, beforeAll } from 'vitest' const Home = { template: `
Home
` } const Foo = { template: `
Foo
` } @@ -15,7 +16,7 @@ describe('router.beforeEach', () => { }) it('calls beforeEach guards on navigation', async () => { - const spy = jest.fn() + const spy = vi.fn() const router = createRouter({ routes }) router.beforeResolve(spy) spy.mockImplementationOnce(noGuard) diff --git a/packages/router/__tests__/guards/beforeRouteEnter.spec.ts b/packages/router/__tests__/guards/beforeRouteEnter.spec.ts index 82e05418c..02c87ecd5 100644 --- a/packages/router/__tests__/guards/beforeRouteEnter.spec.ts +++ b/packages/router/__tests__/guards/beforeRouteEnter.spec.ts @@ -1,27 +1,28 @@ import fakePromise from 'faked-promise' import { createDom, noGuard, newRouter as createRouter } from '../utils' -import { RouteRecordRaw, NavigationGuard } from '../../src/types' +import type { RouteRecordRaw, NavigationGuard } from '../../src' +import { vi, describe, expect, it, beforeAll, beforeEach } from 'vitest' const Home = { template: `
Home
` } const Foo = { template: `
Foo
` } -const beforeRouteEnter = jest.fn< - ReturnType, - Parameters +const beforeRouteEnter = vi.fn< + Parameters, + ReturnType >() const named = { - default: jest.fn(), - other: jest.fn(), + default: vi.fn(), + other: vi.fn(), } const nested = { - parent: jest.fn(), - nestedEmpty: jest.fn(), - nestedA: jest.fn(), - nestedAbs: jest.fn(), - nestedNested: jest.fn(), - nestedNestedFoo: jest.fn(), - nestedNestedParam: jest.fn(), + parent: vi.fn(), + nestedEmpty: vi.fn(), + nestedA: vi.fn(), + nestedAbs: vi.fn(), + nestedNested: vi.fn(), + nestedNestedFoo: vi.fn(), + nestedNestedParam: vi.fn(), } const routes: RouteRecordRaw[] = [ @@ -123,7 +124,7 @@ describe('beforeRouteEnter', () => { it('does not call beforeRouteEnter guards on navigation between aliases', async () => { const router = createRouter({ routes }) - const spy = jest.fn() + const spy = vi.fn() beforeRouteEnter.mockImplementation(spy) await router.push('/guard/valid') expect(beforeRouteEnter).toHaveBeenCalledTimes(1) diff --git a/packages/router/__tests__/guards/beforeRouteEnterCallback.spec.ts b/packages/router/__tests__/guards/beforeRouteEnterCallback.spec.ts index b9918c48a..d851c7c3f 100644 --- a/packages/router/__tests__/guards/beforeRouteEnterCallback.spec.ts +++ b/packages/router/__tests__/guards/beforeRouteEnterCallback.spec.ts @@ -1,13 +1,14 @@ /** - * @jest-environment jsdom + * @vitest-environment jsdom */ import { defineComponent, h } from 'vue' import { mount } from '@vue/test-utils' import { createRouter, createMemoryHistory, RouterOptions } from '../../src' +import { vi, describe, expect, it, beforeEach } from 'vitest' const nextCallbacks = { - Default: jest.fn(), - Other: jest.fn(), + Default: vi.fn(), + Other: vi.fn(), } const Default = defineComponent({ beforeRouteEnter(to, from, next) { diff --git a/packages/router/__tests__/guards/beforeRouteLeave.spec.ts b/packages/router/__tests__/guards/beforeRouteLeave.spec.ts index ff5618337..57dd78158 100644 --- a/packages/router/__tests__/guards/beforeRouteLeave.spec.ts +++ b/packages/router/__tests__/guards/beforeRouteLeave.spec.ts @@ -1,20 +1,21 @@ import { createDom, noGuard, newRouter as createRouter } from '../utils' import { RouteRecordRaw } from '../../src/types' +import { vi, describe, expect, it, beforeAll, beforeEach } from 'vitest' const Home = { template: `
Home
` } const Foo = { template: `
Foo
` } const nested = { - parent: jest.fn(), - nestedEmpty: jest.fn(), - nestedA: jest.fn(), - nestedB: jest.fn(), - nestedAbs: jest.fn(), - nestedNested: jest.fn(), - nestedNestedFoo: jest.fn(), - nestedNestedParam: jest.fn(), + parent: vi.fn(), + nestedEmpty: vi.fn(), + nestedA: vi.fn(), + nestedB: vi.fn(), + nestedAbs: vi.fn(), + nestedNested: vi.fn(), + nestedNestedFoo: vi.fn(), + nestedNestedParam: vi.fn(), } -const beforeRouteLeave = jest.fn() +const beforeRouteLeave = vi.fn() const routes: RouteRecordRaw[] = [ { path: '/', component: Home }, diff --git a/packages/router/__tests__/guards/beforeRouteUpdate.spec.ts b/packages/router/__tests__/guards/beforeRouteUpdate.spec.ts index 5d98f21e5..d4cbf98eb 100644 --- a/packages/router/__tests__/guards/beforeRouteUpdate.spec.ts +++ b/packages/router/__tests__/guards/beforeRouteUpdate.spec.ts @@ -1,11 +1,12 @@ import fakePromise from 'faked-promise' import { createDom, noGuard, newRouter as createRouter } from '../utils' import { RouteRecordRaw } from '../../src/types' +import { vi, describe, expect, it, beforeAll, beforeEach } from 'vitest' const Home = { template: `
Home
` } const Foo = { template: `
Foo
` } -const beforeRouteUpdate = jest.fn() +const beforeRouteUpdate = vi.fn() const routes: RouteRecordRaw[] = [ { path: '/', component: Home }, { path: '/foo', component: Foo }, diff --git a/packages/router/__tests__/guards/extractComponentsGuards.spec.ts b/packages/router/__tests__/guards/extractComponentsGuards.spec.ts index 560d9cc1e..1d11c6e71 100644 --- a/packages/router/__tests__/guards/extractComponentsGuards.spec.ts +++ b/packages/router/__tests__/guards/extractComponentsGuards.spec.ts @@ -1,11 +1,12 @@ import { extractComponentsGuards } from '../../src/navigationGuards' -import { START_LOCATION_NORMALIZED, RouteRecordRaw } from '../../src/types' +import type { RouteRecordRaw, RouteRecordNormalized } from '../../src' +import { START_LOCATION_NORMALIZED } from '../../src/location' import { components } from '../utils' import { normalizeRouteRecord } from '../../src/matcher' -import { RouteRecordNormalized } from 'src/matcher/types' -import { mockWarn } from 'jest-mock-warn' +import { mockWarn } from '../vitest-mock-warn' +import { vi, describe, expect, it, beforeEach } from 'vitest' -const beforeRouteEnter = jest.fn() +const beforeRouteEnter = vi.fn() // stub those two const to = START_LOCATION_NORMALIZED diff --git a/packages/router/__tests__/guards/guardToPromiseFn.spec.ts b/packages/router/__tests__/guards/guardToPromiseFn.spec.ts index d2b596f98..d19267793 100644 --- a/packages/router/__tests__/guards/guardToPromiseFn.spec.ts +++ b/packages/router/__tests__/guards/guardToPromiseFn.spec.ts @@ -1,7 +1,8 @@ import { guardToPromiseFn } from '../../src/navigationGuards' -import { START_LOCATION_NORMALIZED } from '../../src/types' +import { START_LOCATION_NORMALIZED } from '../../src/location' import { ErrorTypes } from '../../src/errors' -import { mockWarn } from 'jest-mock-warn' +import { mockWarn } from '../vitest-mock-warn' +import { vi, describe, expect, it } from 'vitest' // stub those two const to = START_LOCATION_NORMALIZED @@ -15,7 +16,7 @@ describe('guardToPromiseFn', () => { mockWarn() it('calls the guard with to, from and, next', async () => { expect.assertions(2) - const spy = jest.fn((to, from, next) => next()) + const spy = vi.fn((to, from, next) => next()) await expect(guardToPromiseFn(spy, to, from)()).resolves.toEqual(undefined) expect(spy).toHaveBeenCalledWith(to, from, expect.any(Function)) }) diff --git a/packages/router/__tests__/guards/guardsContext.spec.ts b/packages/router/__tests__/guards/guardsContext.spec.ts index 7900f9f3b..9eb59b80b 100644 --- a/packages/router/__tests__/guards/guardsContext.spec.ts +++ b/packages/router/__tests__/guards/guardsContext.spec.ts @@ -1,8 +1,9 @@ /** - * @jest-environment jsdom + * @vitest-environment jsdom */ import { createRouter, createMemoryHistory } from '../../src' import { createApp, defineComponent } from 'vue' +import { vi, describe, expect, it } from 'vitest' const component = { template: '
Generic
', @@ -11,7 +12,7 @@ const component = { describe('beforeRouteLeave', () => { it('invokes with the component context', async () => { expect.assertions(2) - const spy = jest + const spy = vi .fn() .mockImplementationOnce(function (this: any, to, from, next) { expect(typeof this.counter).toBe('number') @@ -53,7 +54,7 @@ describe('beforeRouteLeave', () => { template: `text`, // we use data to check if the context is the right one because saving `this` in a variable logs a few warnings data: () => ({ counter: 0 }), - beforeRouteLeave: jest + beforeRouteLeave: vi .fn() .mockImplementationOnce(function (this: any, to, from, next) { expect(typeof this.counter).toBe('number') @@ -64,7 +65,7 @@ describe('beforeRouteLeave', () => { template: `text`, // we use data to check if the context is the right one because saving `this` in a variable logs a few warnings data: () => ({ counter: 0 }), - beforeRouteLeave: jest + beforeRouteLeave: vi .fn() .mockImplementationOnce(function (this: any, to, from, next) { expect(typeof this.counter).toBe('number') @@ -107,7 +108,7 @@ describe('beforeRouteLeave', () => { template: ``, // we use data to check if the context is the right one because saving `this` in a variable logs a few warnings data: () => ({ counter: 0 }), - beforeRouteLeave: jest + beforeRouteLeave: vi .fn() .mockImplementationOnce(function (this: any, to, from, next) { expect(typeof this.counter).toBe('number') @@ -118,7 +119,7 @@ describe('beforeRouteLeave', () => { template: `text`, // we use data to check if the context is the right one because saving `this` in a variable logs a few warnings data: () => ({ counter: 0 }), - beforeRouteLeave: jest + beforeRouteLeave: vi .fn() .mockImplementationOnce(function (this: any, to, from, next) { expect(typeof this.counter).toBe('number') @@ -166,7 +167,7 @@ describe('beforeRouteLeave', () => { `, // we use data to check if the context is the right one because saving `this` in a variable logs a few warnings data: () => ({ counter: 0 }), - beforeRouteLeave: jest + beforeRouteLeave: vi .fn() .mockImplementationOnce(function (this: any, to, from, next) { expect(typeof this.counter).toBe('number') @@ -177,7 +178,7 @@ describe('beforeRouteLeave', () => { template: `text`, // we use data to check if the context is the right one because saving `this` in a variable logs a few warnings data: () => ({ counter: 0 }), - beforeRouteLeave: jest + beforeRouteLeave: vi .fn() .mockImplementationOnce(function (this: any, to, from, next) { expect(typeof this.counter).toBe('number') @@ -188,7 +189,7 @@ describe('beforeRouteLeave', () => { template: `text`, // we use data to check if the context is the right one because saving `this` in a variable logs a few warnings data: () => ({ counter: 0 }), - beforeRouteLeave: jest + beforeRouteLeave: vi .fn() .mockImplementationOnce(function (this: any, to, from, next) { expect(typeof this.counter).toBe('number') @@ -234,7 +235,7 @@ describe('beforeRouteLeave', () => { describe('beforeRouteUpdate', () => { it('invokes with the component context', async () => { expect.assertions(2) - const spy = jest + const spy = vi .fn() .mockImplementationOnce(function (this: any, to, from, next) { expect(typeof this.counter).toBe('number') diff --git a/packages/router/__tests__/guards/loadRouteLocation.spec.ts b/packages/router/__tests__/guards/loadRouteLocation.spec.ts index e1c63d135..e4d98612b 100644 --- a/packages/router/__tests__/guards/loadRouteLocation.spec.ts +++ b/packages/router/__tests__/guards/loadRouteLocation.spec.ts @@ -1,8 +1,9 @@ import { isRouteComponent, loadRouteLocation } from '../../src/navigationGuards' -import { RouteRecordRaw, RouteLocationRaw } from '../../src/types' +import { RouteRecordRaw } from '../../src/types' import { components } from '../utils' -import { createMemoryHistory, createRouter } from '../../src' +import { RouteLocationRaw, createMemoryHistory, createRouter } from '../../src' import { FunctionalComponent } from 'vue' +import { describe, expect, it } from 'vitest' const FunctionalHome: FunctionalComponent = () => null FunctionalHome.displayName = 'Home' @@ -93,7 +94,7 @@ describe('loadRouteLocation', () => { it('works with nested routes with redirect', async () => { expect.assertions(2) - testLoadRoute( + await testLoadRoute( [ { path: '/', @@ -105,7 +106,7 @@ describe('loadRouteLocation', () => { ], '/foo' ) - testLoadRoute( + await testLoadRoute( [ { path: '/', diff --git a/packages/router/__tests__/guards/navigatioGuardsInjections.spec.ts b/packages/router/__tests__/guards/navigatioGuardsInjections.spec.ts index fe20236be..f6a0e52aa 100644 --- a/packages/router/__tests__/guards/navigatioGuardsInjections.spec.ts +++ b/packages/router/__tests__/guards/navigatioGuardsInjections.spec.ts @@ -1,11 +1,12 @@ /** - * @jest-environment jsdom + * @vitest-environment jsdom */ import { createDom, newRouter as createRouter } from '../utils' import { mount } from '@vue/test-utils' import { inject } from 'vue' -import { mockWarn } from 'jest-mock-warn' +import { mockWarn } from '../vitest-mock-warn' import type { Router } from '../../src' +import { describe, expect, it, beforeAll } from 'vitest' describe('inject() within navigation guards', () => { mockWarn() diff --git a/packages/router/__tests__/guards/onBeforeRouteLeave.spec.ts b/packages/router/__tests__/guards/onBeforeRouteLeave.spec.ts index 7fd60c632..a244874f9 100644 --- a/packages/router/__tests__/guards/onBeforeRouteLeave.spec.ts +++ b/packages/router/__tests__/guards/onBeforeRouteLeave.spec.ts @@ -1,5 +1,5 @@ /** - * @jest-environment jsdom + * @vitest-environment jsdom */ import { createRouter, @@ -7,6 +7,7 @@ import { onBeforeRouteLeave, } from '../../src' import { createApp, defineComponent } from 'vue' +import { vi, describe, expect, it } from 'vitest' const component = { template: '
Generic
', @@ -14,7 +15,7 @@ const component = { describe('onBeforeRouteLeave', () => { it('removes guards when leaving the route', async () => { - const spy = jest.fn() + const spy = vi.fn() const WithLeave = defineComponent({ template: `text`, setup() { diff --git a/packages/router/__tests__/guards/onBeforeRouteUpdate.spec.ts b/packages/router/__tests__/guards/onBeforeRouteUpdate.spec.ts index af11c91a2..f2d278e86 100644 --- a/packages/router/__tests__/guards/onBeforeRouteUpdate.spec.ts +++ b/packages/router/__tests__/guards/onBeforeRouteUpdate.spec.ts @@ -1,5 +1,5 @@ /** - * @jest-environment jsdom + * @vitest-environment jsdom */ import { createRouter, @@ -11,13 +11,14 @@ import { import { defineComponent, h, ComponentOptions, FunctionalComponent } from 'vue' import { mount } from '@vue/test-utils' import { delay } from '../utils' +import { vi, describe, expect, it } from 'vitest' const component = { template: '
Generic
', } function withSpy(name?: string, isAsync = false) { - const spy = jest.fn() + const spy = vi.fn() const Component = defineComponent({ name, template: `

${name || 'No Name'}

`, diff --git a/packages/router/__tests__/hash-manual-navigation.spec.ts b/packages/router/__tests__/hash-manual-navigation.spec.ts index a465bb309..9a0887c43 100644 --- a/packages/router/__tests__/hash-manual-navigation.spec.ts +++ b/packages/router/__tests__/hash-manual-navigation.spec.ts @@ -1,5 +1,6 @@ import { createMemoryHistory, createRouter, RouterHistory } from '../src' import { tick } from './utils' +import { describe, expect, it } from 'vitest' const component = {} @@ -34,7 +35,7 @@ describe('hash history edge cases', () => { return }) - // const spy = jest.spyOn(history, 'go') + // const spy = vi.spyOn(history, 'go') history.changeURL('/') await tick() @@ -71,7 +72,7 @@ describe('hash history edge cases', () => { return }) - // const spy = jest.spyOn(history, 'go') + // const spy = vi.spyOn(history, 'go') history.changeURL('/') await tick() diff --git a/packages/router/__tests__/history/hash.spec.ts b/packages/router/__tests__/history/hash.spec.ts index 43a299c15..65cc6dfdd 100644 --- a/packages/router/__tests__/history/hash.spec.ts +++ b/packages/router/__tests__/history/hash.spec.ts @@ -2,12 +2,23 @@ import { JSDOM } from 'jsdom' import { createWebHashHistory } from '../../src/history/hash' import { createWebHistory } from '../../src/history/html5' import { createDom } from '../utils' -import { mockWarn } from 'jest-mock-warn' - -jest.mock('../../src/history/html5') +import { mockWarn } from '../vitest-mock-warn' +import { + vi, + describe, + expect, + it, + beforeAll, + beforeEach, + Mock, + afterAll, + afterEach, +} from 'vitest' + +vi.mock('../../src/history/html5') // override the value of isBrowser because the variable is created before JSDOM // is created -jest.mock('../../src/utils/env', () => ({ +vi.mock('../../src/utils/env', () => ({ isBrowser: true, })) @@ -19,7 +30,7 @@ describe('History Hash', () => { mockWarn() beforeEach(() => { - ;(createWebHistory as jest.Mock).mockClear() + ;(createWebHistory as Mock).mockClear() }) afterAll(() => { diff --git a/packages/router/__tests__/history/html5.spec.ts b/packages/router/__tests__/history/html5.spec.ts index 4eb446a36..88bc12f65 100644 --- a/packages/router/__tests__/history/html5.spec.ts +++ b/packages/router/__tests__/history/html5.spec.ts @@ -1,10 +1,20 @@ import { JSDOM } from 'jsdom' import { createWebHistory } from '../../src/history/html5' import { createDom } from '../utils' +import { + vi, + describe, + expect, + it, + beforeAll, + beforeEach, + afterAll, + afterEach, +} from 'vitest' // override the value of isBrowser because the variable is created before JSDOM // is created -jest.mock('../../src/utils/env', () => ({ +vi.mock('../../src/utils/env', () => ({ isBrowser: true, })) @@ -89,7 +99,7 @@ describe('History HTMl5', () => { it('prepends the host to support // urls', () => { let history = createWebHistory() - let spy = jest.spyOn(window.history, 'pushState') + let spy = vi.spyOn(window.history, 'pushState') history.push('/foo') expect(spy).toHaveBeenCalledWith( expect.anything(), @@ -108,7 +118,7 @@ describe('History HTMl5', () => { describe('specific to base containing a hash', () => { it('calls push with hash part of the url with a base', () => { dom.reconfigure({ url: 'file:///usr/etc/index.html' }) - let initialSpy = jest.spyOn(window.history, 'replaceState') + let initialSpy = vi.spyOn(window.history, 'replaceState') let history = createWebHistory('#') // initial navigation expect(initialSpy).toHaveBeenCalledWith( @@ -116,7 +126,7 @@ describe('History HTMl5', () => { expect.any(String), '#/' ) - let spy = jest.spyOn(window.history, 'pushState') + let spy = vi.spyOn(window.history, 'pushState') history.push('/foo') expect(spy).toHaveBeenCalledWith( expect.anything(), @@ -129,7 +139,7 @@ describe('History HTMl5', () => { it('works with something after the hash in the base', () => { dom.reconfigure({ url: 'file:///usr/etc/index.html' }) - let initialSpy = jest.spyOn(window.history, 'replaceState') + let initialSpy = vi.spyOn(window.history, 'replaceState') let history = createWebHistory('#something') // initial navigation expect(initialSpy).toHaveBeenCalledWith( @@ -137,7 +147,7 @@ describe('History HTMl5', () => { expect.any(String), '#something/' ) - let spy = jest.spyOn(window.history, 'pushState') + let spy = vi.spyOn(window.history, 'pushState') history.push('/foo') expect(spy).toHaveBeenCalledWith( expect.anything(), @@ -150,7 +160,7 @@ describe('History HTMl5', () => { it('works with #! and on a file with initial location', () => { dom.reconfigure({ url: 'file:///usr/etc/index.html#!/foo' }) - let spy = jest.spyOn(window.history, 'replaceState') + let spy = vi.spyOn(window.history, 'replaceState') createWebHistory('#!') expect(spy).toHaveBeenCalledWith( expect.anything(), @@ -162,7 +172,7 @@ describe('History HTMl5', () => { it('works with #other', () => { dom.reconfigure({ url: 'file:///usr/etc/index.html' }) - let spy = jest.spyOn(window.history, 'replaceState') + let spy = vi.spyOn(window.history, 'replaceState') createWebHistory('#other') expect(spy).toHaveBeenCalledWith( expect.anything(), @@ -174,7 +184,7 @@ describe('History HTMl5', () => { it('works with custom#other in domain', () => { dom.reconfigure({ url: 'https://esm.dev/custom' }) - let spy = jest.spyOn(window.history, 'replaceState') + let spy = vi.spyOn(window.history, 'replaceState') createWebHistory('custom#other') expect(spy).toHaveBeenCalledWith( expect.anything(), @@ -186,7 +196,7 @@ describe('History HTMl5', () => { it('works with #! and a host with initial location', () => { dom.reconfigure({ url: 'https://esm.dev/#!/foo' }) - let spy = jest.spyOn(window.history, 'replaceState') + let spy = vi.spyOn(window.history, 'replaceState') createWebHistory('/#!') expect(spy).toHaveBeenCalledWith( expect.anything(), diff --git a/packages/router/__tests__/history/memory.spec.ts b/packages/router/__tests__/history/memory.spec.ts index a1fab2351..6a6b19d25 100644 --- a/packages/router/__tests__/history/memory.spec.ts +++ b/packages/router/__tests__/history/memory.spec.ts @@ -1,5 +1,6 @@ import { createMemoryHistory } from '../../src/history/memory' import { START, HistoryLocation } from '../../src/history/common' +import { vi, describe, expect, it } from 'vitest' const loc: HistoryLocation = '/foo' @@ -26,7 +27,7 @@ describe('Memory history', () => { it('does not trigger listeners with push', () => { const history = createMemoryHistory() - const spy = jest.fn() + const spy = vi.fn() history.listen(spy) history.push(loc) expect(spy).not.toHaveBeenCalled() @@ -34,7 +35,7 @@ describe('Memory history', () => { it('does not trigger listeners with replace', () => { const history = createMemoryHistory() - const spy = jest.fn() + const spy = vi.fn() history.listen(spy) history.replace(loc) expect(spy).not.toHaveBeenCalled() @@ -91,7 +92,7 @@ describe('Memory history', () => { it('can listen to navigations', () => { const history = createMemoryHistory() - const spy = jest.fn() + const spy = vi.fn() history.listen(spy) history.push(loc) history.go(-1) @@ -112,8 +113,8 @@ describe('Memory history', () => { it('can stop listening to navigation', () => { const history = createMemoryHistory() - const spy = jest.fn() - const spy2 = jest.fn() + const spy = vi.fn() + const spy2 = vi.fn() // remove right away history.listen(spy)() const remove = history.listen(spy2) @@ -129,8 +130,8 @@ describe('Memory history', () => { it('removing the same listener is a noop', () => { const history = createMemoryHistory() - const spy = jest.fn() - const spy2 = jest.fn() + const spy = vi.fn() + const spy2 = vi.fn() const rem = history.listen(spy) const rem2 = history.listen(spy2) rem() @@ -149,7 +150,7 @@ describe('Memory history', () => { it('removes all listeners with destroy', () => { const history = createMemoryHistory() history.push('/other') - const spy = jest.fn() + const spy = vi.fn() history.listen(spy) history.destroy() history.push('/2') @@ -177,7 +178,7 @@ describe('Memory history', () => { it('can avoid listeners with back and forward', () => { const history = createMemoryHistory() - const spy = jest.fn() + const spy = vi.fn() history.listen(spy) history.push(loc) history.go(-1, false) diff --git a/packages/router/__tests__/initialNavigation.spec.ts b/packages/router/__tests__/initialNavigation.spec.ts index 04ee5cbaa..c30d7fd34 100644 --- a/packages/router/__tests__/initialNavigation.spec.ts +++ b/packages/router/__tests__/initialNavigation.spec.ts @@ -2,10 +2,11 @@ import { JSDOM } from 'jsdom' import { createRouter, createWebHistory } from '../src' import { createDom, components, nextNavigation } from './utils' import { RouteRecordRaw } from '../src/types' +import { describe, expect, it, beforeAll, vi, afterAll } from 'vitest' // override the value of isBrowser because the variable is created before JSDOM // is created -jest.mock('../src/utils/env', () => ({ +vi.mock('../src/utils/env', () => ({ isBrowser: true, })) diff --git a/packages/router/__tests__/isReady.spec.ts b/packages/router/__tests__/isReady.spec.ts index cad258a00..fb559e7e5 100644 --- a/packages/router/__tests__/isReady.spec.ts +++ b/packages/router/__tests__/isReady.spec.ts @@ -1,6 +1,7 @@ import { createMemoryHistory, createRouter } from '../src' import { components } from './utils' import { RouteRecordRaw } from '../src/types' +import { vi, describe, expect, it } from 'vitest' // generic component because we are not displaying anything so it doesn't matter const component = components.Home @@ -49,7 +50,7 @@ describe('isReady', () => { it('rejects when an error is thrown in a navigation guard', async () => { const router = newRouter() - const errorSpy = jest.fn() + const errorSpy = vi.fn() const error = new Error('failed') router.onError(errorSpy) const remove = router.beforeEach(async () => { @@ -75,7 +76,7 @@ describe('isReady', () => { it('rejects a cancelled navigation', async () => { const router = newRouter() - const errorSpy = jest.fn() + const errorSpy = vi.fn() router.onError(errorSpy) const remove = router.beforeEach(() => false) router.push('/foo').catch(() => {}) @@ -102,7 +103,7 @@ describe('isReady', () => { it('rejects failed lazy loading', async () => { const router = newRouter() - const errorSpy = jest.fn() + const errorSpy = vi.fn() router.onError(errorSpy) router.push('/fail-lazy').catch(() => {}) await expect(router.isReady()).rejects.toEqual(expect.any(Error)) diff --git a/packages/router/__tests__/lazyLoading.spec.ts b/packages/router/__tests__/lazyLoading.spec.ts index 09e97b178..03665566c 100644 --- a/packages/router/__tests__/lazyLoading.spec.ts +++ b/packages/router/__tests__/lazyLoading.spec.ts @@ -4,7 +4,16 @@ import { RouterOptions } from '../src/router' import { RouteComponent } from '../src/types' import { ticks } from './utils' import { FunctionalComponent, h } from 'vue' -import { mockWarn } from 'jest-mock-warn' +import { mockWarn } from './vitest-mock-warn' +import { + vi, + describe, + expect, + it, + beforeEach, + MockInstance, + afterEach, +} from 'vitest' function newRouter(options: Partial = {}) { let history = createMemoryHistory() @@ -17,7 +26,7 @@ function createLazyComponent() { const [promise, resolve, reject] = fakePromise() return { - component: jest.fn(() => promise.then(() => ({} as RouteComponent))), + component: vi.fn(() => promise.then(() => ({} as RouteComponent))), promise, resolve, reject, @@ -26,9 +35,9 @@ function createLazyComponent() { describe('Lazy Loading', () => { mockWarn() - let consoleErrorSpy: jest.SpyInstance + let consoleErrorSpy: MockInstance beforeEach(() => { - consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}) + consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) }) afterEach(() => { @@ -150,7 +159,7 @@ describe('Lazy Loading', () => { it('avoid fetching async component if navigation is cancelled through beforeEnter', async () => { const { component, resolve } = createLazyComponent() - const spy = jest.fn((to, from, next) => next(false)) + const spy = vi.fn((to, from, next) => next(false)) const { router } = newRouter({ routes: [ { @@ -178,7 +187,7 @@ describe('Lazy Loading', () => { ], }) - const spy = jest.fn((to, from, next) => next(false)) + const spy = vi.fn((to, from, next) => next(false)) router.beforeEach(spy) @@ -190,8 +199,8 @@ describe('Lazy Loading', () => { it('invokes beforeRouteEnter after lazy loading the component', async () => { const { promise, resolve } = createLazyComponent() - const spy = jest.fn((to, from, next) => next()) - const component = jest.fn(() => + const spy = vi.fn((to, from, next) => next()) + const component = vi.fn(() => promise.then(() => ({ beforeRouteEnter: spy })) ) const { router } = newRouter({ @@ -206,8 +215,8 @@ describe('Lazy Loading', () => { it('beforeRouteLeave works on a lazy loaded component', async () => { const { promise, resolve } = createLazyComponent() - const spy = jest.fn((to, from, next) => next()) - const component = jest.fn(() => + const spy = vi.fn((to, from, next) => next()) + const component = vi.fn(() => promise.then(() => ({ beforeRouteLeave: spy })) ) const { router } = newRouter({ @@ -231,8 +240,8 @@ describe('Lazy Loading', () => { it('beforeRouteUpdate works on a lazy loaded component', async () => { const { promise, resolve } = createLazyComponent() - const spy = jest.fn((to, from, next) => next()) - const component = jest.fn(() => + const spy = vi.fn((to, from, next) => next()) + const component = vi.fn(() => promise.then(() => ({ beforeRouteUpdate: spy })) ) const { router } = newRouter({ @@ -257,7 +266,7 @@ describe('Lazy Loading', () => { routes: [{ path: '/foo', component }], }) - const spy = jest.fn() + const spy = vi.fn() const error = new Error('fail') reject(error) @@ -279,7 +288,7 @@ describe('Lazy Loading', () => { routes: [{ path: '/foo', component }], }) - const spy = jest.fn() + const spy = vi.fn() reject() await router.push('/foo').catch(spy) @@ -306,7 +315,7 @@ describe('Lazy Loading', () => { ], }) - const spy = jest.fn() + const spy = vi.fn() parent.resolve() const error = new Error() diff --git a/packages/router/__tests__/location.spec.ts b/packages/router/__tests__/location.spec.ts index 08e111bff..8ccd8a425 100644 --- a/packages/router/__tests__/location.spec.ts +++ b/packages/router/__tests__/location.spec.ts @@ -8,7 +8,8 @@ import { resolveRelativePath, } from '../src/location' import { RouteLocationNormalizedLoaded } from 'src' -import { mockWarn } from 'jest-mock-warn' +import { vi, describe, expect, it } from 'vitest' +import { mockWarn } from './vitest-mock-warn' describe('parseURL', () => { let parseURL = originalParseURL.bind(null, parseQuery) @@ -149,7 +150,7 @@ describe('parseURL', () => { }) it('calls parseQuery', () => { - const parseQuery = jest.fn() + const parseQuery = vi.fn() originalParseURL(parseQuery, '/?é=é&é=a') expect(parseQuery).toHaveBeenCalledTimes(1) expect(parseQuery).toHaveBeenCalledWith('é=é&é=a') @@ -214,7 +215,7 @@ describe('stringifyURL', () => { }) it('calls stringifyQuery', () => { - const stringifyQuery = jest.fn() + const stringifyQuery = vi.fn() originalStringifyURL(stringifyQuery, { path: '/', query: { é: 'é', b: 'a' }, diff --git a/packages/router/__tests__/matcher/__snapshots__/resolve.spec.ts.snap b/packages/router/__tests__/matcher/__snapshots__/resolve.spec.ts.snap index d2ffdb9c2..090cc8bc3 100644 --- a/packages/router/__tests__/matcher/__snapshots__/resolve.spec.ts.snap +++ b/packages/router/__tests__/matcher/__snapshots__/resolve.spec.ts.snap @@ -1,11 +1,11 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`RouterMatcher.resolve LocationAsName throws if the named route does not exists 1`] = ` +exports[`RouterMatcher.resolve > LocationAsName > throws if the named route does not exists 1`] = ` [Error: No match for {"name":"Home"}] `; -exports[`RouterMatcher.resolve LocationAsRelative throws if the current named route does not exists 1`] = ` +exports[`RouterMatcher.resolve > LocationAsRelative > throws if the current named route does not exists 1`] = ` [Error: No match for {"params":{"a":"foo"}} while being at diff --git a/packages/router/__tests__/matcher/addingRemoving.spec.ts b/packages/router/__tests__/matcher/addingRemoving.spec.ts index 336262105..85ef37888 100644 --- a/packages/router/__tests__/matcher/addingRemoving.spec.ts +++ b/packages/router/__tests__/matcher/addingRemoving.spec.ts @@ -1,6 +1,7 @@ import { createRouterMatcher } from '../../src/matcher' import { MatcherLocation } from '../../src/types' -import { mockWarn } from 'jest-mock-warn' +import { mockWarn } from '../vitest-mock-warn' +import { describe, expect, it } from 'vitest' const currentLocation = { path: '/' } as MatcherLocation // @ts-expect-error @@ -15,6 +16,21 @@ describe('Matcher: adding and removing records', () => { }) }) + it('can remove all records', () => { + const matcher = createRouterMatcher([], {}) + matcher.addRoute({ path: '/', component }) + matcher.addRoute({ path: '/about', component, name: 'about' }) + matcher.addRoute({ + path: '/with-children', + component, + children: [{ path: 'child', component }], + }) + expect(matcher.getRoutes()).not.toHaveLength(0) + matcher.clearRoutes() + expect(matcher.getRoutes()).toHaveLength(0) + expect(matcher.getRecordMatcher('about')).toBeFalsy() + }) + it('throws when adding *', () => { const matcher = createRouterMatcher([], {}) expect(() => { diff --git a/packages/router/__tests__/matcher/pathParser.spec.ts b/packages/router/__tests__/matcher/pathParser.spec.ts index 6cff8d4ec..5ed7ed406 100644 --- a/packages/router/__tests__/matcher/pathParser.spec.ts +++ b/packages/router/__tests__/matcher/pathParser.spec.ts @@ -1,5 +1,6 @@ import { tokenizePath, TokenType } from '../../src/matcher/pathTokenizer' import { tokensToParser } from '../../src/matcher/pathParserRanker' +import { describe, expect, it } from 'vitest' describe('Path parser', () => { describe('tokenizer', () => { diff --git a/packages/router/__tests__/matcher/pathRanking.spec.ts b/packages/router/__tests__/matcher/pathRanking.spec.ts index a5d4d46a1..230c3a182 100644 --- a/packages/router/__tests__/matcher/pathRanking.spec.ts +++ b/packages/router/__tests__/matcher/pathRanking.spec.ts @@ -3,6 +3,7 @@ import { tokensToParser, comparePathParserScore, } from '../../src/matcher/pathParserRanker' +import { describe, expect, it } from 'vitest' type PathParserOptions = Parameters[1] diff --git a/packages/router/__tests__/matcher/records.spec.ts b/packages/router/__tests__/matcher/records.spec.ts index 698edcdfb..92d459084 100644 --- a/packages/router/__tests__/matcher/records.spec.ts +++ b/packages/router/__tests__/matcher/records.spec.ts @@ -1,4 +1,5 @@ import { normalizeRouteRecord } from '../../src/matcher' +import { vi, describe, expect, it } from 'vitest' describe('normalizeRouteRecord', () => { it('transforms a single view into multiple views', () => { @@ -22,7 +23,7 @@ describe('normalizeRouteRecord', () => { }) it('keeps original values in single view', () => { - const beforeEnter = jest.fn() + const beforeEnter = vi.fn() const record = normalizeRouteRecord({ path: '/home', beforeEnter, @@ -64,7 +65,7 @@ describe('normalizeRouteRecord', () => { }) it('keeps original values in multiple views', () => { - const beforeEnter = jest.fn() + const beforeEnter = vi.fn() const record = normalizeRouteRecord({ path: '/home', beforeEnter, diff --git a/packages/router/__tests__/matcher/resolve.spec.ts b/packages/router/__tests__/matcher/resolve.spec.ts index de00849e1..2b9c4e7ae 100644 --- a/packages/router/__tests__/matcher/resolve.spec.ts +++ b/packages/router/__tests__/matcher/resolve.spec.ts @@ -1,14 +1,15 @@ import { createRouterMatcher, normalizeRouteRecord } from '../../src/matcher' import { - START_LOCATION_NORMALIZED, RouteComponent, RouteRecordRaw, MatcherLocationRaw, MatcherLocation, } from '../../src/types' import { MatcherLocationNormalizedLoose } from '../utils' -import { mockWarn } from 'jest-mock-warn' -import { defineComponent } from '@vue/runtime-core' +import { defineComponent } from 'vue' +import { START_LOCATION_NORMALIZED } from '../../src/location' +import { mockWarn } from '../vitest-mock-warn' +import { describe, expect, it } from 'vitest' const component: RouteComponent = defineComponent({}) @@ -75,7 +76,7 @@ describe('RouterMatcher.resolve', () => { /** * * @param record - Record or records we are testing the matcher against - * @param location - location we want to reolve against + * @param location - location we want to resolve against * @param [start] Optional currentLocation used when resolving * @returns error */ diff --git a/packages/router/__tests__/multipleApps.spec.ts b/packages/router/__tests__/multipleApps.spec.ts index 1158048e8..664cd08af 100644 --- a/packages/router/__tests__/multipleApps.spec.ts +++ b/packages/router/__tests__/multipleApps.spec.ts @@ -1,7 +1,7 @@ import { createRouter, createMemoryHistory } from '../src' import { h } from 'vue' import { createDom } from './utils' -// import { mockWarn } from 'jest-mock-warn' +import { vi, describe, expect, it, beforeAll } from 'vitest' const delay = (t: number) => new Promise(resolve => setTimeout(resolve, t)) @@ -34,7 +34,7 @@ describe('Multiple apps', () => { it('does not listen to url changes before being ready', async () => { const { router, history } = newRouter() - const spy = jest.fn((to, from, next) => { + const spy = vi.fn((to, from, next) => { next() }) router.beforeEach(spy) diff --git a/packages/router/__tests__/parseQuery.spec.ts b/packages/router/__tests__/parseQuery.spec.ts index b16c86245..031c3932c 100644 --- a/packages/router/__tests__/parseQuery.spec.ts +++ b/packages/router/__tests__/parseQuery.spec.ts @@ -1,5 +1,6 @@ import { parseQuery } from '../src/query' -import { mockWarn } from 'jest-mock-warn' +import { mockWarn } from './vitest-mock-warn' +import { describe, expect, it } from 'vitest' describe('parseQuery', () => { mockWarn() diff --git a/packages/router/__tests__/routeLocation.test-d.ts b/packages/router/__tests__/routeLocation.test-d.ts new file mode 100644 index 000000000..f228ba823 --- /dev/null +++ b/packages/router/__tests__/routeLocation.test-d.ts @@ -0,0 +1,82 @@ +import { describe, it, expectTypeOf } from 'vitest' +import type { + RouteRecordName, + ParamValue, + ParamValueZeroOrMore, + RouteRecordInfo, + RouteLocationNormalizedTypedList, +} from '../src' + +// TODO: could we move this to an .d.ts file that is only loaded for tests? +// https://github.com/microsoft/TypeScript/issues/15300 +type RouteNamedMap = { + home: RouteRecordInfo<'/', '/', Record, Record> + '/[other]': RouteRecordInfo< + '/[other]', + '/:other', + { other: ParamValue }, + { other: ParamValue } + > + '/[name]': RouteRecordInfo< + '/[name]', + '/:name', + { name: ParamValue }, + { name: ParamValue } + > + '/[...path]': RouteRecordInfo< + '/[...path]', + '/:path(.*)', + { path: ParamValue }, + { path: ParamValue } + > + '/deep/nesting/works/[[files]]+': RouteRecordInfo< + '/deep/nesting/works/[[files]]+', + '/deep/nesting/works/:files*', + { files?: ParamValueZeroOrMore }, + { files?: ParamValueZeroOrMore } + > +} + +describe('Route Location types', () => { + it('RouteLocationNormalized', () => { + function withRoute( + fn: ( + to: RouteLocationNormalizedTypedList[keyof RouteNamedMap] + ) => void + ): void + function withRoute( + name: Name, + fn: (to: RouteLocationNormalizedTypedList[Name]) => void + ): void + function withRoute(...args: unknown[]) {} + + withRoute('/[name]', to => { + expectTypeOf(to.params).toEqualTypeOf<{ name: string }>() + expectTypeOf(to.params).not.toEqualTypeOf<{ notExisting: string }>() + expectTypeOf(to.params).not.toEqualTypeOf<{ other: string }>() + }) + + withRoute('/[name]' as keyof RouteNamedMap, to => { + // @ts-expect-error: no all params have this + to.params.name + if (to.name === '/[name]') { + to.params.name + // @ts-expect-error: no param other + to.params.other + } + }) + + withRoute(to => { + // @ts-expect-error: not all params object have a name + to.params.name + // @ts-expect-error: no route named like that + if (to.name === '') { + } + if (to.name === '/[name]') { + expectTypeOf(to.params).toEqualTypeOf<{ name: string }>() + // @ts-expect-error: no param other + to.params.other + } + }) + }) +}) diff --git a/packages/router/__tests__/router.spec.ts b/packages/router/__tests__/router.spec.ts index bd6b186e0..bf11f31ba 100644 --- a/packages/router/__tests__/router.spec.ts +++ b/packages/router/__tests__/router.spec.ts @@ -4,15 +4,15 @@ import { createMemoryHistory, createWebHistory, createWebHashHistory, + loadRouteLocation, + RouteLocationRaw, } from '../src' import { NavigationFailureType } from '../src/errors' import { createDom, components, tick, nextNavigation } from './utils' -import { - RouteRecordRaw, - RouteLocationRaw, - START_LOCATION_NORMALIZED, -} from '../src/types' -import { mockWarn } from 'jest-mock-warn' +import { RouteRecordRaw } from '../src/types' +import { START_LOCATION_NORMALIZED } from '../src/location' +import { vi, describe, expect, it, beforeAll } from 'vitest' +import { mockWarn } from './vitest-mock-warn' declare var __DEV__: boolean @@ -110,7 +110,7 @@ describe('Router', () => { it('calls history.push with router.push', async () => { const { router, history } = await newRouter() - jest.spyOn(history, 'push') + vi.spyOn(history, 'push') await router.push('/foo') expect(history.push).toHaveBeenCalledTimes(1) expect(history.push).toHaveBeenCalledWith('/foo', undefined) @@ -119,7 +119,7 @@ describe('Router', () => { it('calls history.replace with router.replace', async () => { const history = createMemoryHistory() const { router } = await newRouter({ history }) - jest.spyOn(history, 'replace') + vi.spyOn(history, 'replace') await router.replace('/foo') expect(history.replace).toHaveBeenCalledTimes(1) expect(history.replace).toHaveBeenCalledWith('/foo', expect.anything()) @@ -128,7 +128,7 @@ describe('Router', () => { it('parses query and hash with router.replace', async () => { const history = createMemoryHistory() const { router } = await newRouter({ history }) - jest.spyOn(history, 'replace') + vi.spyOn(history, 'replace') await router.replace('/foo?q=2#a') expect(history.replace).toHaveBeenCalledTimes(1) expect(history.replace).toHaveBeenCalledWith( @@ -142,8 +142,8 @@ describe('Router', () => { const { router } = await newRouter({ history }) // move somewhere else await router.push('/search') - jest.spyOn(history, 'replace') - jest.spyOn(history, 'push') + vi.spyOn(history, 'replace') + vi.spyOn(history, 'push') await router.replace('/home-before') expect(history.push).toHaveBeenCalledTimes(0) expect(history.replace).toHaveBeenCalledTimes(1) @@ -160,8 +160,8 @@ describe('Router', () => { } return // no warn }) - jest.spyOn(history, 'replace') - jest.spyOn(history, 'push') + vi.spyOn(history, 'replace') + vi.spyOn(history, 'push') await router.push('/search') expect(history.location).toBe('/foo') expect(history.push).toHaveBeenCalledTimes(0) @@ -170,7 +170,7 @@ describe('Router', () => { }) it('allows to customize parseQuery', async () => { - const parseQuery = jest.fn(_ => ({})) + const parseQuery = vi.fn(_ => ({})) const { router } = await newRouter({ parseQuery }) const to = router.resolve('/foo?bar=baz') expect(parseQuery).toHaveBeenCalledWith('bar=baz') @@ -178,7 +178,7 @@ describe('Router', () => { }) it('allows to customize stringifyQuery', async () => { - const stringifyQuery = jest.fn(_ => '') + const stringifyQuery = vi.fn(_ => '') const { router } = await newRouter({ stringifyQuery }) const to = router.resolve({ query: { foo: 'bar' } }) expect(stringifyQuery).toHaveBeenCalledWith({ foo: 'bar' }) @@ -187,7 +187,7 @@ describe('Router', () => { }) it('creates an empty query with no query', async () => { - const stringifyQuery = jest.fn(_ => '') + const stringifyQuery = vi.fn(_ => '') const { router } = await newRouter({ stringifyQuery }) const to = router.resolve({ hash: '#a' }) expect(stringifyQuery).not.toHaveBeenCalled() @@ -255,7 +255,7 @@ describe('Router', () => { it('can pass replace option to push', async () => { const { router, history } = await newRouter() - jest.spyOn(history, 'replace') + vi.spyOn(history, 'replace') await router.push({ path: '/foo', replace: true }) expect(history.replace).toHaveBeenCalledTimes(1) expect(history.replace).toHaveBeenCalledWith('/foo', expect.anything()) @@ -263,7 +263,7 @@ describe('Router', () => { it('can replaces current location with a string location', async () => { const { router, history } = await newRouter() - jest.spyOn(history, 'replace') + vi.spyOn(history, 'replace') await router.replace('/foo') expect(history.replace).toHaveBeenCalledTimes(1) expect(history.replace).toHaveBeenCalledWith('/foo', expect.anything()) @@ -271,7 +271,7 @@ describe('Router', () => { it('can replaces current location with an object location', async () => { const { router, history } = await newRouter() - jest.spyOn(history, 'replace') + vi.spyOn(history, 'replace') await router.replace({ path: '/foo' }) expect(history.replace).toHaveBeenCalledTimes(1) expect(history.replace).toHaveBeenCalledWith('/foo', expect.anything()) @@ -279,7 +279,7 @@ describe('Router', () => { it('navigates if the location does not exist', async () => { const { router } = await newRouter({ routes: [routes[0]] }) - const spy = jest.fn((to, from, next) => next()) + const spy = vi.fn((to, from, next) => next()) router.beforeEach(spy) await router.push('/idontexist') expect(spy).toHaveBeenCalledTimes(1) @@ -468,7 +468,9 @@ describe('Router', () => { expect( router.resolve( { params: { p: 1 } }, - router.resolve({ name: 'Param', params: { p: 2 } }) + await loadRouteLocation( + router.resolve({ name: 'Param', params: { p: 2 } }) + ) ) ).toMatchObject({ name: 'Param', @@ -502,7 +504,7 @@ describe('Router', () => { describe('alias', () => { it('does not navigate to alias if already on original record', async () => { const { router } = await newRouter() - const spy = jest.fn((to, from, next) => next()) + const spy = vi.fn((to, from, next) => next()) await router.push('/basic') router.beforeEach(spy) await router.push('/basic-alias') @@ -511,7 +513,7 @@ describe('Router', () => { it('does not navigate to alias with children if already on original record', async () => { const { router } = await newRouter() - const spy = jest.fn((to, from, next) => next()) + const spy = vi.fn((to, from, next) => next()) await router.push('/aliases') router.beforeEach(spy) await router.push('/aliases1') @@ -522,7 +524,7 @@ describe('Router', () => { it('does not navigate to child alias if already on original record', async () => { const { router } = await newRouter() - const spy = jest.fn((to, from, next) => next()) + const spy = vi.fn((to, from, next) => next()) await router.push('/aliases/one') router.beforeEach(spy) await router.push('/aliases1/one') @@ -697,7 +699,7 @@ describe('Router', () => { it('only triggers guards once with a redirect option', async () => { const history = createMemoryHistory() const router = createRouter({ history, routes }) - const spy = jest.fn((to, from, next) => next()) + const spy = vi.fn((to, from, next) => next()) router.beforeEach(spy) await router.push('/to-foo') expect(spy).toHaveBeenCalledTimes(1) diff --git a/packages/router/__tests__/scrollBehavior.spec.ts b/packages/router/__tests__/scrollBehavior.spec.ts index 3e1dd70ac..bdd42a3e5 100644 --- a/packages/router/__tests__/scrollBehavior.spec.ts +++ b/packages/router/__tests__/scrollBehavior.spec.ts @@ -1,20 +1,30 @@ import { JSDOM } from 'jsdom' import { scrollToPosition } from '../src/scrollBehavior' import { createDom } from './utils' -import { mockWarn } from 'jest-mock-warn' +import { mockWarn } from './vitest-mock-warn' +import { + vi, + describe, + expect, + it, + beforeEach, + afterAll, + beforeAll, + MockInstance, +} from 'vitest' describe('scrollBehavior', () => { mockWarn() let dom: JSDOM - let scrollTo: jest.SpyInstance - let getElementById: jest.SpyInstance - let querySelector: jest.SpyInstance + let scrollTo: MockInstance + let getElementById: MockInstance + let querySelector: MockInstance beforeAll(() => { dom = createDom() - scrollTo = jest.spyOn(window, 'scrollTo').mockImplementation(() => {}) - getElementById = jest.spyOn(document, 'getElementById') - querySelector = jest.spyOn(document, 'querySelector') + scrollTo = vi.spyOn(window, 'scrollTo').mockImplementation(() => {}) + getElementById = vi.spyOn(document, 'getElementById') + querySelector = vi.spyOn(document, 'querySelector') // #text let el = document.createElement('div') diff --git a/packages/router/__tests__/ssr.spec.ts b/packages/router/__tests__/ssr.spec.ts index 660c1ae02..65091620f 100644 --- a/packages/router/__tests__/ssr.spec.ts +++ b/packages/router/__tests__/ssr.spec.ts @@ -1,5 +1,5 @@ /** - * @jest-environment node + * @vitest-environment node */ import { createRouter, createMemoryHistory } from '../src' import { createSSRApp, resolveComponent, Component } from 'vue' @@ -8,6 +8,7 @@ import { ssrInterpolate, ssrRenderComponent, } from '@vue/server-renderer' +import { describe, expect, it } from 'vitest' const delay = (t: number) => new Promise(resolve => setTimeout(resolve, t)) diff --git a/packages/router/__tests__/stringifyQuery.spec.ts b/packages/router/__tests__/stringifyQuery.spec.ts index 64e774ee6..16a9703d4 100644 --- a/packages/router/__tests__/stringifyQuery.spec.ts +++ b/packages/router/__tests__/stringifyQuery.spec.ts @@ -1,5 +1,6 @@ import { stringifyQuery } from '../src/query' -import { mockWarn } from 'jest-mock-warn' +import { mockWarn } from './vitest-mock-warn' +import { describe, expect, it } from 'vitest' describe('stringifyQuery', () => { mockWarn() diff --git a/packages/router/__tests__/urlEncoding.spec.ts b/packages/router/__tests__/urlEncoding.spec.ts index 4102b73bd..fd83e1a07 100644 --- a/packages/router/__tests__/urlEncoding.spec.ts +++ b/packages/router/__tests__/urlEncoding.spec.ts @@ -3,8 +3,9 @@ import { components } from './utils' import { RouteRecordRaw } from '../src/types' import { createMemoryHistory } from '../src' import * as encoding from '../src/encoding' +import { vi, describe, expect, it, beforeEach } from 'vitest' -jest.mock('../src/encoding') +vi.mock('../src/encoding') const routes: RouteRecordRaw[] = [ { path: '/', name: 'home', component: components.Home }, @@ -28,7 +29,7 @@ describe('URL Encoding', () => { // @ts-expect-error const value = encoding[key] // @ts-expect-error - if (typeof value === 'function') encoding[key] = jest.fn((v: string) => v) + if (typeof value === 'function') encoding[key] = vi.fn((v: string) => v) // @ts-expect-error else if (key === 'PLUS_RE') encoding[key] = /\+/g } diff --git a/packages/router/__tests__/useApi.spec.ts b/packages/router/__tests__/useApi.spec.ts index e8e7be28f..b9d69ae96 100644 --- a/packages/router/__tests__/useApi.spec.ts +++ b/packages/router/__tests__/useApi.spec.ts @@ -1,9 +1,10 @@ /** - * @jest-environment jsdom + * @vitest-environment jsdom */ import { mount } from '@vue/test-utils' import { computed } from 'vue' import { useRoute, createRouter, createMemoryHistory } from '../src' +import { describe, expect, it } from 'vitest' describe('use apis', () => { it('unwraps useRoute()', async () => { diff --git a/packages/router/__tests__/useLink.spec.ts b/packages/router/__tests__/useLink.spec.ts index 6f148bd26..094e837af 100644 --- a/packages/router/__tests__/useLink.spec.ts +++ b/packages/router/__tests__/useLink.spec.ts @@ -1,9 +1,9 @@ /** - * @jest-environment jsdom + * @vitest-environment jsdom */ import { nextTick, ref } from 'vue' import { mount } from '@vue/test-utils' -import { mockWarn } from 'jest-mock-warn' +import { mockWarn } from './vitest-mock-warn' import { createMemoryHistory, createRouter, @@ -11,6 +11,7 @@ import { useLink, UseLinkOptions, } from '../src' +import { describe, expect, it } from 'vitest' async function callUseLink(args: UseLinkOptions) { const router = createRouter({ diff --git a/packages/router/__tests__/utils.ts b/packages/router/__tests__/utils.ts index 620757ce7..a6020fc3a 100644 --- a/packages/router/__tests__/utils.ts +++ b/packages/router/__tests__/utils.ts @@ -1,13 +1,9 @@ import { JSDOM, ConstructorOptions } from 'jsdom' import { - NavigationGuard, RouteRecordMultipleViews, MatcherLocation, - RouteLocationNormalized, RouteComponent, RouteRecordRaw, - RouteRecordName, - _RouteRecordProps, } from '../src/types' import { h, ComponentOptions } from 'vue' import { @@ -17,7 +13,10 @@ import { Router, RouterView, RouteRecordNormalized, + NavigationGuard, + RouteLocationNormalized, } from '../src' +import { _RouteRecordProps } from '../src/typed-routes' export const tick = (time?: number) => new Promise(resolve => { @@ -65,7 +64,7 @@ export interface RouteRecordViewLoose // @ts-expect-error we are intentionally overriding the type export interface RouteLocationNormalizedLoose extends RouteLocationNormalized { - name: RouteRecordName | null | undefined + name: string | symbol | null | undefined path: string // record? params: any diff --git a/packages/router/__tests__/vitest-mock-warn.ts b/packages/router/__tests__/vitest-mock-warn.ts new file mode 100644 index 000000000..8b2268629 --- /dev/null +++ b/packages/router/__tests__/vitest-mock-warn.ts @@ -0,0 +1,125 @@ +// https://github.com/posva/jest-mock-warn/blob/master/src/index.js + +import { afterEach, beforeEach, expect, type MockInstance, vi } from 'vitest' + +export function mockWarn() { + expect.extend({ + toHaveBeenWarned(received: string | RegExp) { + asserted.set(received.toString(), received) + const passed = warn.mock.calls.some(args => + typeof received === 'string' + ? args[0].indexOf(received) > -1 + : received.test(args[0]) + ) + if (passed) { + return { + pass: true, + message: () => `expected "${received}" not to have been warned.`, + } + } else { + const msgs = warn.mock.calls.map(args => args[0]).join('\n - ') + return { + pass: false, + message: () => + `expected "${received}" to have been warned.\n\nActual messages:\n\n - ${msgs}`, + } + } + }, + + toHaveBeenWarnedLast(received: string | RegExp) { + asserted.set(received.toString(), received) + if (warn.mock.calls.length === 0) { + return { + pass: false, + message: () => 'expected console.warn to have been called.', + } + } + const lastCall = warn.mock.calls.at(-1)?.[0] + const passed = + typeof received === 'string' + ? lastCall.indexOf(received) > -1 + : received.test(lastCall) + if (passed) { + return { + pass: true, + message: () => `expected "${received}" not to have been warned last.`, + } + } else { + const msgs = warn.mock.calls.map(args => args[0]).join('\n - ') + return { + pass: false, + message: () => + `expected "${received}" to have been warned last.\n\nActual messages:\n\n - ${msgs}`, + } + } + }, + + toHaveBeenWarnedTimes(received: string | RegExp, n: number) { + asserted.set(received.toString(), received) + let found = 0 + warn.mock.calls.forEach(args => { + const isFound = + typeof received === 'string' + ? args[0].indexOf(received) > -1 + : received.test(args[0]) + if (isFound) { + found++ + } + }) + + if (found === n) { + return { + pass: true, + message: () => + `expected "${received}" to have been warned ${n} times.`, + } + } else { + return { + pass: false, + message: () => + `expected "${received}" to have been warned ${n} times but got ${found}.`, + } + } + }, + }) + + let warn: MockInstance + const asserted = new Map() + + beforeEach(() => { + asserted.clear() + warn = vi.spyOn(console, 'warn') + warn.mockImplementation(() => {}) + }) + + afterEach(() => { + const assertedArray = Array.from(asserted) + const nonAssertedWarnings = warn.mock.calls + .map(args => args[0]) + .filter(received => { + return !assertedArray.some(([_key, assertedMsg]) => { + return typeof assertedMsg === 'string' + ? received.indexOf(assertedMsg) > -1 + : assertedMsg.test(received) + }) + }) + warn.mockRestore() + if (nonAssertedWarnings.length) { + nonAssertedWarnings.forEach(warning => { + console.warn(warning) + }) + throw new Error(`test case threw unexpected warnings.`) + } + }) +} + +interface CustomMatchers { + toHaveBeenWarned(): R + toHaveBeenWarnedLast(): R + toHaveBeenWarnedTimes(n: number): R +} + +declare module 'vitest' { + interface Assertion extends CustomMatchers {} + interface AsymmetricMatchersContaining extends CustomMatchers {} +} diff --git a/packages/router/__tests__/warnings.spec.ts b/packages/router/__tests__/warnings.spec.ts index 82f6450d5..7d9d9274e 100644 --- a/packages/router/__tests__/warnings.spec.ts +++ b/packages/router/__tests__/warnings.spec.ts @@ -1,4 +1,3 @@ -import { mockWarn } from 'jest-mock-warn' import { createMemoryHistory, createRouter, createRouterMatcher } from '../src' import { defineAsyncComponent, @@ -6,6 +5,8 @@ import { FunctionalComponent, h, } from 'vue' +import { describe, expect, it } from 'vitest' +import { mockWarn } from './vitest-mock-warn' let component = defineComponent({}) @@ -117,7 +118,7 @@ describe('warnings', () => { ).toHaveBeenWarned() }) - it('warns if next is called multiple times in one navigation guard', done => { + it('warns if next is called multiple times in one navigation guard', async () => { expect.assertions(3) let router = createRouter({ history: createMemoryHistory(), @@ -134,10 +135,9 @@ describe('warnings', () => { expect('called more than once').toHaveBeenWarnedTimes(1) next() expect('called more than once').toHaveBeenWarnedTimes(1) - done() }) - router.push('/b') + await router.push('/b') }) it('warns if a non valid function is passed as a component', async () => { diff --git a/packages/router/jest.config.js b/packages/router/jest.config.js deleted file mode 100644 index 8059c1247..000000000 --- a/packages/router/jest.config.js +++ /dev/null @@ -1,26 +0,0 @@ -module.exports = { - testEnvironment: 'node', - testEnvironmentOptions: { - customExportConditions: ['node', 'node-addons'], - }, - globals: { - __DEV__: true, - __TEST__: true, - __BROWSER__: true, - }, - setupFilesAfterEnv: ['/__tests__/setup.ts'], - coverageDirectory: 'coverage', - coverageReporters: ['html', 'lcov', 'text'], - collectCoverageFrom: ['src/**/*.ts'], - coveragePathIgnorePatterns: [ - '/node_modules/', - 'src/index.ts', - 'src/entries', - 'src/devtools.ts', - ], - transform: { - '^.+\\.tsx?$': '@sucrase/jest-plugin', - }, - testMatch: ['/__tests__/**/*.spec.ts?(x)'], - watchPathIgnorePatterns: ['/node_modules'], -} diff --git a/packages/router/package.json b/packages/router/package.json index 752cf3c31..6b22749a9 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -1,6 +1,6 @@ { "name": "vue-router", - "version": "4.3.3", + "version": "4.4.0-alpha.3", "main": "index.js", "unpkg": "dist/vue-router.global.js", "jsdelivr": "dist/vue-router.global.js", @@ -88,7 +88,7 @@ "README.md" ], "scripts": { - "dev": "jest --watch", + "dev": "vitest --ui", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1", "build": "rimraf dist && rollup -c rollup.config.mjs", "build:dts": "api-extractor run --local --verbose && tail -n +10 src/globalExtensions.ts >> dist/vue-router.d.ts", @@ -97,9 +97,8 @@ "build:size": "pnpm run build && rollup -c size-checks/rollup.config.mjs", "dev:e2e": "vite --config e2e/vite.config.mjs", "test:types": "tsc --build tsconfig.json", - "test:dts": "tsc -p ./test-dts/tsconfig.json", - "test:unit": "jest --coverage", - "test": "pnpm run test:types && pnpm run test:unit && pnpm run build && pnpm run build:dts && pnpm run test:e2e", + "test:unit": "vitest --coverage run", + "test": "pnpm run test:types && pnpm run build && pnpm run build:dts && pnpm run test:unit && pnpm run test:e2e", "test:e2e": "pnpm run test:e2e:headless", "test:e2e:headless": "node e2e/runner.mjs --env chrome-headless", "test:e2e:native": "node e2e/runner.mjs --env chrome", @@ -124,11 +123,11 @@ "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-replace": "^5.0.5", "@rollup/plugin-terser": "^0.4.4", - "@sucrase/jest-plugin": "^3.0.0", - "@types/jest": "^29.5.12", "@types/jsdom": "^21.1.6", "@types/nightwatch": "^2.3.30", "@vitejs/plugin-vue": "^5.0.4", + "@vitest/coverage-v8": "^1.6.0", + "@vitest/ui": "^1.6.0", "@vue/compiler-sfc": "^3.4.23", "@vue/server-renderer": "^3.4.23", "@vue/test-utils": "^2.4.4", @@ -139,9 +138,6 @@ "dotenv": "^16.4.5", "faked-promise": "^2.2.2", "geckodriver": "^3.2.0", - "jest": "^29.7.0", - "jest-environment-jsdom": "^29.7.0", - "jest-mock-warn": "^1.1.0", "nightwatch": "^2.6.22", "nightwatch-helpers": "^1.2.0", "rimraf": "^5.0.5", @@ -151,6 +147,7 @@ "sucrase": "^3.34.0", "typescript": "~5.3.3", "vite": "^5.2.9", + "vitest": "^1.6.0", "vue": "^3.4.23" } } diff --git a/packages/router/size-checks/webRouterAndVue.js b/packages/router/size-checks/webRouterAndVue.js index e22ed0a74..0c392949b 100644 --- a/packages/router/size-checks/webRouterAndVue.js +++ b/packages/router/size-checks/webRouterAndVue.js @@ -1,4 +1,4 @@ -import { h, createApp } from '@vue/runtime-dom' +import { h, createApp } from 'vue' import { createRouter, createWebHistory } from '../dist/vue-router.esm-bundler' createRouter({ diff --git a/packages/router/src/RouterLink.ts b/packages/router/src/RouterLink.ts index d2e66fa28..731dfbf75 100644 --- a/packages/router/src/RouterLink.ts +++ b/packages/router/src/RouterLink.ts @@ -25,20 +25,24 @@ import { RendererNode, // @ts-ignore ComponentOptionsMixin, + MaybeRef, } from 'vue' -import { - isRouteLocation, - RouteLocationRaw, - VueUseOptions, - RouteLocation, - RouteLocationNormalized, -} from './types' import { isSameRouteLocationParams, isSameRouteRecord } from './location' import { routerKey, routeLocationKey } from './injectionSymbols' import { RouteRecord } from './matcher/types' import { NavigationFailure } from './errors' import { isArray, isBrowser, noop } from './utils' import { warn } from './warning' +import { isRouteLocation } from './types' +import { + RouteLocation, + RouteLocationAsPath, + RouteLocationAsRelativeTyped, + RouteLocationAsString, + RouteLocationRaw, + RouteLocationResolved, + RouteMap, +} from './typed-routes' export interface RouterLinkOptions { /** @@ -81,18 +85,52 @@ export interface RouterLinkProps extends RouterLinkOptions { | 'false' } +/** + * Context passed from router-link components to devtools. + * @internal + */ export interface UseLinkDevtoolsContext { - route: RouteLocationNormalized & { href: string } + route: RouteLocationResolved isActive: boolean isExactActive: boolean error: string | null } -export type UseLinkOptions = VueUseOptions +/** + * Options passed to {@link useLink}. + */ +export interface UseLinkOptions { + to: MaybeRef< + | RouteLocationAsString + | RouteLocationAsRelativeTyped + | RouteLocationAsPath + | RouteLocationRaw + > + replace?: MaybeRef +} + +/** + * Return type of {@link useLink}. + * @internal + */ +export interface UseLinkReturn { + route: ComputedRef> + href: ComputedRef + isActive: ComputedRef + isExactActive: ComputedRef + navigate(e?: MouseEvent): Promise +} // TODO: we could allow currentRoute as a prop to expose `isActive` and // `isExactActive` behavior should go through an RFC -export function useLink(props: UseLinkOptions) { +/** + * Returns the internal behavior of a {@link RouterLink} without the rendering part. + * + * @param props - a `to` location and an optional `replace` flag + */ +export function useLink( + props: UseLinkOptions +): UseLinkReturn { const router = inject(routerKey)! const currentRoute = inject(routeLocationKey)! @@ -317,7 +355,8 @@ export interface _RouterLinkI { isActive, isExactActive, navigate, - }: UnwrapRef>) => VNode[] + }: // TODO: How do we add the name generic + UnwrapRef) => VNode[] } } diff --git a/packages/router/src/RouterView.ts b/packages/router/src/RouterView.ts index ad4d8f720..33a09b507 100644 --- a/packages/router/src/RouterView.ts +++ b/packages/router/src/RouterView.ts @@ -17,11 +17,11 @@ import { VNode, Component, } from 'vue' -import { +import type { RouteLocationNormalized, RouteLocationNormalizedLoaded, - RouteLocationMatched, -} from './types' +} from './typed-routes' +import type { RouteLocationMatched } from './types' import { matchedRouteKey, viewDepthKey, diff --git a/packages/router/src/config.ts b/packages/router/src/config.ts index 1da3f7f15..02204e9ba 100644 --- a/packages/router/src/config.ts +++ b/packages/router/src/config.ts @@ -1,5 +1,13 @@ /** - * Allows customizing existing types of the router that are used globally like `$router`, ``, and `beforeRouteLeave()`. **ONLY FOR INTERNAL USAGE**. + * Allows customizing existing types of the router that are used globally like `$router`, ``, etc. **ONLY FOR INTERNAL USAGE**. + * + * - `$router` - the router instance + * - `$route` - the current route location + * - `beforeRouteEnter` - Page component option + * - `beforeRouteUpdate` - Page component option + * - `beforeRouteLeave` - Page component option + * - `RouterLink` - RouterLink Component + * - `RouterView` - RouterView Component * * @internal */ diff --git a/packages/router/src/devtools.ts b/packages/router/src/devtools.ts index b543fa80b..e4bef80d7 100644 --- a/packages/router/src/devtools.ts +++ b/packages/router/src/devtools.ts @@ -16,8 +16,8 @@ import { PathParser } from './matcher/pathParserRanker' import { Router } from './router' import { UseLinkDevtoolsContext } from './RouterLink' import { RouterViewDevtoolsContext } from './RouterView' -import { RouteLocationNormalized } from './types' import { assign, isArray } from './utils' +import { RouteLocationNormalized } from './typed-routes' /** * Copies a route location and removes any problematic properties that cannot be shown in devtools (e.g. Vue instances). diff --git a/packages/router/src/errors.ts b/packages/router/src/errors.ts index 021e7a8bb..877a0de21 100644 --- a/packages/router/src/errors.ts +++ b/packages/router/src/errors.ts @@ -1,9 +1,5 @@ -import { - MatcherLocationRaw, - MatcherLocation, - RouteLocationRaw, - RouteLocationNormalized, -} from './types' +import type { MatcherLocationRaw, MatcherLocation } from './types' +import type { RouteLocationRaw, RouteLocationNormalized } from './typed-routes' import { assign } from './utils' /** diff --git a/packages/router/src/globalExtensions.ts b/packages/router/src/globalExtensions.ts index 368978578..7aa983d62 100644 --- a/packages/router/src/globalExtensions.ts +++ b/packages/router/src/globalExtensions.ts @@ -2,9 +2,9 @@ import type { NavigationGuardWithThis, NavigationGuard, RouteLocationNormalizedLoaded, -} from './types' -import { RouterView } from './RouterView' -import { RouterLink } from './RouterLink' +} from './typed-routes' +import type { RouterView } from './RouterView' +import type { RouterLink } from './RouterLink' import type { Router } from './router' import type { TypesConfig } from './config' diff --git a/packages/router/src/index.ts b/packages/router/src/index.ts index 816dd2c51..2a62ad156 100644 --- a/packages/router/src/index.ts +++ b/packages/router/src/index.ts @@ -29,30 +29,30 @@ export { viewDepthKey, } from './injectionSymbols' -export { START_LOCATION_NORMALIZED as START_LOCATION } from './types' +export { START_LOCATION_NORMALIZED as START_LOCATION } from './location' export type { // route location _RouteLocationBase, MatcherLocationAsPath, LocationAsRelativeRaw, RouteQueryAndHash, - RouteLocationRaw, - RouteLocation, - RouteLocationNormalized, - RouteLocationNormalizedLoaded, - RouteParams, - RouteParamsRaw, + + // route params RouteParamValue, RouteParamValueRaw, + + // Partial route location RouteLocationNamedRaw, + // exported for backwards compat for old RouteLocationRaw RouteLocationPathRaw, RouteLocationMatched, + + // extra options when navigating RouteLocationOptions, + // route records _RouteRecordBase, - RouteRecordName, RouteRecordRaw, - RouteRecordRedirectOption, RouteRecordSingleView, RouteRecordSingleViewWithChildren, RouteRecordMultipleViews, @@ -61,11 +61,80 @@ export type { RouteMeta, RouteComponent, // RawRouteComponent, + RouteParamsGeneric, + RouteParamsRawGeneric, + MatcherLocation, +} from './types' +export type { _Awaitable } from './types/utils' + +// Experimental Type Safe API +export type { + RouteMap, + RouteMapGeneric, + + // route location + RouteLocationRaw, + RouteLocation, + RouteLocationGeneric, + RouteLocationTyped, + RouteLocationTypedList, + + // RouteLocationNormalized + RouteLocationNormalizedGeneric, + RouteLocationNormalized, + RouteLocationNormalizedTyped, + RouteLocationNormalizedTypedList, + + // RouteLocationNormalizedLoaded + RouteLocationNormalizedLoadedGeneric, + RouteLocationNormalizedLoaded, + RouteLocationNormalizedLoadedTyped, + RouteLocationNormalizedLoadedTypedList, + + // RouteLocationResolved + RouteLocationResolvedGeneric, + RouteLocationResolved, + RouteLocationResolvedTyped, + RouteLocationResolvedTypedList, + + // relative + RouteLocationAsRelativeGeneric, + RouteLocationAsRelative, + RouteLocationAsRelativeTyped, + RouteLocationAsRelativeTypedList, + // string + RouteLocationAsStringTyped, + RouteLocationAsString, + RouteLocationAsStringTypedList, + // as path + RouteLocationAsPathGeneric, + RouteLocationAsPath, + RouteLocationAsPathTyped, + RouteLocationAsPathTypedList, + + // route records + RouteRecordInfo, + RouteRecordNameGeneric, + RouteRecordName, + _RouteRecordProps, + RouteRecordRedirectOption, + + // params + RouteParams, + RouteParamsRaw, + ParamValue, + ParamValueOneOrMore, + ParamValueZeroOrMore, + ParamValueZeroOrOne, + + // navigation guards NavigationGuard, - NavigationGuardNext, NavigationGuardWithThis, NavigationHookAfter, -} from './types' + NavigationGuardReturn, + NavigationGuardNext, + NavigationGuardNextCallback, +} from './typed-routes' export { createRouter } from './router' export type { Router, RouterOptions, RouterScrollBehavior } from './router' @@ -87,6 +156,7 @@ export type { _RouterLinkI, RouterLinkProps, UseLinkOptions, + UseLinkReturn, } from './RouterLink' export { RouterView } from './RouterView' export type { RouterViewProps } from './RouterView' diff --git a/packages/router/src/injectionSymbols.ts b/packages/router/src/injectionSymbols.ts index ad391624f..49ec55e92 100644 --- a/packages/router/src/injectionSymbols.ts +++ b/packages/router/src/injectionSymbols.ts @@ -1,5 +1,5 @@ import type { InjectionKey, ComputedRef, Ref } from 'vue' -import { RouteLocationNormalizedLoaded } from './types' +import type { RouteLocationNormalizedLoaded } from './typed-routes' import { RouteRecordNormalized } from './matcher/types' import type { Router } from './router' diff --git a/packages/router/src/location.ts b/packages/router/src/location.ts index 1f29cfb63..08c2b744b 100644 --- a/packages/router/src/location.ts +++ b/packages/router/src/location.ts @@ -1,13 +1,10 @@ import { LocationQuery, LocationQueryRaw } from './query' -import { - RouteLocation, - RouteLocationNormalized, - RouteParamValue, -} from './types' +import { RouteParamValue, RouteParamsGeneric } from './types' import { RouteRecord } from './matcher/types' import { warn } from './warning' import { isArray } from './utils' import { decode } from './encoding' +import { RouteLocation, RouteLocationNormalizedLoaded } from './typed-routes' /** * Location object returned by {@link `parseURL`}. @@ -159,8 +156,8 @@ export function isSameRouteRecord(a: RouteRecord, b: RouteRecord): boolean { } export function isSameRouteLocationParams( - a: RouteLocationNormalized['params'], - b: RouteLocationNormalized['params'] + a: RouteParamsGeneric, + b: RouteParamsGeneric ): boolean { if (Object.keys(a).length !== Object.keys(b).length) return false @@ -247,3 +244,31 @@ export function resolveRelativePath(to: string, from: string): string { toSegments.slice(toPosition).join('/') ) } + +/** + * Initial route location where the router is. Can be used in navigation guards + * to differentiate the initial navigation. + * + * @example + * ```js + * import { START_LOCATION } from 'vue-router' + * + * router.beforeEach((to, from) => { + * if (from === START_LOCATION) { + * // initial navigation + * } + * }) + * ``` + */ +export const START_LOCATION_NORMALIZED: RouteLocationNormalizedLoaded = { + path: '/', + // TODO: could we use a symbol in the future? + name: undefined, + params: {}, + query: {}, + hash: '', + fullPath: '/', + matched: [], + meta: {}, + redirectedFrom: undefined, +} diff --git a/packages/router/src/matcher/index.ts b/packages/router/src/matcher/index.ts index 7040fa810..0d67061d3 100644 --- a/packages/router/src/matcher/index.ts +++ b/packages/router/src/matcher/index.ts @@ -3,8 +3,6 @@ import { MatcherLocationRaw, MatcherLocation, isRouteName, - RouteRecordName, - _RouteRecordProps, } from '../types' import { createRouterError, ErrorTypes, MatcherError } from '../errors' import { createRouteRecordMatcher, RouteRecordMatcher } from './pathMatcher' @@ -20,6 +18,7 @@ import { comparePathParserScore } from './pathParserRanker' import { warn } from '../warning' import { assign, noop } from '../utils' +import type { RouteRecordNameGeneric, _RouteRecordProps } from '../typed-routes' /** * Internal RouterMatcher @@ -28,12 +27,13 @@ import { assign, noop } from '../utils' */ export interface RouterMatcher { addRoute: (record: RouteRecordRaw, parent?: RouteRecordMatcher) => () => void - removeRoute: { - (matcher: RouteRecordMatcher): void - (name: RouteRecordName): void - } + removeRoute(matcher: RouteRecordMatcher): void + removeRoute(name: NonNullable): void + clearRoutes: () => void getRoutes: () => RouteRecordMatcher[] - getRecordMatcher: (name: RouteRecordName) => RouteRecordMatcher | undefined + getRecordMatcher: ( + name: NonNullable + ) => RouteRecordMatcher | undefined /** * Resolves a location. Gives access to the route record that corresponds to the actual path as well as filling the corresponding params objects @@ -60,13 +60,16 @@ export function createRouterMatcher( ): RouterMatcher { // normalized ordered array of matchers const matchers: RouteRecordMatcher[] = [] - const matcherMap = new Map() + const matcherMap = new Map< + NonNullable, + RouteRecordMatcher + >() globalOptions = mergeOptions( { strict: false, end: true, sensitive: false } as PathParserOptions, globalOptions ) - function getRecordMatcher(name: RouteRecordName) { + function getRecordMatcher(name: NonNullable) { return matcherMap.get(name) } @@ -193,7 +196,9 @@ export function createRouterMatcher( : noop } - function removeRoute(matcherRef: RouteRecordName | RouteRecordMatcher) { + function removeRoute( + matcherRef: NonNullable | RouteRecordMatcher + ) { if (isRouteName(matcherRef)) { const matcher = matcherMap.get(matcherRef) if (matcher) { @@ -339,7 +344,19 @@ export function createRouterMatcher( // add initial routes routes.forEach(route => addRoute(route)) - return { addRoute, resolve, removeRoute, getRoutes, getRecordMatcher } + function clearRoutes() { + matchers.length = 0 + matcherMap.clear() + } + + return { + addRoute, + resolve, + removeRoute, + clearRoutes, + getRoutes, + getRecordMatcher, + } } function paramsFromLocation( diff --git a/packages/router/src/matcher/types.ts b/packages/router/src/matcher/types.ts index 6693a3d75..5efab0559 100644 --- a/packages/router/src/matcher/types.ts +++ b/packages/router/src/matcher/types.ts @@ -1,9 +1,11 @@ +import type { + NavigationGuard, + NavigationGuardNextCallback, + _RouteRecordProps, +} from '../typed-routes' import { RouteRecordMultipleViews, - NavigationGuard, _RouteRecordBase, - _RouteRecordProps, - NavigationGuardNextCallback, RouteRecordRaw, } from '../types' import { ComponentPublicInstance } from 'vue' diff --git a/packages/router/src/navigationGuards.ts b/packages/router/src/navigationGuards.ts index 27eac6cfc..b1b9268a5 100644 --- a/packages/router/src/navigationGuards.ts +++ b/packages/router/src/navigationGuards.ts @@ -1,16 +1,20 @@ import { - NavigationGuard, - RouteLocationNormalized, - NavigationGuardNext, - RouteLocationRaw, - RouteLocationNormalizedLoaded, - NavigationGuardNextCallback, isRouteLocation, Lazy, RouteComponent, RawRouteComponent, } from './types' +import type { + RouteLocationNormalized, + RouteLocationNormalizedLoaded, + NavigationGuard, + RouteLocation, + RouteLocationRaw, + NavigationGuardNext, + NavigationGuardNextCallback, +} from './typed-routes' + import { createRouterError, ErrorTypes, @@ -368,7 +372,7 @@ export function isRouteComponent( * @param route - resolved route to load */ export function loadRouteLocation( - route: RouteLocationNormalized + route: RouteLocation | RouteLocationNormalized ): Promise { return route.matched.every(record => record.redirect) ? Promise.reject(new Error('Cannot load a route that redirects.')) diff --git a/packages/router/src/router.ts b/packages/router/src/router.ts index 20ab4d1e2..da33e976d 100644 --- a/packages/router/src/router.ts +++ b/packages/router/src/router.ts @@ -1,20 +1,25 @@ import { - RouteLocationNormalized, RouteRecordRaw, - RouteLocationRaw, - NavigationHookAfter, - START_LOCATION_NORMALIZED, Lazy, - RouteLocationNormalizedLoaded, - RouteLocation, - RouteRecordName, isRouteLocation, isRouteName, - NavigationGuardWithThis, RouteLocationOptions, MatcherLocationRaw, - RouteParams, } from './types' +import type { + RouteLocation, + RouteLocationRaw, + RouteParams, + RouteLocationNormalized, + RouteLocationNormalizedLoaded, + NavigationGuardWithThis, + NavigationHookAfter, + RouteLocationResolved, + RouteLocationAsRelative, + RouteLocationAsPath, + RouteLocationAsString, + RouteRecordNameGeneric, +} from './typed-routes' import { RouterHistory, HistoryState, NavigationType } from './history/common' import { ScrollPosition, @@ -49,6 +54,7 @@ import { stringifyURL, isSameRouteLocation, isSameRouteRecord, + START_LOCATION_NORMALIZED, } from './location' import { extractComponentsGuards, guardToPromiseFn } from './navigationGuards' import { warn } from './warning' @@ -60,6 +66,9 @@ import { routerViewLocationKey, } from './injectionSymbols' import { addDevtools } from './devtools' +import { _LiteralUnion } from './types/utils' +import { RouteLocationAsRelativeTyped } from './typed-routes/route-location' +import { RouteMap } from './typed-routes/route-map' /** * Internal type to define an ErrorHandler @@ -192,7 +201,7 @@ export interface Router { readonly options: RouterOptions /** - * Allows turning off the listening of history events. This is a low level api for micro-frontends. + * Allows turning off the listening of history events. This is a low level api for micro-frontend. */ listening: boolean @@ -202,7 +211,11 @@ export interface Router { * @param parentName - Parent Route Record where `route` should be appended at * @param route - Route Record to add */ - addRoute(parentName: RouteRecordName, route: RouteRecordRaw): () => void + addRoute( + // NOTE: it could be `keyof RouteMap` but the point of dynamic routes is not knowing the routes at build + parentName: NonNullable, + route: RouteRecordRaw + ): () => void /** * Add a new {@link RouteRecordRaw | route record} to the router. * @@ -214,18 +227,23 @@ export interface Router { * * @param name - Name of the route to remove */ - removeRoute(name: RouteRecordName): void + removeRoute(name: NonNullable): void /** * Checks if a route with a given name exists * * @param name - Name of the route to check */ - hasRoute(name: RouteRecordName): boolean + hasRoute(name: NonNullable): boolean /** * Get a full list of all the {@link RouteRecord | route records}. */ getRoutes(): RouteRecord[] + /** + * Delete all routes from the router matcher. + */ + clearRoutes(): void + /** * Returns the {@link RouteLocation | normalized version} of a * {@link RouteLocationRaw | route location}. Also includes an `href` property @@ -235,10 +253,17 @@ export interface Router { * @param to - Raw route location to resolve * @param currentLocation - Optional current location to resolve against */ + resolve( + to: RouteLocationAsRelativeTyped, + // NOTE: This version doesn't work probably because it infers the type too early + // | RouteLocationAsRelative + currentLocation?: RouteLocationNormalizedLoaded + ): RouteLocationResolved resolve( - to: RouteLocationRaw, + // not having the overload produces errors in RouterLink calls to router.resolve() + to: RouteLocationAsString | RouteLocationAsRelative | RouteLocationAsPath, currentLocation?: RouteLocationNormalizedLoaded - ): RouteLocation & { href: string } + ): RouteLocationResolved /** * Programmatically navigate to a new URL by pushing an entry in the history @@ -391,7 +416,7 @@ export function createRouter(options: RouterOptions): Router { applyToParams.bind(null, decode) function addRoute( - parentOrRoute: RouteRecordName | RouteRecordRaw, + parentOrRoute: NonNullable | RouteRecordRaw, route?: RouteRecordRaw ) { let parent: Parameters<(typeof matcher)['addRoute']>[1] | undefined @@ -414,7 +439,7 @@ export function createRouter(options: RouterOptions): Router { return matcher.addRoute(record, parent) } - function removeRoute(name: RouteRecordName) { + function removeRoute(name: NonNullable) { const recordMatcher = matcher.getRecordMatcher(name) if (recordMatcher) { matcher.removeRoute(recordMatcher) @@ -427,14 +452,15 @@ export function createRouter(options: RouterOptions): Router { return matcher.getRoutes().map(routeMatcher => routeMatcher.record) } - function hasRoute(name: RouteRecordName): boolean { + function hasRoute(name: NonNullable): boolean { return !!matcher.getRecordMatcher(name) } function resolve( - rawLocation: Readonly, + rawLocation: RouteLocationRaw, currentLocation?: RouteLocationNormalizedLoaded - ): RouteLocation & { href: string } { + ): RouteLocationResolved { + // const resolve: Router['resolve'] = (rawLocation: RouteLocationRaw, currentLocation) => { // const objectLocation = routerLocationAsObject(rawLocation) // we create a copy to modify it later currentLocation = assign({}, currentLocation || currentRoute.value) @@ -474,7 +500,7 @@ export function createRouter(options: RouterOptions): Router { `router.resolve() was passed an invalid location. This will fail in production.\n- Location:`, rawLocation ) - rawLocation = {} + return resolve({}) } let matcherLocation: MatcherLocationRaw @@ -1207,6 +1233,7 @@ export function createRouter(options: RouterOptions): Router { addRoute, removeRoute, + clearRoutes: matcher.clearRoutes, hasRoute, getRoutes, resolve, diff --git a/packages/router/src/scrollBehavior.ts b/packages/router/src/scrollBehavior.ts index 7072604ae..642556452 100644 --- a/packages/router/src/scrollBehavior.ts +++ b/packages/router/src/scrollBehavior.ts @@ -1,4 +1,7 @@ -import { RouteLocationNormalized, RouteLocationNormalizedLoaded } from './types' +import type { + RouteLocationNormalized, + RouteLocationNormalizedLoaded, +} from './typed-routes' import { warn } from './warning' // we use types instead of interfaces to make it work with HistoryStateValue type diff --git a/packages/router/src/typed-routes/index.ts b/packages/router/src/typed-routes/index.ts new file mode 100644 index 000000000..3a64a4633 --- /dev/null +++ b/packages/router/src/typed-routes/index.ts @@ -0,0 +1,5 @@ +export type * from './params' +export type * from './route-map' +export type * from './route-location' +export type * from './route-records' +export type * from './navigation-guards' diff --git a/packages/router/src/typed-routes/navigation-guards.ts b/packages/router/src/typed-routes/navigation-guards.ts new file mode 100644 index 000000000..ab624e1f7 --- /dev/null +++ b/packages/router/src/typed-routes/navigation-guards.ts @@ -0,0 +1,94 @@ +import type { _Awaitable } from '../types/utils' +import type { + RouteLocationNormalizedLoaded, + RouteLocationNormalized, + RouteLocationRaw, +} from './route-location' +import type { TypesConfig } from '../config' +import type { NavigationFailure } from '../errors' +import { ComponentPublicInstance } from 'vue' + +/** + * Return types for a Navigation Guard. Based on `TypesConfig` + * + * @see {@link TypesConfig} + */ +export type NavigationGuardReturn = void | Error | boolean | RouteLocationRaw + +/** + * Navigation Guard with a type parameter for `this`. + * @see {@link TypesConfig} + */ +export interface NavigationGuardWithThis { + ( + this: T, + to: RouteLocationNormalized, + from: RouteLocationNormalizedLoaded, + // intentionally not typed to make people use the return + next: NavigationGuardNext + ): _Awaitable +} + +/** + * In `router.beforeResolve((to) => {})`, the `to` is typed as `RouteLocationNormalizedLoaded`, not + * `RouteLocationNormalized` like in `router.beforeEach()`. In practice it doesn't change much as users do not rely on + * the difference between them but if we update the type in vue-router, we will have to update this type too. + * @internal + */ +export interface _NavigationGuardResolved { + ( + this: undefined, + to: RouteLocationNormalizedLoaded, + from: RouteLocationNormalizedLoaded, + // intentionally not typed to make people use the return + next: NavigationGuardNext + ): _Awaitable +} + +/** + * Navigation Guard. + */ +export interface NavigationGuard { + ( + to: RouteLocationNormalized, + from: RouteLocationNormalizedLoaded, + // intentionally not typed to make people use the return + next: NavigationGuardNext + ): _Awaitable +} + +/** + * Navigation hook triggered after a navigation is settled. + */ +export interface NavigationHookAfter { + ( + to: RouteLocationNormalized, + from: RouteLocationNormalizedLoaded, + failure?: NavigationFailure | void + ): unknown +} + +/** + * `next()` callback passed to navigation guards. + */ +export interface NavigationGuardNext { + (): void + (error: Error): void + (location: RouteLocationRaw): void + (valid: boolean | undefined): void + (cb: NavigationGuardNextCallback): void + /** + * Allows to detect if `next` isn't called in a resolved guard. Used + * internally in DEV mode to emit a warning. Commented out to simplify + * typings. + * @internal + */ + // _called: boolean +} + +/** + * Callback that can be passed to `next()` in `beforeRouteEnter()` guards. + */ +export type NavigationGuardNextCallback = ( + vm: ComponentPublicInstance +) => unknown diff --git a/packages/router/src/typed-routes/params.ts b/packages/router/src/typed-routes/params.ts new file mode 100644 index 000000000..63dc8e9b5 --- /dev/null +++ b/packages/router/src/typed-routes/params.ts @@ -0,0 +1,53 @@ +import type { RouteMap } from './route-map' + +/** + * Utility type for raw and non raw params like :id+ + * + */ +export type ParamValueOneOrMore = [ + ParamValue, + ...ParamValue[] +] + +/** + * Utility type for raw and non raw params like :id* + * + */ +export type ParamValueZeroOrMore = true extends isRaw + ? ParamValue[] | undefined | null + : ParamValue[] | undefined + +/** + * Utility type for raw and non raw params like :id? + * + */ +export type ParamValueZeroOrOne = true extends isRaw + ? string | number | null | undefined + : string + +/** + * Utility type for raw and non raw params like :id + * + */ +export type ParamValue = true extends isRaw + ? string | number + : string + +// TODO: finish this refactor +// export type ParamValueOneOrMoreRaw = [ParamValueRaw, ...ParamValueRaw[]] +// export type ParamValue = string +// export type ParamValueRaw = string | number + +/** + * Generate a type safe params for a route location. Requires the name of the route to be passed as a generic. + * @see {@link RouteParamsGeneric} + */ +export type RouteParams = + RouteMap[Name]['params'] + +/** + * Generate a type safe raw params for a route location. Requires the name of the route to be passed as a generic. + * @see {@link RouteParamsRaw} + */ +export type RouteParamsRaw = + RouteMap[Name]['paramsRaw'] diff --git a/packages/router/src/typed-routes/route-location.ts b/packages/router/src/typed-routes/route-location.ts new file mode 100644 index 000000000..3be525760 --- /dev/null +++ b/packages/router/src/typed-routes/route-location.ts @@ -0,0 +1,312 @@ +import type { + RouteLocationOptions, + RouteQueryAndHash, + _RouteLocationBase, + RouteParamsGeneric, + RouteLocationMatched, + RouteParamsRawGeneric, +} from '../types' +import type { _LiteralUnion } from '../types/utils' +// inlining the type as it avoids code splitting issues +import type { RouteMap, RouteMapGeneric } from './route-map' +import type { Router } from '../router' +import type { RouteRecord, RouteRecordNormalized } from '../matcher/types' +import type { RouteRecordNameGeneric } from './route-records' + +/** + * Generic version of {@link RouteLocation}. It is used when no {@link RouteMap} is provided. + */ +export interface RouteLocationGeneric extends _RouteLocationBase { + /** + * Array of {@link RouteRecord} containing components as they were + * passed when adding records. It can also contain redirect records. This + * can't be used directly. **This property is non-enumerable**. + */ + matched: RouteRecord[] +} + +/** + * Helper to generate a type safe version of the {@link RouteLocation} type. + */ +export interface RouteLocationTyped< + RouteMap extends RouteMapGeneric, + Name extends keyof RouteMap +> extends RouteLocationGeneric { + // Extract is needed because keyof can produce numbers + name: Extract + params: RouteMap[Name]['params'] +} + +/** + * List of all possible {@link RouteLocation} indexed by the route name. + * @internal + */ +export type RouteLocationTypedList< + RouteMap extends RouteMapGeneric = RouteMapGeneric +> = { [N in keyof RouteMap]: RouteLocationTyped } + +/** + * Generic version of {@link RouteLocationNormalized} that is used when no {@link RouteMap} is provided. + */ +export interface RouteLocationNormalizedGeneric extends _RouteLocationBase { + name: RouteRecordNameGeneric + params: RouteParamsGeneric + /** + * Array of {@link RouteRecordNormalized} + */ + matched: RouteRecordNormalized[] +} + +/** + * Helper to generate a type safe version of the {@link RouteLocationNormalized} type. + */ +export interface RouteLocationNormalizedTyped< + RouteMap extends RouteMapGeneric = RouteMapGeneric, + Name extends keyof RouteMap = keyof RouteMap +> extends RouteLocationNormalizedGeneric { + name: Extract + // we don't override path because it could contain params and in practice it's just not useful + params: RouteMap[Name]['params'] + + /** + * Array of {@link RouteRecordNormalized} + */ + matched: RouteRecordNormalized[] // non-enumerable +} + +/** + * List of all possible {@link RouteLocationNormalized} indexed by the route name. + * @internal + */ +export type RouteLocationNormalizedTypedList< + RouteMap extends RouteMapGeneric = RouteMapGeneric +> = { [N in keyof RouteMap]: RouteLocationNormalizedTyped } + +/** + * Generic version of {@link RouteLocationNormalizedLoaded} that is used when no {@link RouteMap} is provided. + */ +export interface RouteLocationNormalizedLoadedGeneric + extends RouteLocationNormalizedGeneric { + /** + * Array of {@link RouteLocationMatched} containing only plain components (any + * lazy-loaded components have been loaded and were replaced inside the + * `components` object) so it can be directly used to display routes. It + * cannot contain redirect records either. **This property is non-enumerable**. + */ + matched: RouteLocationMatched[] +} + +/** + * Helper to generate a type safe version of the {@link RouteLocationNormalizedLoaded} type. + */ +export interface RouteLocationNormalizedLoadedTyped< + RouteMap extends RouteMapGeneric = RouteMapGeneric, + Name extends keyof RouteMap = keyof RouteMap +> extends RouteLocationNormalizedLoadedGeneric { + name: Extract + // we don't override path because it could contain params and in practice it's just not useful + params: RouteMap[Name]['params'] +} + +/** + * List of all possible {@link RouteLocationNormalizedLoaded} indexed by the route name. + * @internal + */ +export type RouteLocationNormalizedLoadedTypedList< + RouteMap extends RouteMapGeneric = RouteMapGeneric +> = { [N in keyof RouteMap]: RouteLocationNormalizedLoadedTyped } + +/** + * Generic version of {@link RouteLocationAsRelative}. It is used when no {@link RouteMap} is provided. + */ +export interface RouteLocationAsRelativeGeneric + extends RouteQueryAndHash, + RouteLocationOptions { + name?: RouteRecordNameGeneric + params?: RouteParamsRawGeneric + /** + * A relative path to the current location. This property should be removed + */ + path?: undefined +} + +/** + * Helper to generate a type safe version of the {@link RouteLocationAsRelative} type. + */ +export interface RouteLocationAsRelativeTyped< + RouteMap extends RouteMapGeneric = RouteMapGeneric, + Name extends keyof RouteMap = keyof RouteMap +> extends RouteLocationAsRelativeGeneric { + name?: Extract + params?: RouteMap[Name]['paramsRaw'] +} + +/** + * List of all possible {@link RouteLocationAsRelative} indexed by the route name. + * @internal + */ +export type RouteLocationAsRelativeTypedList< + RouteMap extends RouteMapGeneric = RouteMapGeneric +> = { [N in keyof RouteMap]: RouteLocationAsRelativeTyped } + +/** + * Generic version of {@link RouteLocationAsPath}. It is used when no {@link RouteMap} is provided. + */ +export interface RouteLocationAsPathGeneric + extends RouteQueryAndHash, + RouteLocationOptions { + /** + * Percentage encoded pathname section of the URL. + */ + path: string +} + +/** + * Helper to generate a type safe version of the {@link RouteLocationAsPath} type. + */ +export interface RouteLocationAsPathTyped< + RouteMap extends RouteMapGeneric = RouteMapGeneric, + Name extends keyof RouteMap = keyof RouteMap +> extends RouteLocationAsPathGeneric { + path: _LiteralUnion + + // // allows to check for .path and other properties that exist in different route location types + // [key: string]: unknown +} + +/** + * List of all possible {@link RouteLocationAsPath} indexed by the route name. + * @internal + */ +export type RouteLocationAsPathTypedList< + RouteMap extends RouteMapGeneric = RouteMapGeneric +> = { [N in keyof RouteMap]: RouteLocationAsPathTyped } + +/** + * Helper to generate a type safe version of the {@link RouteLocationAsString} type. + */ +export type RouteLocationAsStringTyped< + RouteMap extends RouteMapGeneric = RouteMapGeneric, + Name extends keyof RouteMap = keyof RouteMap +> = RouteMap[Name]['path'] + +/** + * List of all possible {@link RouteLocationAsString} indexed by the route name. + * @internal + */ +export type RouteLocationAsStringTypedList< + RouteMap extends RouteMapGeneric = RouteMapGeneric +> = { [N in keyof RouteMap]: RouteLocationAsStringTyped } + +/** + * Generic version of {@link RouteLocationResolved}. It is used when no {@link RouteMap} is provided. + */ +export interface RouteLocationResolvedGeneric extends RouteLocationGeneric { + /** + * Resolved `href` for the route location that will be set on the ``. + */ + href: string +} + +/** + * Helper to generate a type safe version of the {@link RouteLocationResolved} type. + */ +export interface RouteLocationResolvedTyped< + RouteMap extends RouteMapGeneric, + Name extends keyof RouteMap +> extends RouteLocationTyped { + /** + * Resolved `href` for the route location that will be set on the ``. + */ + href: string +} + +/** + * List of all possible {@link RouteLocationResolved} indexed by the route name. + * @internal + */ +export type RouteLocationResolvedTypedList< + RouteMap extends RouteMapGeneric = RouteMapGeneric +> = { [N in keyof RouteMap]: RouteLocationResolvedTyped } + +/** + * Type safe versions of types that are exposed by vue-router. We have to use a generic check to allow for names to be `undefined` when no `RouteMap` is provided. + */ + +/** + * {@link RouteLocationRaw} resolved using the matcher + */ +export type RouteLocation = + RouteMapGeneric extends RouteMap + ? RouteLocationGeneric + : RouteLocationTypedList[Name] + +/** + * Similar to {@link RouteLocation} but its + * {@link RouteLocationNormalizedTyped.matched | `matched` property} cannot contain redirect records + */ +export type RouteLocationNormalized< + Name extends keyof RouteMap = keyof RouteMap +> = RouteMapGeneric extends RouteMap + ? RouteLocationNormalizedGeneric + : RouteLocationNormalizedTypedList[Name] + +/** + * Similar to {@link RouteLocationNormalized} but its `components` do not contain any function to lazy load components. + * In other words, it's ready to be rendered by ``. + */ +export type RouteLocationNormalizedLoaded< + Name extends keyof RouteMap = keyof RouteMap +> = RouteMapGeneric extends RouteMap + ? RouteLocationNormalizedLoadedGeneric + : RouteLocationNormalizedLoadedTypedList[Name] + +/** + * Route location relative to the current location. It accepts other properties than `path` like `params`, `query` and + * `hash` to conveniently change them. + */ +export type RouteLocationAsRelative< + Name extends keyof RouteMap = keyof RouteMap +> = RouteMapGeneric extends RouteMap + ? RouteLocationAsRelativeGeneric + : RouteLocationAsRelativeTypedList[Name] + +/** + * Route location resolved with {@link Router | `router.resolve()`}. + */ +export type RouteLocationResolved< + Name extends keyof RouteMap = keyof RouteMap +> = RouteMapGeneric extends RouteMap + ? RouteLocationResolvedGeneric + : RouteLocationResolvedTypedList[Name] + +/** + * Same as {@link RouteLocationAsPath} but as a string literal. + */ +export type RouteLocationAsString< + Name extends keyof RouteMap = keyof RouteMap +> = RouteMapGeneric extends RouteMap + ? string + : _LiteralUnion[Name], string> + +/** + * Route location as an object with a `path` property. + */ +export type RouteLocationAsPath = + RouteMapGeneric extends RouteMap + ? RouteLocationAsPathGeneric + : RouteLocationAsPathTypedList[Name] + +/** + * Route location that can be passed to `router.push()` and other user-facing APIs. + */ +export type RouteLocationRaw = + RouteMapGeneric extends RouteMap + ? + | RouteLocationAsString + | RouteLocationAsRelativeGeneric + | RouteLocationAsPathGeneric + : + | _LiteralUnion[Name], string> + | RouteLocationAsRelativeTypedList[Name] + | RouteLocationAsPathTypedList[Name] diff --git a/packages/router/src/typed-routes/route-map.ts b/packages/router/src/typed-routes/route-map.ts new file mode 100644 index 000000000..b26014091 --- /dev/null +++ b/packages/router/src/typed-routes/route-map.ts @@ -0,0 +1,43 @@ +import type { TypesConfig } from '../config' +import type { + RouteMeta, + RouteParamsGeneric, + RouteParamsRawGeneric, +} from '../types' +import type { RouteRecord } from '../matcher/types' + +/** + * Helper type to define a Typed `RouteRecord` + * @see {@link RouteRecord} + */ +export interface RouteRecordInfo< + // the name cannot be nullish here as that would not allow type narrowing + Name extends string | symbol = string, + Path extends string = string, + // TODO: could probably be inferred from the Params + ParamsRaw extends RouteParamsRawGeneric = RouteParamsRawGeneric, + Params extends RouteParamsGeneric = RouteParamsGeneric, + Meta extends RouteMeta = RouteMeta +> { + name: Name + path: Path + paramsRaw: ParamsRaw + params: Params + // TODO: implement meta with a defineRoute macro + meta: Meta +} + +/** + * Convenience type to get the typed RouteMap or a generic one if not provided. It is extracted from the {@link TypesConfig} if it exists, it becomes {@link RouteMapGeneric} otherwise. + */ +export type RouteMap = TypesConfig extends Record< + 'RouteNamedMap', + infer RouteNamedMap +> + ? RouteNamedMap + : RouteMapGeneric + +/** + * Generic version of the `RouteMap`. + */ +export type RouteMapGeneric = Record diff --git a/packages/router/src/typed-routes/route-records.ts b/packages/router/src/typed-routes/route-records.ts new file mode 100644 index 000000000..d7d9d2022 --- /dev/null +++ b/packages/router/src/typed-routes/route-records.ts @@ -0,0 +1,35 @@ +import type { + RouteLocation, + RouteLocationNormalized, + RouteLocationRaw, +} from './route-location' +import type { RouteMap, RouteMapGeneric } from './route-map' + +/** + * @internal + */ +export type RouteRecordRedirectOption = + | RouteLocationRaw + | ((to: RouteLocation) => RouteLocationRaw) + +/** + * Generic version of {@link RouteRecordName}. + */ +export type RouteRecordNameGeneric = string | symbol | undefined + +/** + * Possible values for a route record **after normalization** + * + * NOTE: since `RouteRecordName` is a type, it evaluates too early and it's often the generic version {@link RouteRecordNameGeneric}. If you need a typed version of all of the names of routes, use {@link RouteMap | `keyof RouteMap`} + */ +export type RouteRecordName = RouteMapGeneric extends RouteMap + ? RouteRecordNameGeneric + : keyof RouteMap + +/** + * @internal + */ +export type _RouteRecordProps = + | boolean + | Record + | ((to: RouteLocationNormalized) => Record) diff --git a/packages/router/src/types/index.ts b/packages/router/src/types/index.ts index 9c2396b21..e3069740b 100644 --- a/packages/router/src/types/index.ts +++ b/packages/router/src/types/index.ts @@ -1,9 +1,16 @@ -import { LocationQuery, LocationQueryRaw } from '../query' -import { PathParserOptions } from '../matcher' -import { Ref, ComponentPublicInstance, Component, DefineComponent } from 'vue' -import { RouteRecord, RouteRecordNormalized } from '../matcher/types' -import { HistoryState } from '../history/common' -import { NavigationFailure } from '../errors' +import type { LocationQuery, LocationQueryRaw } from '../query' +import type { PathParserOptions } from '../matcher' +import type { Ref, Component, DefineComponent } from 'vue' +import type { RouteRecord, RouteRecordNormalized } from '../matcher/types' +import type { HistoryState } from '../history/common' +import type { + NavigationGuardWithThis, + RouteLocation, + RouteRecordRedirectOption, + _RouteRecordProps, + RouteRecordNameGeneric, +} from '../typed-routes' +import type { _Awaitable } from './utils' export type Lazy = () => Promise export type Override = Pick> & U @@ -32,8 +39,11 @@ export type RouteParamValue = string * @internal */ export type RouteParamValueRaw = RouteParamValue | number | null | undefined -export type RouteParams = Record -export type RouteParamsRaw = Record< +export type RouteParamsGeneric = Record< + string, + RouteParamValue | RouteParamValue[] +> +export type RouteParamsRawGeneric = Record< string, RouteParamValueRaw | Exclude[] > @@ -57,13 +67,13 @@ export interface MatcherLocationAsPath { * @internal */ export interface MatcherLocationAsName { - name: RouteRecordName + name: RouteRecordNameGeneric // to allow checking location.path == null /** * Ignored path property since we are dealing with a relative location. Only `undefined` is allowed. */ path?: undefined - params?: RouteParams + params?: RouteParamsGeneric } /** @@ -75,20 +85,20 @@ export interface MatcherLocationAsRelative { * Ignored path property since we are dealing with a relative location. Only `undefined` is allowed. */ path?: undefined - params?: RouteParams + params?: RouteParamsGeneric } /** * @internal */ export interface LocationAsRelativeRaw { - name?: RouteRecordName + name?: RouteRecordNameGeneric // to allow checking location.path == null /** * Ignored path property since we are dealing with a relative location. Only `undefined` is allowed. */ path?: undefined - params?: RouteParamsRaw + params?: RouteParamsRawGeneric } /** @@ -113,14 +123,6 @@ export interface RouteLocationOptions { state?: HistoryState } -/** - * User-level route location - */ -export type RouteLocationRaw = - | string - | RouteLocationPathRaw - | RouteLocationNamedRaw - /** * Route Location that can infer the necessary params based on the name. * @@ -141,6 +143,7 @@ export interface RouteLocationPathRaw MatcherLocationAsPath, RouteLocationOptions {} +// TODO: rename in next major to RouteRecordMatched? export interface RouteLocationMatched extends RouteRecordNormalized { // components cannot be Lazy components: Record | null | undefined @@ -173,43 +176,6 @@ export interface _RouteLocationBase redirectedFrom: RouteLocation | undefined } -// matched contains resolved components -/** - * {@link RouteLocationRaw} with - */ -export interface RouteLocationNormalizedLoaded extends _RouteLocationBase { - /** - * Array of {@link RouteLocationMatched} containing only plain components (any - * lazy-loaded components have been loaded and were replaced inside the - * `components` object) so it can be directly used to display routes. It - * cannot contain redirect records either - */ - matched: RouteLocationMatched[] // non-enumerable -} - -/** - * {@link RouteLocationRaw} resolved using the matcher - */ -export interface RouteLocation extends _RouteLocationBase { - /** - * Array of {@link RouteRecord} containing components as they were - * passed when adding records. It can also contain redirect records. This - * can't be used directly - */ - matched: RouteRecord[] // non-enumerable -} - -/** - * Similar to {@link RouteLocation} but its - * {@link RouteLocationNormalized.matched} cannot contain redirect records - */ -export interface RouteLocationNormalized extends _RouteLocationBase { - /** - * Array of {@link RouteRecordNormalized} - */ - matched: RouteRecordNormalized[] // non-enumerable -} - /** * Allowed Component in {@link RouteLocationMatched} */ @@ -219,19 +185,6 @@ export type RouteComponent = Component | DefineComponent */ export type RawRouteComponent = RouteComponent | Lazy -/** - * Possible values for a user-defined route record's name - */ -export type RouteRecordName = string | symbol - -/** - * @internal - */ -export type _RouteRecordProps = - | boolean - | Record - | ((to: RouteLocationNormalized) => Record) - // TODO: could this be moved to matcher? /** * Internal type for common properties among all kind of {@link RouteRecordRaw}. @@ -262,7 +215,7 @@ export interface _RouteRecordBase extends PathParserOptions { /** * Name for the route record. Must be unique. */ - name?: RouteRecordName + name?: RouteRecordNameGeneric /** * Before Enter guard specific to this record. Note `beforeEnter` has no @@ -306,13 +259,6 @@ export interface _RouteRecordBase extends PathParserOptions { */ export interface RouteMeta extends Record {} -/** - * @internal - */ -export type RouteRecordRedirectOption = - | RouteLocationRaw - | ((to: RouteLocation) => RouteLocationRaw) - /** * Route Record defining one single component with the `component` option. */ @@ -407,33 +353,6 @@ export type RouteRecordRaw = | RouteRecordMultipleViewsWithChildren | RouteRecordRedirect -/** - * Initial route location where the router is. Can be used in navigation guards - * to differentiate the initial navigation. - * - * @example - * ```js - * import { START_LOCATION } from 'vue-router' - * - * router.beforeEach((to, from) => { - * if (from === START_LOCATION) { - * // initial navigation - * } - * }) - * ``` - */ -export const START_LOCATION_NORMALIZED: RouteLocationNormalizedLoaded = { - path: '/', - name: undefined, - params: {}, - query: {}, - hash: '', - fullPath: '/', - matched: [], - meta: {}, - redirectedFrom: undefined, -} - // make matched non-enumerable for easy printing // NOTE: commented for tests at RouterView.spec // Object.defineProperty(START_LOCATION_NORMALIZED, 'matched', { @@ -457,7 +376,7 @@ export interface MatcherLocation { /** * Name of the matched record */ - name: RouteRecordName | null | undefined + name: RouteRecordNameGeneric | null | undefined /** * Percentage encoded pathname section of the URL. @@ -467,7 +386,7 @@ export interface MatcherLocation { /** * Object of decoded params extracted from the `path`. */ - params: RouteParams + params: RouteParamsGeneric /** * Merged `meta` properties from all the matched route records. @@ -482,67 +401,6 @@ export interface MatcherLocation { matched: RouteRecord[] // non-enumerable } -export interface NavigationGuardNext { - (): void - (error: Error): void - (location: RouteLocationRaw): void - (valid: boolean | undefined): void - (cb: NavigationGuardNextCallback): void - /** - * Allows to detect if `next` isn't called in a resolved guard. Used - * internally in DEV mode to emit a warning. Commented out to simplify - * typings. - * @internal - */ - // _called: boolean -} - -export type NavigationGuardNextCallback = ( - vm: ComponentPublicInstance -) => unknown - -export type NavigationGuardReturn = - | void - | Error - | RouteLocationRaw - | boolean - // FIXME: this one is only allowed in options api - | NavigationGuardNextCallback - -/** - * Navigation guard. See [Navigation - * Guards](/guide/advanced/navigation-guards.md). - */ -export interface NavigationGuard { - ( - // TODO: we could maybe add extra information like replace: true/false - to: RouteLocationNormalized, - from: RouteLocationNormalized, - next: NavigationGuardNext - // FIXME: this one shouldn't allow returning () => ... - ): NavigationGuardReturn | Promise -} - -/** - * {@inheritDoc NavigationGuard} - */ -export interface NavigationGuardWithThis { - ( - this: T, - to: RouteLocationNormalized, - from: RouteLocationNormalized, - next: NavigationGuardNext - ): NavigationGuardReturn | Promise -} - -export interface NavigationHookAfter { - ( - to: RouteLocationNormalized, - from: RouteLocationNormalized, - failure?: NavigationFailure | void - ): any -} - export * from './typeGuards' export type Mutable = { diff --git a/packages/router/src/types/typeGuards.ts b/packages/router/src/types/typeGuards.ts index 782ef08c0..ba30bd9b6 100644 --- a/packages/router/src/types/typeGuards.ts +++ b/packages/router/src/types/typeGuards.ts @@ -1,9 +1,9 @@ -import { RouteLocationRaw, RouteRecordName } from './index' +import type { RouteLocationRaw, RouteRecordNameGeneric } from '../typed-routes' export function isRouteLocation(route: any): route is RouteLocationRaw { return typeof route === 'string' || (route && typeof route === 'object') } -export function isRouteName(name: any): name is RouteRecordName { +export function isRouteName(name: any): name is RouteRecordNameGeneric { return typeof name === 'string' || typeof name === 'symbol' } diff --git a/packages/router/src/types/utils.ts b/packages/router/src/types/utils.ts index b5874a260..e7d163184 100644 --- a/packages/router/src/types/utils.ts +++ b/packages/router/src/types/utils.ts @@ -1,10 +1,17 @@ /** + * Creates a union type that still allows autocompletion for strings. * @internal */ export type _LiteralUnion = | LiteralType | (BaseType & Record) +/** + * Maybe a promise maybe not + * @internal + */ +export type _Awaitable = T | PromiseLike + /** * @internal */ diff --git a/packages/router/src/useApi.ts b/packages/router/src/useApi.ts index 9bf578539..988430014 100644 --- a/packages/router/src/useApi.ts +++ b/packages/router/src/useApi.ts @@ -1,7 +1,8 @@ import { inject } from 'vue' import { routerKey, routeLocationKey } from './injectionSymbols' import { Router } from './router' -import { RouteLocationNormalizedLoaded } from './types' +import { RouteMap } from './typed-routes/route-map' +import { RouteLocationNormalizedLoaded } from './typed-routes' /** * Returns the router instance. Equivalent to using `$router` inside @@ -15,6 +16,8 @@ export function useRouter(): Router { * Returns the current route location. Equivalent to using `$route` inside * templates. */ -export function useRoute(): RouteLocationNormalizedLoaded { +export function useRoute( + _name?: Name +): RouteLocationNormalizedLoaded { return inject(routeLocationKey)! } diff --git a/packages/router/src/utils/index.ts b/packages/router/src/utils/index.ts index af90cb5ce..cb5ac7bc4 100644 --- a/packages/router/src/utils/index.ts +++ b/packages/router/src/utils/index.ts @@ -1,7 +1,7 @@ import { - RouteParams, + RouteParamsGeneric, RouteComponent, - RouteParamsRaw, + RouteParamsRawGeneric, RouteParamValueRaw, } from '../types' @@ -15,9 +15,9 @@ export const assign = Object.assign export function applyToParams( fn: (v: string | number | null | undefined) => string, - params: RouteParamsRaw | undefined -): RouteParams { - const newParams: RouteParams = {} + params: RouteParamsRawGeneric | undefined +): RouteParamsGeneric { + const newParams: RouteParamsGeneric = {} for (const key in params) { const value = params[key] diff --git a/packages/router/test-dts/components.test-d.tsx b/packages/router/test-dts/components.test-d.tsx index e34707fdd..532f66bac 100644 --- a/packages/router/test-dts/components.test-d.tsx +++ b/packages/router/test-dts/components.test-d.tsx @@ -4,9 +4,8 @@ import { RouterView, createRouter, createMemoryHistory, - expectError, - expectType, } from './index' +import { expectTypeOf } from 'vitest' let router = createRouter({ history: createMemoryHistory(), @@ -20,13 +19,13 @@ expectError() expectError() // @ts-expect-error: invalid prop expectError() -expectType() -expectType() -expectType() -expectType() -expectType() +expectTypeOf() +expectTypeOf() +expectTypeOf() +expectTypeOf() +expectTypeOf() // RouterView -expectType() -expectType() -expectType() +expectTypeOf() +expectTypeOf() +expectTypeOf() diff --git a/packages/router/test-dts/createRouter.test-d.ts b/packages/router/test-dts/createRouter.test-d.ts deleted file mode 100644 index 75ff3b872..000000000 --- a/packages/router/test-dts/createRouter.test-d.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { - createRouter, - createWebHistory, - NavigationGuard, - NavigationGuardNext, - RouteLocationNormalized, -} from './index' -import { createApp, defineComponent, h } from 'vue' - -const component = defineComponent({}) - -const WithProps = defineComponent({ - props: { - id: { - type: String, - required: true, - }, - }, -}) - -const Foo = defineComponent({ - props: { - test: String, - }, - setup() { - return { - title: 'homepage', - } - }, - render() { - return h('div', `${this.title}: ${this.test}`) - }, -}) - -const router = createRouter({ - history: createWebHistory(), - routes: [ - { path: '/', component }, - { path: '/foo', component: Foo }, - { path: '/', component: WithProps }, - ], - parseQuery: search => ({}), - stringifyQuery: query => '', - strict: true, - end: true, - sensitive: true, - scrollBehavior(to, from, savedPosition) {}, -}) - -export const loggedInGuard: NavigationGuard = (to, from, next) => next('/') -function beforeGuardFn( - to: RouteLocationNormalized, - from: RouteLocationNormalized, - next: NavigationGuardNext -) {} - -router.beforeEach(loggedInGuard) -router.beforeEach(beforeGuardFn) - -const app = createApp({}) -app.use(router) diff --git a/packages/router/test-dts/index.d.ts b/packages/router/test-dts/index.d.ts index 1008e4d73..e7c8be7b6 100644 --- a/packages/router/test-dts/index.d.ts +++ b/packages/router/test-dts/index.d.ts @@ -1,7 +1,2 @@ export * from '../dist/vue-router' // export * from '../src' - -export function describe(_name: string, _fn: () => void): void -export function expectType(value: T): void -export function expectError(value: T): void -export function expectAssignable(value: T2): void diff --git a/packages/router/test-dts/legacy.test-d.ts b/packages/router/test-dts/legacy.test-d.ts index cd28179b4..8f129a7e6 100644 --- a/packages/router/test-dts/legacy.test-d.ts +++ b/packages/router/test-dts/legacy.test-d.ts @@ -1,11 +1,12 @@ -import { Router, RouteLocationNormalizedLoaded, expectType } from './index' +import { expectTypeOf } from 'vitest' +import { Router, RouteLocationNormalizedLoaded } from './index' import { defineComponent } from 'vue' defineComponent({ methods: { doStuff() { - expectType(this.$router) - expectType(this.$route) + expectTypeOf(this.$router) + expectTypeOf(this.$route) }, }, }) diff --git a/packages/router/test-dts/meta.test-d.ts b/packages/router/test-dts/meta.test-d.ts index 5e3d6e9b2..32e2010fa 100644 --- a/packages/router/test-dts/meta.test-d.ts +++ b/packages/router/test-dts/meta.test-d.ts @@ -1,5 +1,6 @@ -import { createRouter, createWebHistory, expectType } from './index' -import { createApp, defineComponent } from 'vue' +import { createRouter, createWebHistory } from './index' +import { defineComponent } from 'vue' +import { describe, it, expectTypeOf } from 'vitest' const component = defineComponent({}) @@ -10,34 +11,41 @@ declare module './index' { } } -const router = createRouter({ - history: createWebHistory(), - routes: [ - { - path: '/', - component, - meta: { - requiresAuth: true, - lol: true, - nested: { - foo: 'bar', +describe('RouteMeta', () => { + it('route creation', () => { + const router = createRouter({ + history: createWebHistory(), + routes: [ + { + path: '/', + component, + meta: { + requiresAuth: true, + lol: true, + nested: { + foo: 'bar', + }, + }, }, - }, - }, - { - path: '/foo', - component, - // @ts-expect-error - meta: {}, - }, - ], -}) + { + path: '/foo', + component, + // @ts-expect-error + meta: {}, + }, + ], + }) + }) -router.beforeEach(to => { - expectType<{ requiresAuth?: Boolean; nested: { foo: string } }>(to.meta) - expectType(to.meta.lol) - if (to.meta.nested.foo == 'foo' || to.meta.lol) return false + it('route location in guards', () => { + const router = createRouter({ + history: createWebHistory(), + routes: [], + }) + router.beforeEach(to => { + expectTypeOf<{ requiresAuth?: Boolean; nested: { foo: string } }>(to.meta) + expectTypeOf(to.meta.lol) + if (to.meta.nested.foo == 'foo' || to.meta.lol) return false + }) + }) }) - -const app = createApp({}) -app.use(router) diff --git a/packages/router/test-dts/navigationGuards.test-d.ts b/packages/router/test-dts/navigationGuards.test-d.ts index 71a244eb8..6bf24ed8a 100644 --- a/packages/router/test-dts/navigationGuards.test-d.ts +++ b/packages/router/test-dts/navigationGuards.test-d.ts @@ -1,7 +1,7 @@ +import { expectTypeOf } from 'vitest' import { createRouter, createWebHistory, - expectType, isNavigationFailure, NavigationFailure, NavigationFailureType, @@ -45,13 +45,13 @@ router.beforeEach((to, from, next) => { }) router.afterEach((to, from, failure) => { - expectType(failure) + expectTypeOf(failure) if (isNavigationFailure(failure)) { - expectType(failure.from) - expectType(failure.to) + expectTypeOf(failure.from) + expectTypeOf(failure.to) } if (isNavigationFailure(failure, NavigationFailureType.cancelled)) { - expectType(failure.from) - expectType(failure.to) + expectTypeOf(failure.from) + expectTypeOf(failure.to) } }) diff --git a/packages/router/test-dts/tsconfig.json b/packages/router/test-dts/tsconfig.json index 1a6da3927..1d95ccdf2 100644 --- a/packages/router/test-dts/tsconfig.json +++ b/packages/router/test-dts/tsconfig.json @@ -4,10 +4,19 @@ "noEmit": true, "declaration": true, "paths": { - "vue-router": ["../dist"] + "vue-router": [ + "../dist" + ] }, "noImplicitReturns": false }, - "include": ["./"], - "exclude": ["../__tests__", "../src"] + "include": [ + "./", + "components.test-d.tsx", + "../__tests__/createRouter.test-d.ts" + ], + "exclude": [ + "../__tests__", + "../src" + ] } diff --git a/packages/router/test-dts/typed-routes.test-d.ts b/packages/router/test-dts/typed-routes.test-d.ts new file mode 100644 index 000000000..e12bdab2a --- /dev/null +++ b/packages/router/test-dts/typed-routes.test-d.ts @@ -0,0 +1,139 @@ +import { describe, it, expectTypeOf } from 'vitest' +import { + type RouteRecordInfo, + type ParamValue, + type ParamValueOneOrMore, + type RouteLocationTyped, + createRouter, + createWebHistory, +} from './index' + +// type is needed instead of an interface +// https://github.com/microsoft/TypeScript/issues/15300 +type RouteMap = { + '/[...path]': RouteRecordInfo< + '/[...path]', + '/:path(.*)', + { path: ParamValue }, + { path: ParamValue } + > + '/[a]': RouteRecordInfo< + '/[a]', + '/:a', + { a: ParamValue }, + { a: ParamValue } + > + '/a': RouteRecordInfo<'/a', '/a', Record, Record> + '/[id]+': RouteRecordInfo< + '/[id]+', + '/:id+', + { id: ParamValueOneOrMore }, + { id: ParamValueOneOrMore } + > +} + +declare module './index' { + interface TypesConfig { + RouteNamedMap: RouteMap + } +} + +describe('RouterTyped', () => { + const router = createRouter({ + history: createWebHistory(), + routes: [], + }) + + it('resolve', () => { + expectTypeOf>(router.resolve({ name: '/a' }).params) + expectTypeOf<{ a: ParamValue }>( + router.resolve({ name: '/[a]' }).params + ) + + expectTypeOf>( + router.resolve({ name: '/a' }) + ) + expectTypeOf<'/a'>( + // @ts-expect-error: cannot infer based on path + router.resolve({ path: '/a' }).name + ) + expectTypeOf(router.resolve({ path: '/a' }).name) + }) + + it('resolve', () => { + router.push({ name: '/a', params: { a: 2 } }) + // @ts-expect-error + router.push({ name: '/[a]', params: {} }) + // still allow relative params + router.push({ name: '/[a]' }) + // @ts-expect-error + router.push({ name: '/[a]', params: { a: [2] } }) + router.push({ name: '/[id]+', params: { id: [2] } }) + router.push({ name: '/[id]+', params: { id: [2, '3'] } }) + // @ts-expect-error + router.push({ name: '/[id]+', params: { id: 2 } }) + }) + + it('beforeEach', () => { + router.beforeEach((to, from) => { + // @ts-expect-error: no route named this way + if (to.name === '/[id]') { + } else if (to.name === '/[a]') { + expectTypeOf<{ a: ParamValue }>(to.params) + } + // @ts-expect-error: no route named this way + if (from.name === '/[id]') { + } else if (to.name === '/[a]') { + expectTypeOf<{ a: ParamValue }>(to.params) + } + if (Math.random()) { + return { name: '/[a]', params: { a: 2 } } + } else if (Math.random()) { + return '/any route does' + } + return true + }) + }) + + it('beforeResolve', () => { + router.beforeResolve((to, from) => { + // @ts-expect-error: no route named this way + if (to.name === '/[id]') { + } else if (to.name === '/[a]') { + expectTypeOf<{ a: ParamValue }>(to.params) + } + // @ts-expect-error: no route named this way + if (from.name === '/[id]') { + } else if (to.name === '/[a]') { + expectTypeOf<{ a: ParamValue }>(to.params) + } + if (Math.random()) { + return { name: '/[a]', params: { a: 2 } } + } else if (Math.random()) { + return '/any route does' + } + return true + }) + }) + + it('afterEach', () => { + router.afterEach((to, from) => { + // @ts-expect-error: no route named this way + if (to.name === '/[id]') { + } else if (to.name === '/[a]') { + expectTypeOf<{ a: ParamValue }>(to.params) + } + // @ts-expect-error: no route named this way + if (from.name === '/[id]') { + } else if (to.name === '/[a]') { + expectTypeOf<{ a: ParamValue }>(to.params) + } + if (Math.random()) { + return { name: '/[a]', params: { a: 2 } } + } else if (Math.random()) { + return '/any route does' + } + return true + }) + }) +}) diff --git a/packages/router/tsconfig.json b/packages/router/tsconfig.json index 0a32fcb75..22219c6f0 100644 --- a/packages/router/tsconfig.json +++ b/packages/router/tsconfig.json @@ -32,7 +32,6 @@ "dom" ], "types": [ - "jest", "node", "vite/client" ] diff --git a/packages/router/vitest.config.ts b/packages/router/vitest.config.ts new file mode 100644 index 000000000..bb42313f7 --- /dev/null +++ b/packages/router/vitest.config.ts @@ -0,0 +1,40 @@ +import { defineConfig } from 'vitest/config' +import Vue from '@vitejs/plugin-vue' +import { fileURLToPath } from 'node:url' + +const __dirname = new URL('.', import.meta.url).pathname +export default defineConfig({ + resolve: { + alias: [], + }, + define: { + __DEV__: true, + __TEST__: true, + __BROWSER__: true, + }, + plugins: [Vue()], + + test: { + // open: false, + coverage: { + include: ['src/**/*.ts'], + exclude: [ + 'src/**/*.d.ts', + 'src/**/*.test-d.ts', + 'src/**/*.spec.ts', + // '/node_modules/', + 'src/index.ts', + 'src/devtools.ts', + ], + }, + typecheck: { + enabled: true, + checker: 'vue-tsc', + // only: true, + // by default it includes all specs too + include: ['**/*.test-d.ts'], + + // tsconfig: './tsconfig.typecheck.json', + }, + }, +}) diff --git a/packages/router/vitest.workspace.js b/packages/router/vitest.workspace.js new file mode 100644 index 000000000..45759c07c --- /dev/null +++ b/packages/router/vitest.workspace.js @@ -0,0 +1,3 @@ +import { defineWorkspace } from 'vitest/config' + +export default defineWorkspace(['./vitest.config.ts']) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8e88c91d0..e5840010a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -121,12 +121,6 @@ importers: '@rollup/plugin-terser': specifier: ^0.4.4 version: 0.4.4(rollup@3.29.4) - '@sucrase/jest-plugin': - specifier: ^3.0.0 - version: 3.0.0(jest@29.7.0)(sucrase@3.34.0) - '@types/jest': - specifier: ^29.5.12 - version: 29.5.12 '@types/jsdom': specifier: ^21.1.6 version: 21.1.6 @@ -136,6 +130,12 @@ importers: '@vitejs/plugin-vue': specifier: ^5.0.4 version: 5.0.4(vite@5.2.9)(vue@3.4.23) + '@vitest/coverage-v8': + specifier: ^1.6.0 + version: 1.6.0(vitest@1.6.0) + '@vitest/ui': + specifier: ^1.6.0 + version: 1.6.0(vitest@1.6.0) '@vue/compiler-sfc': specifier: ^3.4.23 version: 3.4.23 @@ -166,15 +166,6 @@ importers: geckodriver: specifier: ^3.2.0 version: 3.2.0 - jest: - specifier: ^29.7.0 - version: 29.7.0 - jest-environment-jsdom: - specifier: ^29.7.0 - version: 29.7.0 - jest-mock-warn: - specifier: ^1.1.0 - version: 1.1.0 nightwatch: specifier: ^2.6.22 version: 2.6.24(chromedriver@121.0.2)(geckodriver@3.2.0) @@ -202,6 +193,9 @@ importers: vite: specifier: ^5.2.9 version: 5.2.9(@types/node@20.12.7) + vitest: + specifier: ^1.6.0 + version: 1.6.0(@vitest/ui@1.6.0) vue: specifier: ^3.4.23 version: 3.4.23(typescript@5.3.3) @@ -374,115 +368,6 @@ packages: chalk: 2.4.2 dev: true - /@babel/compat-data@7.22.9: - resolution: {integrity: sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/core@7.22.11: - resolution: {integrity: sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.22.10 - '@babel/generator': 7.22.10 - '@babel/helper-compilation-targets': 7.22.10 - '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.11) - '@babel/helpers': 7.22.11 - '@babel/parser': 7.24.4 - '@babel/template': 7.22.5 - '@babel/traverse': 7.22.11 - '@babel/types': 7.24.0 - convert-source-map: 1.9.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/generator@7.22.10: - resolution: {integrity: sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.22.11 - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.19 - jsesc: 2.5.2 - dev: true - - /@babel/helper-compilation-targets@7.22.10: - resolution: {integrity: sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/compat-data': 7.22.9 - '@babel/helper-validator-option': 7.22.5 - browserslist: 4.21.10 - lru-cache: 5.1.1 - semver: 6.3.1 - dev: true - - /@babel/helper-environment-visitor@7.22.5: - resolution: {integrity: sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-function-name@7.22.5: - resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.22.5 - '@babel/types': 7.24.0 - dev: true - - /@babel/helper-hoist-variables@7.22.5: - resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.0 - dev: true - - /@babel/helper-module-imports@7.22.5: - resolution: {integrity: sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.0 - dev: true - - /@babel/helper-module-transforms@7.22.9(@babel/core@7.22.11): - resolution: {integrity: sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-module-imports': 7.22.5 - '@babel/helper-simple-access': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.20 - dev: true - - /@babel/helper-plugin-utils@7.22.5: - resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-simple-access@7.22.5: - resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.0 - dev: true - - /@babel/helper-split-export-declaration@7.22.6: - resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.0 - dev: true - /@babel/helper-string-parser@7.24.1: resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==} engines: {node: '>=6.9.0'} @@ -491,22 +376,6 @@ packages: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-option@7.22.5: - resolution: {integrity: sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helpers@7.22.11: - resolution: {integrity: sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.22.5 - '@babel/traverse': 7.22.11 - '@babel/types': 7.24.0 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/highlight@7.22.10: resolution: {integrity: sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==} engines: {node: '>=6.9.0'} @@ -523,171 +392,6 @@ packages: dependencies: '@babel/types': 7.24.0 - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.22.11): - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.22.11): - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.22.11): - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.22.11): - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.22.11): - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.22.11): - resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.22.11): - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.22.11): - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.22.11): - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.22.11): - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.22.11): - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.22.11): - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.22.11): - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.22.11): - resolution: {integrity: sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/template@7.22.5: - resolution: {integrity: sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.22.10 - '@babel/parser': 7.24.4 - '@babel/types': 7.24.0 - dev: true - - /@babel/traverse@7.22.11: - resolution: {integrity: sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.22.10 - '@babel/generator': 7.22.10 - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-function-name': 7.22.5 - '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.24.4 - '@babel/types': 7.24.0 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/types@7.22.11: - resolution: {integrity: sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.24.1 - '@babel/helper-validator-identifier': 7.22.20 - to-fast-properties: 2.0.0 - dev: true - /@babel/types@7.24.0: resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} engines: {node: '>=6.9.0'} @@ -951,292 +655,81 @@ packages: wrap-ansi-cjs: /wrap-ansi@7.0.0 dev: true - /@istanbuljs/load-nyc-config@1.1.0: - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} - engines: {node: '>=8'} - dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.14.1 - resolve-from: 5.0.0 - dev: true - /@istanbuljs/schema@0.1.3: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} dev: true - /@jest/console@29.7.0: - resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.6.3 - '@types/node': 20.12.7 - chalk: 4.1.2 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 + '@sinclair/typebox': 0.27.8 dev: true - /@jest/core@29.7.0: - resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.12.7 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.8.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.12.7) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.5 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.19 dev: true - /@jest/environment@29.7.0: - resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.11.19 - jest-mock: 29.7.0 + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} dev: true - /@jest/expect-utils@29.7.0: - resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.6.3 + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} dev: true - /@jest/expect@29.7.0: - resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + /@jridgewell/source-map@0.3.5: + resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==} dependencies: - expect: 29.7.0 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.19 dev: true - /@jest/fake-timers@29.7.0: - resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + + /@jridgewell/trace-mapping@0.3.19: + resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} dependencies: - '@jest/types': 29.6.3 - '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.11.19 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-util: 29.7.0 + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /@jest/globals@29.7.0: - resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + /@jridgewell/trace-mapping@0.3.25: + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/types': 29.6.3 - jest-mock: 29.7.0 - transitivePeerDependencies: - - supports-color + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /@jest/reporters@29.7.0: - resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true + /@kwsites/file-exists@1.1.1: + resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==} dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.19 - '@types/node': 20.12.7 - chalk: 4.1.2 - collect-v8-coverage: 1.0.2 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.0 - istanbul-lib-instrument: 6.0.0 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.6 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - jest-worker: 29.7.0 - slash: 3.0.0 - string-length: 4.0.2 - strip-ansi: 6.0.1 - v8-to-istanbul: 9.1.0 + debug: 4.3.4 transitivePeerDependencies: - supports-color - dev: true + dev: false - /@jest/schemas@29.6.3: - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + /@kwsites/promise-deferred@1.1.1: + resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==} + dev: false + + /@microsoft/api-extractor-model@7.28.9: + resolution: {integrity: sha512-lM77dV+VO46MGp5lu4stUBnO3jyr+CrDzU+DtapcOQEZUqJxPYUoK5zjeD+gRZ9ckgGMZC94ch6FBkpmsjwQgw==} dependencies: - '@sinclair/typebox': 0.27.8 - dev: true - - /@jest/source-map@29.6.3: - resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jridgewell/trace-mapping': 0.3.19 - callsites: 3.1.0 - graceful-fs: 4.2.11 - dev: true - - /@jest/test-result@29.7.0: - resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.7.0 - '@jest/types': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.4 - collect-v8-coverage: 1.0.2 - dev: true - - /@jest/test-sequencer@29.7.0: - resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.7.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - slash: 3.0.0 - dev: true - - /@jest/transform@29.7.0: - resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.22.11 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.19 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - micromatch: 4.0.5 - pirates: 4.0.6 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@jest/types@29.6.3: - resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 20.12.7 - '@types/yargs': 17.0.24 - chalk: 4.1.2 - dev: true - - /@jridgewell/gen-mapping@0.3.3: - resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.19 - dev: true - - /@jridgewell/resolve-uri@3.1.1: - resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} - engines: {node: '>=6.0.0'} - dev: true - - /@jridgewell/set-array@1.1.2: - resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} - engines: {node: '>=6.0.0'} - dev: true - - /@jridgewell/source-map@0.3.5: - resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==} - dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.19 - dev: true - - /@jridgewell/sourcemap-codec@1.4.15: - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - - /@jridgewell/trace-mapping@0.3.19: - resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} - dependencies: - '@jridgewell/resolve-uri': 3.1.1 - '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - - /@kwsites/file-exists@1.1.1: - resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==} - dependencies: - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: false - - /@kwsites/promise-deferred@1.1.1: - resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==} - dev: false - - /@microsoft/api-extractor-model@7.28.9: - resolution: {integrity: sha512-lM77dV+VO46MGp5lu4stUBnO3jyr+CrDzU+DtapcOQEZUqJxPYUoK5zjeD+gRZ9ckgGMZC94ch6FBkpmsjwQgw==} - dependencies: - '@microsoft/tsdoc': 0.14.2 - '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 3.66.0 - transitivePeerDependencies: - - '@types/node' + '@microsoft/tsdoc': 0.14.2 + '@microsoft/tsdoc-config': 0.16.2 + '@rushstack/node-core-library': 3.66.0 + transitivePeerDependencies: + - '@types/node' dev: true /@microsoft/api-extractor@7.40.1: @@ -1320,6 +813,10 @@ packages: dev: true optional: true + /@polka/url@1.0.0-next.25: + resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==} + dev: true + /@rollup/plugin-alias@5.1.0(rollup@3.29.4): resolution: {integrity: sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==} engines: {node: '>=14.0.0'} @@ -1590,28 +1087,6 @@ packages: engines: {node: '>=18'} dev: true - /@sinonjs/commons@3.0.0: - resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==} - dependencies: - type-detect: 4.0.8 - dev: true - - /@sinonjs/fake-timers@10.3.0: - resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - dependencies: - '@sinonjs/commons': 3.0.0 - dev: true - - /@sucrase/jest-plugin@3.0.0(jest@29.7.0)(sucrase@3.34.0): - resolution: {integrity: sha512-VRY6YKYImVWiRg1H3Yu24hwB1UPJDSDR62R/n+lOHR3+yDrfHEIAoddJivblMYN6U3vD+ndfTSrecZ9Jl+iGNw==} - peerDependencies: - jest: '>=27' - sucrase: '>=3.25.0' - dependencies: - jest: 29.7.0 - sucrase: 3.34.0 - dev: true - /@szmarczak/http-timer@4.0.6: resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} engines: {node: '>=10'} @@ -1632,35 +1107,6 @@ packages: resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} dev: true - /@types/babel__core@7.20.1: - resolution: {integrity: sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==} - dependencies: - '@babel/parser': 7.24.4 - '@babel/types': 7.24.0 - '@types/babel__generator': 7.6.4 - '@types/babel__template': 7.4.1 - '@types/babel__traverse': 7.20.1 - dev: true - - /@types/babel__generator@7.6.4: - resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} - dependencies: - '@babel/types': 7.24.0 - dev: true - - /@types/babel__template@7.4.1: - resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} - dependencies: - '@babel/parser': 7.24.4 - '@babel/types': 7.24.0 - dev: true - - /@types/babel__traverse@7.20.1: - resolution: {integrity: sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==} - dependencies: - '@babel/types': 7.24.0 - dev: true - /@types/cacheable-request@6.0.3: resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} dependencies: @@ -1681,47 +1127,10 @@ packages: /@types/estree@1.0.5: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - /@types/graceful-fs@4.1.6: - resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} - dependencies: - '@types/node': 20.12.7 - dev: true - /@types/http-cache-semantics@4.0.1: resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==} dev: true - /@types/istanbul-lib-coverage@2.0.4: - resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} - dev: true - - /@types/istanbul-lib-report@3.0.0: - resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} - dependencies: - '@types/istanbul-lib-coverage': 2.0.4 - dev: true - - /@types/istanbul-reports@3.0.1: - resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} - dependencies: - '@types/istanbul-lib-report': 3.0.0 - dev: true - - /@types/jest@29.5.12: - resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==} - dependencies: - expect: 29.7.0 - pretty-format: 29.7.0 - dev: true - - /@types/jsdom@20.0.1: - resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} - dependencies: - '@types/node': 20.11.19 - '@types/tough-cookie': 4.0.2 - parse5: 7.1.2 - dev: true - /@types/jsdom@21.1.6: resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==} dependencies: @@ -1794,10 +1203,6 @@ packages: '@types/ws': 8.5.5 dev: true - /@types/stack-utils@2.0.1: - resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} - dev: true - /@types/tough-cookie@4.0.2: resolution: {integrity: sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==} dev: true @@ -1812,16 +1217,6 @@ packages: '@types/node': 20.12.7 dev: true - /@types/yargs-parser@21.0.0: - resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} - dev: true - - /@types/yargs@17.0.24: - resolution: {integrity: sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==} - dependencies: - '@types/yargs-parser': 21.0.0 - dev: true - /@types/yauzl@2.10.3: resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} requiresBuild: true @@ -1844,6 +1239,83 @@ packages: vite: 5.2.9(@types/node@20.12.7) vue: 3.4.23(typescript@5.3.3) + /@vitest/coverage-v8@1.6.0(vitest@1.6.0): + resolution: {integrity: sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew==} + peerDependencies: + vitest: 1.6.0 + dependencies: + '@ampproject/remapping': 2.2.1 + '@bcoe/v8-coverage': 0.2.3 + debug: 4.3.4 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.4 + istanbul-reports: 3.1.6 + magic-string: 0.30.9 + magicast: 0.3.4 + picocolors: 1.0.0 + std-env: 3.7.0 + strip-literal: 2.1.0 + test-exclude: 6.0.0 + vitest: 1.6.0(@vitest/ui@1.6.0) + transitivePeerDependencies: + - supports-color + dev: true + + /@vitest/expect@1.6.0: + resolution: {integrity: sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==} + dependencies: + '@vitest/spy': 1.6.0 + '@vitest/utils': 1.6.0 + chai: 4.4.1 + dev: true + + /@vitest/runner@1.6.0: + resolution: {integrity: sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==} + dependencies: + '@vitest/utils': 1.6.0 + p-limit: 5.0.0 + pathe: 1.1.2 + dev: true + + /@vitest/snapshot@1.6.0: + resolution: {integrity: sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==} + dependencies: + magic-string: 0.30.9 + pathe: 1.1.2 + pretty-format: 29.7.0 + dev: true + + /@vitest/spy@1.6.0: + resolution: {integrity: sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==} + dependencies: + tinyspy: 2.2.1 + dev: true + + /@vitest/ui@1.6.0(vitest@1.6.0): + resolution: {integrity: sha512-k3Lyo+ONLOgylctiGovRKy7V4+dIN2yxstX3eY5cWFXH6WP+ooVX79YSyi0GagdTQzLmT43BF27T0s6dOIPBXA==} + peerDependencies: + vitest: 1.6.0 + dependencies: + '@vitest/utils': 1.6.0 + fast-glob: 3.3.2 + fflate: 0.8.2 + flatted: 3.3.1 + pathe: 1.1.2 + picocolors: 1.0.0 + sirv: 2.0.4 + vitest: 1.6.0(@vitest/ui@1.6.0) + dev: true + + /@vitest/utils@1.6.0: + resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} + dependencies: + diff-sequences: 29.6.3 + estree-walker: 3.0.3 + loupe: 2.3.7 + pretty-format: 29.7.0 + dev: true + /@volar/language-core@1.11.1: resolution: {integrity: sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==} dependencies: @@ -2097,21 +1569,16 @@ packages: acorn-walk: 7.2.0 dev: true - /acorn-globals@7.0.1: - resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} - dependencies: - acorn: 8.10.0 - acorn-walk: 8.2.0 - dev: true - /acorn-walk@7.2.0: resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} engines: {node: '>=0.4.0'} dev: true - /acorn-walk@8.2.0: - resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + /acorn-walk@8.3.3: + resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} engines: {node: '>=0.4.0'} + dependencies: + acorn: 8.12.0 dev: true /acorn@7.4.1: @@ -2126,6 +1593,12 @@ packages: hasBin: true dev: true + /acorn@8.12.0: + resolution: {integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + /add-stream@1.0.0: resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==} dev: true @@ -2189,13 +1662,6 @@ packages: engines: {node: '>=6'} dev: true - /ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - dependencies: - type-fest: 0.21.3 - dev: true - /ansi-escapes@6.2.0: resolution: {integrity: sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==} engines: {node: '>=14.16'} @@ -2307,78 +1773,6 @@ packages: - debug dev: true - /babel-jest@29.7.0(@babel/core@7.22.11): - resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.8.0 - dependencies: - '@babel/core': 7.22.11 - '@jest/transform': 29.7.0 - '@types/babel__core': 7.20.1 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.22.11) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /babel-plugin-istanbul@6.1.1: - resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} - engines: {node: '>=8'} - dependencies: - '@babel/helper-plugin-utils': 7.22.5 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.1 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /babel-plugin-jest-hoist@29.6.3: - resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/template': 7.22.5 - '@babel/types': 7.24.0 - '@types/babel__core': 7.20.1 - '@types/babel__traverse': 7.20.1 - dev: true - - /babel-preset-current-node-syntax@1.0.1(@babel/core@7.22.11): - resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.22.11 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.22.11) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.22.11) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.22.11) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.22.11) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.22.11) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.22.11) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.22.11) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.22.11) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.22.11) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.22.11) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.11) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.22.11) - dev: true - - /babel-preset-jest@29.6.3(@babel/core@7.22.11): - resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.22.11 - babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.11) - dev: true - /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true @@ -2452,17 +1846,6 @@ packages: resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} dev: true - /browserslist@4.21.10: - resolution: {integrity: sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - dependencies: - caniuse-lite: 1.0.30001524 - electron-to-chromium: 1.4.503 - node-releases: 2.0.13 - update-browserslist-db: 1.0.11(browserslist@4.21.10) - dev: true - /browserstack-local@1.5.5: resolution: {integrity: sha512-jKne7yosrMcptj3hqxp36TP9k0ZW2sCqhyurX24rUL4G3eT7OLgv+CSQN8iq5dtkv5IK+g+v8fWvsiC/S9KxMg==} dependencies: @@ -2475,12 +1858,6 @@ packages: - supports-color dev: true - /bser@2.1.1: - resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - dependencies: - node-int64: 0.4.0 - dev: true - /buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} dev: true @@ -2501,6 +1878,11 @@ packages: engines: {node: '>=6'} dev: true + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true + /cacheable-lookup@5.0.4: resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} engines: {node: '>=10.6.0'} @@ -2519,11 +1901,6 @@ packages: responselike: 2.0.1 dev: true - /callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - dev: true - /camelcase-keys@6.2.2: resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} engines: {node: '>=8'} @@ -2543,10 +1920,6 @@ packages: engines: {node: '>=10'} dev: true - /caniuse-lite@1.0.30001524: - resolution: {integrity: sha512-Jj917pJtYg9HSJBF95HVX3Cdr89JUyLT4IZ8SvM5aDRni95swKgYi3TgYLH5hnGfPE/U1dg6IfZ50UsIlLkwSA==} - dev: true - /chai-nightwatch@0.5.3: resolution: {integrity: sha512-38ixH/mqpY6IwnZkz6xPqx8aB5/KVR+j6VPugcir3EGOsphnWXrPH/mUt8Jp+ninL6ghY0AaJDQ10hSfCPGy/g==} engines: {node: '>= 12.0.0'} @@ -2554,6 +1927,19 @@ packages: assertion-error: 1.1.0 dev: true + /chai@4.4.1: + resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.4 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -2576,16 +1962,17 @@ packages: engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} dev: true - /char-regex@1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} - dev: true - /check-error@1.0.2: resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} dev: true - /chokidar@3.5.3: + /check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + dependencies: + get-func-name: 2.0.2 + dev: true + + /chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} dependencies: @@ -2631,15 +2018,6 @@ packages: resolution: {integrity: sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==} dev: true - /ci-info@3.8.0: - resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} - engines: {node: '>=8'} - dev: true - - /cjs-module-lexer@1.2.3: - resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} - dev: true - /cli-boxes@2.2.1: resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==} engines: {node: '>=6'} @@ -2689,15 +2067,6 @@ packages: wrap-ansi: 7.0.0 dev: true - /cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - dev: true - /clone-response@1.0.3: resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} dependencies: @@ -2709,15 +2078,6 @@ packages: engines: {node: '>=0.8'} dev: true - /co@4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} - engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - dev: true - - /collect-v8-coverage@1.0.2: - resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} - dev: true - /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -2804,6 +2164,10 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true + /confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + dev: true + /config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} dependencies: @@ -2974,37 +2338,10 @@ packages: through2: 4.0.2 dev: true - /convert-source-map@1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} - dev: true - - /convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - dev: true - /core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} dev: true - /create-jest@29.7.0: - resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.12.7) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - /cross-spawn@5.1.0: resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} dependencies: @@ -3127,15 +2464,6 @@ packages: mimic-response: 3.1.0 dev: true - /dedent@1.5.1: - resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true - dev: true - /deep-eql@4.0.1: resolution: {integrity: sha512-D/Oxqobjr+kxaHsgiQBZq9b6iAWdEj5W/JdJm8deNduAPc9CwXQ3BJJCuEqlrPXcy45iOMkGPZ0T81Dnz7UDCA==} engines: {node: '>=6'} @@ -3143,6 +2471,13 @@ packages: type-detect: 4.0.8 dev: true + /deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.0.8 + dev: true + /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true @@ -3173,11 +2508,6 @@ packages: engines: {node: '>=0.4.0'} dev: true - /detect-newline@3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} - engines: {node: '>=8'} - dev: true - /devtools-protocol@0.0.1025565: resolution: {integrity: sha512-0s5sbGQR/EfYQhd8EpZgphpndsv+CufTlaeUyA6vYXCA0H5kMAsHCS/cHtUFWoKJCO125hpoKicQCfpxRj4oqw==} dev: true @@ -3248,15 +2578,6 @@ packages: jake: 10.8.7 dev: true - /electron-to-chromium@1.4.503: - resolution: {integrity: sha512-LF2IQit4B0VrUHFeQkWhZm97KuJSGF2WJqq1InpY+ECpFRkXd8yTIaTtJxsO0OKDmiBYwWqcrNaXOurn2T2wiA==} - dev: true - - /emittery@0.13.1: - resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} - engines: {node: '>=12'} - dev: true - /emoji-regex@10.3.0: resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} dev: true @@ -3343,11 +2664,6 @@ packages: engines: {node: '>=0.8.0'} dev: true - /escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - dev: true - /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -3379,6 +2695,12 @@ packages: /estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + /estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + dependencies: + '@types/estree': 1.0.5 + dev: true + /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -3413,21 +2735,6 @@ packages: strip-eof: 1.0.0 dev: true - /execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - dev: true - /execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} @@ -3443,22 +2750,6 @@ packages: strip-final-newline: 3.0.0 dev: true - /exit@0.1.2: - resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} - engines: {node: '>= 0.8.0'} - dev: true - - /expect@29.7.0: - resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/expect-utils': 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - dev: true - /extract-zip@2.0.1: resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} @@ -3502,18 +2793,16 @@ packages: reusify: 1.0.4 dev: true - /fb-watchman@2.0.2: - resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - dependencies: - bser: 2.1.1 - dev: true - /fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} dependencies: pend: 1.2.0 dev: true + /fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + dev: true + /filelist@1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} dependencies: @@ -3564,6 +2853,10 @@ packages: hasBin: true dev: true + /flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + dev: true + /focus-trap@7.5.4: resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==} dependencies: @@ -3660,11 +2953,6 @@ packages: - supports-color dev: true - /gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - dev: true - /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -3679,9 +2967,8 @@ packages: resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} dev: true - /get-package-type@0.1.0: - resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} - engines: {node: '>=8.0.0'} + /get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} dev: true /get-pkg-repo@4.2.1: @@ -3707,11 +2994,6 @@ packages: pump: 3.0.0 dev: true - /get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - dev: true - /get-stream@8.0.1: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} @@ -3815,11 +3097,6 @@ packages: once: 1.4.0 dev: true - /globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - dev: true - /globby@14.0.1: resolution: {integrity: sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==} engines: {node: '>=18'} @@ -3964,11 +3241,6 @@ packages: - supports-color dev: true - /human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - dev: true - /human-signals@5.0.0: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} @@ -3999,20 +3271,6 @@ packages: engines: {node: '>=8'} dev: true - /import-local@3.1.0: - resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} - engines: {node: '>=8'} - hasBin: true - dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 - dev: true - - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - dev: true - /indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} @@ -4103,11 +3361,6 @@ packages: get-east-asian-width: 1.2.0 dev: true - /is-generator-fn@2.1.0: - resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} - engines: {node: '>=6'} - dev: true - /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -4163,11 +3416,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - dev: true - /is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -4213,53 +3461,27 @@ packages: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true - /istanbul-lib-coverage@3.2.0: - resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} + /istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} dev: true - /istanbul-lib-instrument@5.2.1: - resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} - engines: {node: '>=8'} - dependencies: - '@babel/core': 7.22.11 - '@babel/parser': 7.24.4 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.0 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - dev: true - - /istanbul-lib-instrument@6.0.0: - resolution: {integrity: sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==} - engines: {node: '>=10'} - dependencies: - '@babel/core': 7.22.11 - '@babel/parser': 7.24.4 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.0 - semver: 7.6.0 - transitivePeerDependencies: - - supports-color - dev: true - /istanbul-lib-report@3.0.1: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} dependencies: - istanbul-lib-coverage: 3.2.0 + istanbul-lib-coverage: 3.2.2 make-dir: 4.0.0 supports-color: 7.2.0 dev: true - /istanbul-lib-source-maps@4.0.1: - resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + /istanbul-lib-source-maps@5.0.4: + resolution: {integrity: sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==} engines: {node: '>=10'} dependencies: + '@jridgewell/trace-mapping': 0.3.25 debug: 4.3.4 - istanbul-lib-coverage: 3.2.0 - source-map: 0.6.1 + istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color dev: true @@ -4292,443 +3514,6 @@ packages: minimatch: 3.1.2 dev: true - /jest-changed-files@29.7.0: - resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - execa: 5.1.1 - jest-util: 29.7.0 - p-limit: 3.1.0 - dev: true - - /jest-circus@29.7.0: - resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.12.7 - chalk: 4.1.2 - co: 4.6.0 - dedent: 1.5.1 - is-generator-fn: 2.1.0 - jest-each: 29.7.0 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - p-limit: 3.1.0 - pretty-format: 29.7.0 - pure-rand: 6.0.2 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - dev: true - - /jest-cli@29.7.0: - resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0 - exit: 0.1.2 - import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.12.7) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - - /jest-config@29.7.0(@types/node@20.12.7): - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - dependencies: - '@babel/core': 7.22.11 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.12.7 - babel-jest: 29.7.0(@babel/core@7.22.11) - chalk: 4.1.2 - ci-info: 3.8.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - dev: true - - /jest-diff@29.7.0: - resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - diff-sequences: 29.6.3 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - dev: true - - /jest-docblock@29.7.0: - resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - detect-newline: 3.1.0 - dev: true - - /jest-each@29.7.0: - resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - jest-get-type: 29.6.3 - jest-util: 29.7.0 - pretty-format: 29.7.0 - dev: true - - /jest-environment-jsdom@29.7.0: - resolution: {integrity: sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - canvas: ^2.5.0 - peerDependenciesMeta: - canvas: - optional: true - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/jsdom': 20.0.1 - '@types/node': 20.11.19 - jest-mock: 29.7.0 - jest-util: 29.7.0 - jsdom: 20.0.3 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: true - - /jest-environment-node@29.7.0: - resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.12.7 - jest-mock: 29.7.0 - jest-util: 29.7.0 - dev: true - - /jest-get-type@29.6.3: - resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - - /jest-haste-map@29.7.0: - resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/graceful-fs': 4.1.6 - '@types/node': 20.12.7 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - jest-worker: 29.7.0 - micromatch: 4.0.5 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 - dev: true - - /jest-leak-detector@29.7.0: - resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - dev: true - - /jest-matcher-utils@29.7.0: - resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - dev: true - - /jest-message-util@29.7.0: - resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/code-frame': 7.22.10 - '@jest/types': 29.6.3 - '@types/stack-utils': 2.0.1 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.5 - pretty-format: 29.7.0 - slash: 3.0.0 - stack-utils: 2.0.6 - dev: true - - /jest-mock-warn@1.1.0: - resolution: {integrity: sha512-Q0EjGIUowgcuH7K1v6KgZ/WtqQaA9kc/TxayKaZKKeTGBn9nC4uKI65nt0O3l8opaPi2VSvG18WcLPEqzowxrQ==} - dev: true - - /jest-mock@29.7.0: - resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/node': 20.11.19 - jest-util: 29.7.0 - dev: true - - /jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - dependencies: - jest-resolve: 29.7.0 - dev: true - - /jest-regex-util@29.6.3: - resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - - /jest-resolve-dependencies@29.7.0: - resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-regex-util: 29.6.3 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-resolve@29.7.0: - resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) - jest-util: 29.7.0 - jest-validate: 29.7.0 - resolve: 1.22.8 - resolve.exports: 2.0.2 - slash: 3.0.0 - dev: true - - /jest-runner@29.7.0: - resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.7.0 - '@jest/environment': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.12.7 - chalk: 4.1.2 - emittery: 0.13.1 - graceful-fs: 4.2.11 - jest-docblock: 29.7.0 - jest-environment-node: 29.7.0 - jest-haste-map: 29.7.0 - jest-leak-detector: 29.7.0 - jest-message-util: 29.7.0 - jest-resolve: 29.7.0 - jest-runtime: 29.7.0 - jest-util: 29.7.0 - jest-watcher: 29.7.0 - jest-worker: 29.7.0 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-runtime@29.7.0: - resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/globals': 29.7.0 - '@jest/source-map': 29.6.3 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.12.7 - chalk: 4.1.2 - cjs-module-lexer: 1.2.3 - collect-v8-coverage: 1.0.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-snapshot@29.7.0: - resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.22.11 - '@babel/generator': 7.22.10 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.11) - '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.11) - '@babel/types': 7.22.11 - '@jest/expect-utils': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.11) - chalk: 4.1.2 - expect: 29.7.0 - graceful-fs: 4.2.11 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - natural-compare: 1.4.0 - pretty-format: 29.7.0 - semver: 7.6.0 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-util@29.7.0: - resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/node': 20.11.19 - chalk: 4.1.2 - ci-info: 3.8.0 - graceful-fs: 4.2.11 - picomatch: 2.3.1 - dev: true - - /jest-validate@29.7.0: - resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 29.6.3 - leven: 3.1.0 - pretty-format: 29.7.0 - dev: true - - /jest-watcher@29.7.0: - resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.12.7 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 29.7.0 - string-length: 4.0.2 - dev: true - - /jest-worker@29.7.0: - resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@types/node': 20.12.7 - jest-util: 29.7.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 - dev: true - - /jest@29.7.0: - resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.7.0 - '@jest/types': 29.6.3 - import-local: 3.1.0 - jest-cli: 29.7.0 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - /jju@1.4.0: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} dev: true @@ -4748,12 +3533,8 @@ packages: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} dev: true - /js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 + /js-tokens@9.0.0: + resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} dev: true /js-yaml@4.1.0: @@ -4805,53 +3586,6 @@ packages: - utf-8-validate dev: true - /jsdom@20.0.3: - resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==} - engines: {node: '>=14'} - peerDependencies: - canvas: ^2.5.0 - peerDependenciesMeta: - canvas: - optional: true - dependencies: - abab: 2.0.6 - acorn: 8.10.0 - acorn-globals: 7.0.1 - cssom: 0.5.0 - cssstyle: 2.3.0 - data-urls: 3.0.2 - decimal.js: 10.4.3 - domexception: 4.0.0 - escodegen: 2.1.0 - form-data: 4.0.0 - html-encoding-sniffer: 3.0.0 - http-proxy-agent: 5.0.0 - https-proxy-agent: 5.0.1 - is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.7 - parse5: 7.1.2 - saxes: 6.0.0 - symbol-tree: 3.2.4 - tough-cookie: 4.1.3 - w3c-xmlserializer: 4.0.0 - webidl-conversions: 7.0.0 - whatwg-encoding: 2.0.0 - whatwg-mimetype: 3.0.0 - whatwg-url: 11.0.0 - ws: 8.13.0 - xml-name-validator: 4.0.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: true - - /jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true - dev: true - /json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} dev: true @@ -4872,12 +3606,6 @@ packages: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} dev: true - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - dev: true - /jsonc-parser@3.2.1: resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} dev: true @@ -4921,16 +3649,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} - dev: true - - /leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - dev: true - /lie@3.3.0: resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} dependencies: @@ -4987,6 +3705,14 @@ packages: strip-bom: 3.0.0 dev: true + /local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + engines: {node: '>=14'} + dependencies: + mlly: 1.7.1 + pkg-types: 1.1.1 + dev: true + /locate-path@2.0.0: resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} engines: {node: '>=4'} @@ -5137,6 +3863,12 @@ packages: get-func-name: 2.0.0 dev: true + /loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + dependencies: + get-func-name: 2.0.2 + dev: true + /lowercase-keys@2.0.0: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} @@ -5154,12 +3886,6 @@ packages: yallist: 2.1.2 dev: true - /lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - dependencies: - yallist: 3.1.1 - dev: true - /lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -5184,6 +3910,14 @@ packages: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 + /magicast@0.3.4: + resolution: {integrity: sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==} + dependencies: + '@babel/parser': 7.24.4 + '@babel/types': 7.24.0 + source-map-js: 1.2.0 + dev: true + /make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -5198,12 +3932,6 @@ packages: semver: 7.6.0 dev: true - /makeerror@1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - dependencies: - tmpl: 1.0.5 - dev: true - /map-obj@1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} @@ -5383,6 +4111,15 @@ packages: hasBin: true dev: true + /mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + dependencies: + acorn: 8.12.0 + pathe: 1.1.2 + pkg-types: 1.1.1 + ufo: 1.5.3 + dev: true + /mocha@9.2.2: resolution: {integrity: sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==} engines: {node: '>= 12.0.0'} @@ -5419,6 +4156,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /mrmime@2.0.0: + resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} + engines: {node: '>=10'} + dev: true + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} @@ -5449,10 +4191,6 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - /natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - dev: true - /neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} dev: true @@ -5524,14 +4262,6 @@ packages: - utf-8-validate dev: true - /node-int64@0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - dev: true - - /node-releases@2.0.13: - resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} - dev: true - /nopt@7.2.0: resolution: {integrity: sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -5581,13 +4311,6 @@ packages: path-key: 2.0.1 dev: true - /npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - dependencies: - path-key: 3.1.1 - dev: true - /npm-run-path@5.1.0: resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -5679,6 +4402,13 @@ packages: yocto-queue: 0.1.0 dev: true + /p-limit@5.0.0: + resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} + engines: {node: '>=18'} + dependencies: + yocto-queue: 1.0.0 + dev: true + /p-locate@2.0.0: resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} engines: {node: '>=4'} @@ -5805,6 +4535,10 @@ packages: engines: {node: '>=12'} dev: true + /pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + dev: true + /pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} dev: true @@ -5859,6 +4593,14 @@ packages: find-up: 4.1.0 dev: true + /pkg-types@1.1.1: + resolution: {integrity: sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==} + dependencies: + confbox: 0.1.7 + mlly: 1.7.1 + pathe: 1.1.2 + dev: true + /postcss@8.4.38: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} @@ -5890,14 +4632,6 @@ packages: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} dev: true - /prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - dev: true - /proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} dev: true @@ -5939,10 +4673,6 @@ packages: engines: {node: '>=6'} dev: true - /pure-rand@6.0.2: - resolution: {integrity: sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==} - dev: true - /q@1.5.1: resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} engines: {node: '>=0.6.0', teleport: '>=0.2.0'} @@ -6061,23 +4791,6 @@ packages: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} dev: true - /resolve-cwd@3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} - dependencies: - resolve-from: 5.0.0 - dev: true - - /resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - dev: true - - /resolve.exports@2.0.2: - resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} - engines: {node: '>=10'} - dev: true - /resolve@1.19.0: resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} dependencies: @@ -6233,13 +4946,6 @@ packages: xmlchars: 2.2.0 dev: true - /saxes@6.0.0: - resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} - engines: {node: '>=v12.22.7'} - dependencies: - xmlchars: 2.2.0 - dev: true - /search-insights@2.13.0: resolution: {integrity: sha512-Orrsjf9trHHxFRuo9/rzm0KIWmgzE8RMlZMzuhZOJ01Rnz3D0YBAe+V6473t6/H6c7irs6Lt48brULAiRWb3Vw==} dev: false @@ -6345,6 +5051,10 @@ packages: '@shikijs/core': 1.3.0 dev: false + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true + /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true @@ -6364,13 +5074,13 @@ packages: - supports-color dev: false - /sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - dev: true - - /slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} + /sirv@2.0.4: + resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} + engines: {node: '>= 10'} + dependencies: + '@polka/url': 1.0.0-next.25 + mrmime: 2.0.0 + totalist: 3.0.1 dev: true /slash@4.0.0: @@ -6407,13 +5117,6 @@ packages: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} - /source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - dev: true - /source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} dependencies: @@ -6475,11 +5178,8 @@ packages: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: true - /stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} - dependencies: - escape-string-regexp: 2.0.0 + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} dev: true /stacktrace-parser@0.1.10: @@ -6489,6 +5189,10 @@ packages: type-fest: 0.7.1 dev: true + /std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + dev: true + /stream-combiner@0.0.4: resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} dependencies: @@ -6500,14 +5204,6 @@ packages: engines: {node: '>=0.6.19'} dev: true - /string-length@4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} - engines: {node: '>=10'} - dependencies: - char-regex: 1.0.2 - strip-ansi: 6.0.1 - dev: true - /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -6566,21 +5262,11 @@ packages: engines: {node: '>=4'} dev: true - /strip-bom@4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - dev: true - /strip-eof@1.0.0: resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} engines: {node: '>=0.10.0'} dev: true - /strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - dev: true - /strip-final-newline@3.0.0: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} @@ -6603,6 +5289,12 @@ packages: engines: {node: '>=8'} dev: true + /strip-literal@2.1.0: + resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} + dependencies: + js-tokens: 9.0.0 + dev: true + /sucrase@3.34.0: resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==} engines: {node: '>=8'} @@ -6747,6 +5439,20 @@ packages: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} dev: true + /tinybench@2.8.0: + resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} + dev: true + + /tinypool@0.8.4: + resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} + engines: {node: '>=14.0.0'} + dev: true + /tmp@0.2.1: resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==} engines: {node: '>=8.17.0'} @@ -6754,10 +5460,6 @@ packages: rimraf: 3.0.2 dev: true - /tmpl@1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - dev: true - /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} @@ -6769,6 +5471,11 @@ packages: is-number: 7.0.0 dev: true + /totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + dev: true + /tough-cookie@4.1.3: resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==} engines: {node: '>=6'} @@ -6814,11 +5521,6 @@ packages: engines: {node: '>=10'} dev: true - /type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - dev: true - /type-fest@0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} engines: {node: '>=8'} @@ -6867,6 +5569,10 @@ packages: engines: {node: '>=14.17'} hasBin: true + /ufo@1.5.3: + resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + dev: true + /uglify-js@3.17.4: resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} engines: {node: '>=0.8.0'} @@ -6903,17 +5609,6 @@ packages: engines: {node: '>=8'} dev: true - /update-browserslist-db@1.0.11(browserslist@4.21.10): - resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - dependencies: - browserslist: 4.21.10 - escalade: 3.1.1 - picocolors: 1.0.0 - dev: true - /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: @@ -6942,15 +5637,6 @@ packages: hasBin: true dev: true - /v8-to-istanbul@9.1.0: - resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} - engines: {node: '>=10.12.0'} - dependencies: - '@jridgewell/trace-mapping': 0.3.19 - '@types/istanbul-lib-coverage': 2.0.4 - convert-source-map: 1.9.0 - dev: true - /validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: @@ -6963,6 +5649,27 @@ packages: engines: {node: '>= 0.10'} dev: true + /vite-node@1.6.0: + resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + pathe: 1.1.2 + picocolors: 1.0.0 + vite: 5.2.9(@types/node@20.12.7) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vite@5.2.9(@types/node@20.12.7): resolution: {integrity: sha512-uOQWfuZBlc6Y3W/DTuQ1Sr+oIXWvqljLvS881SVmAj00d5RdgShLcuXWxseWPd4HXwiYBFW/vXHfKFeqj9uQnw==} engines: {node: ^18.0.0 || >=20.0.0} @@ -7068,6 +5775,62 @@ packages: - universal-cookie dev: false + /vitest@1.6.0(@vitest/ui@1.6.0): + resolution: {integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 1.6.0 + '@vitest/ui': 1.6.0 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@vitest/expect': 1.6.0 + '@vitest/runner': 1.6.0 + '@vitest/snapshot': 1.6.0 + '@vitest/spy': 1.6.0 + '@vitest/ui': 1.6.0(vitest@1.6.0) + '@vitest/utils': 1.6.0 + acorn-walk: 8.3.3 + chai: 4.4.1 + debug: 4.3.4 + execa: 8.0.1 + local-pkg: 0.5.0 + magic-string: 0.30.9 + pathe: 1.1.2 + picocolors: 1.0.0 + std-env: 3.7.0 + strip-literal: 2.1.0 + tinybench: 2.8.0 + tinypool: 0.8.4 + vite: 5.2.9(@types/node@20.12.7) + vite-node: 1.6.0 + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vscode-oniguruma@1.7.0: resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} dev: true @@ -7143,19 +5906,6 @@ packages: xml-name-validator: 4.0.0 dev: true - /w3c-xmlserializer@4.0.0: - resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} - engines: {node: '>=14'} - dependencies: - xml-name-validator: 4.0.0 - dev: true - - /walker@1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - dependencies: - makeerror: 1.0.12 - dev: true - /wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: @@ -7210,6 +5960,15 @@ packages: isexe: 2.0.0 dev: true + /why-is-node-running@2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: true + /widest-line@3.1.0: resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} engines: {node: '>=8'} @@ -7256,14 +6015,6 @@ packages: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true - /write-file-atomic@4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - dev: true - /ws@8.13.0: resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} engines: {node: '>=10.0.0'} @@ -7300,10 +6051,6 @@ packages: resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} dev: true - /yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - dev: true - /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true @@ -7323,11 +6070,6 @@ packages: engines: {node: '>=10'} dev: true - /yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - dev: true - /yargs-unparser@2.0.0: resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} engines: {node: '>=10'} @@ -7351,19 +6093,6 @@ packages: yargs-parser: 20.2.4 dev: true - /yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - dependencies: - cliui: 8.0.1 - escalade: 3.1.1 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - dev: true - /yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} dependencies: @@ -7376,6 +6105,11 @@ packages: engines: {node: '>=10'} dev: true + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: true + /yorkie@2.0.0: resolution: {integrity: sha512-jcKpkthap6x63MB4TxwCyuIGkV0oYP/YRyuQU5UO0Yz/E/ZAu+653/uov+phdmO54n6BcvFRyyt0RRrWdN2mpw==} engines: {node: '>=4'} diff --git a/scripts/release.mjs b/scripts/release.mjs index 5499a75b6..29199d2f1 100644 --- a/scripts/release.mjs +++ b/scripts/release.mjs @@ -1,7 +1,7 @@ +import minimist from 'minimist' import fs from 'node:fs/promises' import { join, resolve, dirname } from 'node:path' import { fileURLToPath } from 'node:url' -import minimist from 'minimist' import chalk from 'chalk' import semver from 'semver' import enquirer from 'enquirer' @@ -20,15 +20,33 @@ let { tag: optionTag, dry: isDryRun, skipCleanCheck: skipCleanGitCheck, + noDepsUpdate, + noPublish, } = args +if (args.h || args.help) { + console.log( + ` +Usage: node release.mjs [flags] + node release.mjs [ -h | --help ] + +Flags: + --skipBuild Skip building packages + --tag Publish under a given npm dist tag + --dry Dry run + --skipCleanCheck Skip checking if the git repo is clean + --noDepsUpdate Skip updating dependencies in package.json files + --noPublish Skip publishing packages +`.trim() + ) + process.exit(0) +} + // const preId = // args.preid || // (semver.prerelease(currentVersion) && semver.prerelease(currentVersion)[0]) const EXPECTED_BRANCH = 'main' -const incrementVersion = increment => - semver.inc(currentVersion, increment, preId) const bin = name => resolve(__dirname, '../node_modules/.bin/' + name) /** * @param bin {string} @@ -136,6 +154,17 @@ async function main() { message: `Select release type for ${chalk.bold.white(name)}`, choices: versionIncrements .map(i => `${i}: ${name} (${semver.inc(version, i, preId)})`) + .concat( + optionTag === 'edge' + ? [ + `edge: ${name} (${semver.inc( + version, + 'prerelease', + 'alpha' + )})`, + ] + : [] + ) .concat(['custom']), }) @@ -178,15 +207,8 @@ async function main() { step('\nUpdating versions in package.json files...') await updateVersions(pkgWithVersions) - step('\nCopying README for router package...') - if (!isDryRun) { - await fs.copyFile( - resolve(__dirname, '../README.md'), - resolve(__dirname, '../packages/router/README.md') - ) - } else { - console.log(`(skipped)`) - } + step('\nUpdating lock...') + await runIfNotDry(`pnpm`, ['install']) step('\nGenerating changelogs...') for (const pkg of pkgWithVersions) { @@ -226,6 +248,7 @@ async function main() { 'add', 'packages/*/CHANGELOG.md', 'packages/*/package.json', + 'pnpm-lock.yaml', ]) await runIfNotDry('git', [ 'commit', @@ -249,14 +272,18 @@ async function main() { await runIfNotDry('git', ['tag', `${tagName}`]) } - step('\nPublishing packages...') - for (const pkg of pkgWithVersions) { - await publishPackage(pkg) - } + if (!noPublish) { + step('\nPublishing packages...') + for (const pkg of pkgWithVersions) { + await publishPackage(pkg) + } - step('\nPushing to Github...') - await runIfNotDry('git', ['push', 'origin', ...versionsToPush]) - await runIfNotDry('git', ['push']) + step('\nPushing to Github...') + await runIfNotDry('git', ['push', 'origin', ...versionsToPush]) + await runIfNotDry('git', ['push']) + } else { + console.log(chalk.bold.white(`Skipping publishing...`)) + } } /** @@ -267,11 +294,14 @@ async function updateVersions(packageList) { return Promise.all( packageList.map(({ pkg, version, path, name }) => { pkg.version = version - updateDeps(pkg, 'dependencies', packageList) - updateDeps(pkg, 'peerDependencies', packageList) + if (!noDepsUpdate) { + updateDeps(pkg, 'dependencies', packageList) + updateDeps(pkg, 'peerDependencies', packageList) + } const content = JSON.stringify(pkg, null, 2) + '\n' return isDryRun ? dryRun('write', [name], { + version: pkg.version, dependencies: pkg.dependencies, peerDependencies: pkg.peerDependencies, }) @@ -307,11 +337,11 @@ async function publishPackage(pkg) { [ 'publish', ...(optionTag ? ['--tag', optionTag] : []), + ...(skipCleanGitCheck ? ['--no-git-checks'] : []), '--access', 'public', '--publish-branch', EXPECTED_BRANCH, - ...(skipCleanGitCheck ? ['--no-git-checks'] : []), ], { cwd: pkg.path,