From ec46a6f76089523145d051b77a74933fae3dbc2c Mon Sep 17 00:00:00 2001 From: Daniel Lautzenheiser Date: Sun, 4 Dec 2022 22:15:59 -0500 Subject: [PATCH] Feature/unit tests (#184) --- .github/workflows/build.yml | 23 + core/jest.config.js | 17 + core/package.json | 15 +- core/src/__mocks__/clean-stack.ts | 3 + core/src/__mocks__/copy-text-to-clipboard.ts | 1 + core/src/__mocks__/localforage.ts | 17 + core/src/__mocks__/styleMock.ts | 1 + core/src/backend.ts | 6 +- core/src/backends/github/API.ts | 4 +- .../src/backends/github/__tests__/API.spec.ts | 318 ++++++++++++++ .../github/__tests__/implementation.spec.ts | 289 ++++++++++++ .../src/backends/gitlab/__tests__/API.spec.ts | 166 +++++++ core/src/interface.ts | 6 + .../{object.util.ts => object.util.spec.ts} | 0 core/src/tsconfig.json | 3 + core/test/fileTransformer.js | 9 + core/test/mockFetch.ts | 90 ++++ core/test/mockLocalStorage.ts | 22 + core/test/setupEnv.js | 14 + core/tsconfig.base.json | 59 +++ core/tsconfig.dev.json | 5 + core/tsconfig.json | 56 +-- core/yarn.lock | 414 +++++++++++++++--- 23 files changed, 1423 insertions(+), 115 deletions(-) create mode 100644 core/jest.config.js create mode 100644 core/src/__mocks__/clean-stack.ts create mode 100644 core/src/__mocks__/copy-text-to-clipboard.ts create mode 100644 core/src/__mocks__/localforage.ts create mode 100644 core/src/__mocks__/styleMock.ts create mode 100644 core/src/backends/github/__tests__/API.spec.ts create mode 100644 core/src/backends/github/__tests__/implementation.spec.ts create mode 100644 core/src/backends/gitlab/__tests__/API.spec.ts rename core/src/lib/util/__tests__/{object.util.ts => object.util.spec.ts} (100%) create mode 100644 core/src/tsconfig.json create mode 100644 core/test/fileTransformer.js create mode 100644 core/test/mockFetch.ts create mode 100644 core/test/mockLocalStorage.ts create mode 100644 core/test/setupEnv.js create mode 100644 core/tsconfig.base.json create mode 100644 core/tsconfig.dev.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eb34ecd6..e5fc47fb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -57,3 +57,26 @@ jobs: working-directory: ./core run: | yarn lint + + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Setup Node + uses: actions/setup-node@v3 + with: + cache: yarn + cache-dependency-path: core/yarn.lock + node-version: 16 + + - name: Install + working-directory: ./core + run: | + yarn install --frozen-lockfile + + - name: Test + working-directory: ./core + run: | + yarn test diff --git a/core/jest.config.js b/core/jest.config.js new file mode 100644 index 00000000..4b02fbf4 --- /dev/null +++ b/core/jest.config.js @@ -0,0 +1,17 @@ +const { pathsToModuleNameMapper } = require('ts-jest'); + +const { compilerOptions } = require('./tsconfig.base'); + +module.exports = { + preset: 'ts-jest', + transform: { + '\\.[jt]sx?$': ['ts-jest', { tsConfig: 'tsconfig.dev.json' }], + '^.+\\.svg$': './test/fileTransformer', + }, + moduleNameMapper: { + ...pathsToModuleNameMapper(compilerOptions.paths, { prefix: '/' }), + '\\.(css|less)$': '/src/__mocks__/styleMock.ts', + }, + transformIgnorePatterns: ['node_modules/(?!(url-join|array-move|ol)/)'], + setupFiles: ['./test/setupEnv.js'], +}; diff --git a/core/package.json b/core/package.json index 8dac928f..3c4b5c1e 100644 --- a/core/package.json +++ b/core/package.json @@ -24,6 +24,7 @@ "lint": "run-p -c --aggregate-output \"lint:*\"", "prepublishOnly": "yarn build", "start": "run-s clean develop", + "test": "cross-env NODE_ENV=test jest", "type-check": "tsc --watch" }, "main": "dist/static-cms-core.js", @@ -41,12 +42,22 @@ "last 2 Safari versions" ], "dependencies": { + "@babel/runtime": "7.20.6", + "@codemirror/autocomplete": "6.3.4", "@codemirror/commands": "6.1.2", + "@codemirror/language": "6.3.1", + "@codemirror/language-data": "6.1.0", + "@codemirror/legacy-modes": "6.3.1", + "@codemirror/lint": "6.1.0", + "@codemirror/search": "6.2.3", + "@codemirror/state": "6.1.4", + "@codemirror/theme-one-dark": "6.1.0", "@codemirror/view": "6.6.0", "@emotion/babel-preset-css-prop": "11.10.0", "@emotion/css": "11.10.5", "@emotion/react": "11.10.5", "@emotion/styled": "11.10.5", + "@lezer/common": "1.0.2", "@ltd/j-toml": "1.35.3", "@mdx-js/mdx": "2.1.5", "@mdx-js/react": "2.1.5", @@ -120,6 +131,7 @@ "remark-mdx": "2.1.5", "remark-parse": "10.0.1", "sanitize-filename": "1.6.3", + "scheduler": "0.23.0", "semaphore": "1.1.0", "slate": "0.87.0", "slate-history": "0.86.0", @@ -217,11 +229,11 @@ "gitlab": "14.2.2", "http-server": "14.1.1", "jest": "29.3.1", + "jest-environment-jsdom": "^29.3.1", "js-yaml": "4.1.0", "mockserver-client": "5.14.0", "mockserver-node": "5.14.0", "ncp": "2.0.0", - "nock": "13.2.9", "node-fetch": "2.6.7", "npm-run-all": "4.1.5", "postcss": "8.4.19", @@ -235,6 +247,7 @@ "source-map-loader": "4.0.1", "style-loader": "3.3.1", "to-string-loader": "1.2.0", + "ts-jest": "29.0.3", "tsconfig-paths-webpack-plugin": "4.0.0", "typescript": "4.9.3", "webpack": "5.75.0", diff --git a/core/src/__mocks__/clean-stack.ts b/core/src/__mocks__/clean-stack.ts new file mode 100644 index 00000000..08022551 --- /dev/null +++ b/core/src/__mocks__/clean-stack.ts @@ -0,0 +1,3 @@ +export default function cleanStack(stack: string) { + return stack; +} diff --git a/core/src/__mocks__/copy-text-to-clipboard.ts b/core/src/__mocks__/copy-text-to-clipboard.ts new file mode 100644 index 00000000..0bb2acf5 --- /dev/null +++ b/core/src/__mocks__/copy-text-to-clipboard.ts @@ -0,0 +1 @@ +export default jest.fn(); diff --git a/core/src/__mocks__/localforage.ts b/core/src/__mocks__/localforage.ts new file mode 100644 index 00000000..74427c23 --- /dev/null +++ b/core/src/__mocks__/localforage.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +const mock = { + storage: {}, + setItem: jest.fn().mockImplementation(function (this: any, key: string, data: any) { + this.storage[key] = data; + return Promise.resolve(); + }), + getItem: jest.fn().mockImplementation(function (this: any, key: string) { + return Promise.resolve(this.storage[key]); + }), + removeItem: jest.fn().mockImplementation(function (this: any, key: string) { + delete this.storage[key]; + return Promise.resolve(); + }), +}; + +export default mock; diff --git a/core/src/__mocks__/styleMock.ts b/core/src/__mocks__/styleMock.ts new file mode 100644 index 00000000..ff8b4c56 --- /dev/null +++ b/core/src/__mocks__/styleMock.ts @@ -0,0 +1 @@ +export default {}; diff --git a/core/src/backend.ts b/core/src/backend.ts index b01c00ec..01a9463e 100644 --- a/core/src/backend.ts +++ b/core/src/backend.ts @@ -271,8 +271,8 @@ function collectionDepth(collection: Collection) { return depth; } -export class Backend { - implementation: BackendClass; +export class Backend { + implementation: BC; backendName: string; config: Config; authStore?: AuthStore; @@ -288,7 +288,7 @@ export class Backend { this.config = config; this.implementation = implementation.init(this.config, { updateUserCredentials: this.updateUserCredentials, - }); + }) as BC; this.backendName = backendName; this.authStore = authStore; if (this.implementation === null) { diff --git a/core/src/backends/github/API.ts b/core/src/backends/github/API.ts index 1c4c0d11..4600f6e2 100644 --- a/core/src/backends/github/API.ts +++ b/core/src/backends/github/API.ts @@ -71,7 +71,9 @@ export interface BlobArgs { type Param = string | number | undefined; -type Options = RequestInit & { params?: Record | string[]> }; +export type Options = RequestInit & { + params?: Record | string[]>; +}; type MediaFile = { sha: string; diff --git a/core/src/backends/github/__tests__/API.spec.ts b/core/src/backends/github/__tests__/API.spec.ts new file mode 100644 index 00000000..f5a8cc29 --- /dev/null +++ b/core/src/backends/github/__tests__/API.spec.ts @@ -0,0 +1,318 @@ +import { Base64 } from 'js-base64'; + +import API from '../API'; + +import type { Options } from '../API'; + +describe('github API', () => { + beforeEach(() => { + jest.resetAllMocks(); + + global.fetch = jest.fn().mockRejectedValue(new Error('should not call fetch inside tests')); + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + function mockAPI(api: API, responses: Record any>) { + api.request = jest.fn().mockImplementation((path, options = {}) => { + const normalizedPath = path.indexOf('?') !== -1 ? path.slice(0, path.indexOf('?')) : path; + const response = responses[normalizedPath]; + return typeof response === 'function' + ? Promise.resolve(response(options)) + : Promise.reject(new Error(`No response for path '${normalizedPath}'`)); + }); + } + + describe('updateTree', () => { + it('should create tree with nested paths', async () => { + const api = new API({ branch: 'master', repo: 'owner/repo' }); + + api.createTree = jest.fn().mockImplementation(() => Promise.resolve({ sha: 'newTreeSha' })); + + const files = [ + { path: '/static/media/new-image.jpeg', sha: null }, + { path: 'content/posts/new-post.md', sha: 'new-post.md' }, + ]; + + const baseTreeSha = 'baseTreeSha'; + + await expect(api.updateTree(baseTreeSha, files)).resolves.toEqual({ + sha: 'newTreeSha', + parentSha: baseTreeSha, + }); + + expect(api.createTree).toHaveBeenCalledTimes(1); + expect(api.createTree).toHaveBeenCalledWith(baseTreeSha, [ + { + path: 'static/media/new-image.jpeg', + mode: '100644', + type: 'blob', + sha: null, + }, + { + path: 'content/posts/new-post.md', + mode: '100644', + type: 'blob', + sha: 'new-post.md', + }, + ]); + }); + }); + + describe('request', () => { + const fetch = jest.fn(); + beforeEach(() => { + global.fetch = fetch; + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('should fetch url with authorization header', async () => { + const api = new API({ branch: 'gh-pages', repo: 'my-repo', token: 'token' }); + + fetch.mockResolvedValue({ + text: jest.fn().mockResolvedValue('some response'), + ok: true, + status: 200, + headers: { get: () => '' }, + }); + const result = await api.request('/some-path'); + expect(result).toEqual('some response'); + expect(fetch).toHaveBeenCalledTimes(1); + expect(fetch).toHaveBeenCalledWith('https://api.github.com/some-path', { + cache: 'no-cache', + headers: { + Authorization: 'token token', + 'Content-Type': 'application/json; charset=utf-8', + }, + }); + }); + + it('should throw error on not ok response', async () => { + const api = new API({ branch: 'gh-pages', repo: 'my-repo', token: 'token' }); + + fetch.mockResolvedValue({ + text: jest.fn().mockResolvedValue({ message: 'some error' }), + ok: false, + status: 404, + headers: { get: () => '' }, + }); + + await expect(api.request('some-path')).rejects.toThrow( + expect.objectContaining({ + message: 'some error', + name: 'API_ERROR', + status: 404, + api: 'GitHub', + }), + ); + }); + + it('should allow overriding requestHeaders to return a promise ', async () => { + const api = new API({ branch: 'gh-pages', repo: 'my-repo', token: 'token' }); + + api.requestHeaders = jest.fn().mockResolvedValue({ + Authorization: 'promise-token', + 'Content-Type': 'application/json; charset=utf-8', + }); + + fetch.mockResolvedValue({ + text: jest.fn().mockResolvedValue('some response'), + ok: true, + status: 200, + headers: { get: () => '' }, + }); + const result = await api.request('/some-path'); + expect(result).toEqual('some response'); + expect(fetch).toHaveBeenCalledTimes(1); + expect(fetch).toHaveBeenCalledWith('https://api.github.com/some-path', { + cache: 'no-cache', + headers: { + Authorization: 'promise-token', + 'Content-Type': 'application/json; charset=utf-8', + }, + }); + }); + }); + + describe('persistFiles', () => { + it('should update tree, commit and patch branch when useWorkflow is false', async () => { + const api = new API({ branch: 'master', repo: 'owner/repo' }); + + const responses = { + // upload the file + '/repos/owner/repo/git/blobs': () => ({ sha: 'new-file-sha' }), + + // get the branch + '/repos/owner/repo/branches/master': () => ({ commit: { sha: 'root' } }), + + // create new tree + '/repos/owner/repo/git/trees': (options: Options) => { + const data = JSON.parse((options.body as string) ?? ''); + return { sha: data.base_tree }; + }, + + // update the commit with the tree + '/repos/owner/repo/git/commits': () => ({ sha: 'commit-sha' }), + + // patch the branch + '/repos/owner/repo/git/refs/heads/master': () => ({}), + }; + mockAPI(api, responses); + + const entry = { + dataFiles: [ + { + slug: 'entry', + sha: 'abc', + path: 'content/posts/new-post.md', + raw: 'content', + }, + ], + assets: [], + }; + await api.persistFiles(entry.dataFiles, entry.assets, { commitMessage: 'commitMessage' }); + + expect(api.request).toHaveBeenCalledTimes(5); + + expect((api.request as jest.Mock).mock.calls[0]).toEqual([ + '/repos/owner/repo/git/blobs', + { + method: 'POST', + body: JSON.stringify({ + content: Base64.encode(entry.dataFiles[0].raw), + encoding: 'base64', + }), + }, + ]); + + expect((api.request as jest.Mock).mock.calls[1]).toEqual([ + '/repos/owner/repo/branches/master', + ]); + + expect((api.request as jest.Mock).mock.calls[2]).toEqual([ + '/repos/owner/repo/git/trees', + { + body: JSON.stringify({ + base_tree: 'root', + tree: [ + { + path: 'content/posts/new-post.md', + mode: '100644', + type: 'blob', + sha: 'new-file-sha', + }, + ], + }), + method: 'POST', + }, + ]); + + expect((api.request as jest.Mock).mock.calls[3]).toEqual([ + '/repos/owner/repo/git/commits', + { + body: JSON.stringify({ + message: 'commitMessage', + tree: 'root', + parents: ['root'], + }), + method: 'POST', + }, + ]); + + expect((api.request as jest.Mock).mock.calls[4]).toEqual([ + '/repos/owner/repo/git/refs/heads/master', + { + body: JSON.stringify({ + sha: 'commit-sha', + }), + method: 'PATCH', + }, + ]); + }); + }); + + describe('listFiles', () => { + it('should get files by depth', async () => { + const api = new API({ branch: 'master', repo: 'owner/repo' }); + + const tree = [ + { + path: 'post.md', + type: 'blob', + }, + { + path: 'dir1', + type: 'tree', + }, + { + path: 'dir1/nested-post.md', + type: 'blob', + }, + { + path: 'dir1/dir2', + type: 'tree', + }, + { + path: 'dir1/dir2/nested-post.md', + type: 'blob', + }, + ]; + api.request = jest.fn().mockResolvedValue({ tree }); + + await expect(api.listFiles('posts', { depth: 1 })).resolves.toEqual([ + { + path: 'posts/post.md', + type: 'blob', + name: 'post.md', + }, + ]); + expect(api.request).toHaveBeenCalledTimes(1); + expect(api.request).toHaveBeenCalledWith('/repos/owner/repo/git/trees/master:posts', { + params: {}, + }); + + jest.clearAllMocks(); + await expect(api.listFiles('posts', { depth: 2 })).resolves.toEqual([ + { + path: 'posts/post.md', + type: 'blob', + name: 'post.md', + }, + { + path: 'posts/dir1/nested-post.md', + type: 'blob', + name: 'nested-post.md', + }, + ]); + expect(api.request).toHaveBeenCalledTimes(1); + expect(api.request).toHaveBeenCalledWith('/repos/owner/repo/git/trees/master:posts', { + params: { recursive: 1 }, + }); + + jest.clearAllMocks(); + await expect(api.listFiles('posts', { depth: 3 })).resolves.toEqual([ + { + path: 'posts/post.md', + type: 'blob', + name: 'post.md', + }, + { + path: 'posts/dir1/nested-post.md', + type: 'blob', + name: 'nested-post.md', + }, + { + path: 'posts/dir1/dir2/nested-post.md', + type: 'blob', + name: 'nested-post.md', + }, + ]); + expect(api.request).toHaveBeenCalledTimes(1); + expect(api.request).toHaveBeenCalledWith('/repos/owner/repo/git/trees/master:posts', { + params: { recursive: 1 }, + }); + }); + }); +}); diff --git a/core/src/backends/github/__tests__/implementation.spec.ts b/core/src/backends/github/__tests__/implementation.spec.ts new file mode 100644 index 00000000..81cb6a5a --- /dev/null +++ b/core/src/backends/github/__tests__/implementation.spec.ts @@ -0,0 +1,289 @@ +import Cursor, { CURSOR_COMPATIBILITY_SYMBOL } from '@staticcms/core/lib/util/Cursor'; +import GitHubImplementation from '../implementation'; + +import type { Config, UnknownField } from '@staticcms/core'; +import type API from '../API'; +import type { AssetProxy } from '@staticcms/core/valueObjects'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +declare const global: any; + +describe('github backend implementation', () => { + const config = { + backend: { + repo: 'owner/repo', + api_root: 'https://api.github.com', + }, + } as Config; + + const createObjectURL = jest.fn(); + global.URL = { + createObjectURL, + }; + + createObjectURL.mockReturnValue('displayURL'); + + beforeAll(() => { + // eslint-disable-next-line @typescript-eslint/no-empty-function + jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + + describe('persistMedia', () => { + const persistFiles = jest.fn(); + const mockAPI = { + persistFiles, + } as unknown as API; + + persistFiles.mockImplementation((_, files: (AssetProxy & { sha: string })[]) => { + files.forEach((file, index) => { + file.sha = `${index}`; + }); + }); + + it('should persist media file', async () => { + const gitHubImplementation = new GitHubImplementation(config); + gitHubImplementation.api = mockAPI; + + const mediaFile = { + fileObj: { size: 100, name: 'image.png' }, + path: '/media/image.png', + } as AssetProxy; + + expect.assertions(5); + await expect( + gitHubImplementation.persistMedia(mediaFile, { commitMessage: 'Persisting media' }), + ).resolves.toEqual({ + id: '0', + name: 'image.png', + size: 100, + displayURL: 'displayURL', + path: 'media/image.png', + }); + + expect(persistFiles).toHaveBeenCalledTimes(1); + expect(persistFiles).toHaveBeenCalledWith([], [mediaFile], { + commitMessage: 'Persisting media', + }); + expect(createObjectURL).toHaveBeenCalledTimes(1); + expect(createObjectURL).toHaveBeenCalledWith(mediaFile.fileObj); + }); + + it('should log and throw error on "persistFiles" error', async () => { + const gitHubImplementation = new GitHubImplementation(config); + gitHubImplementation.api = mockAPI; + + const error = new Error('failed to persist files'); + persistFiles.mockRejectedValue(error); + + const mediaFile = { + fileObj: { size: 100 }, + path: '/media/image.png', + } as AssetProxy; + + expect.assertions(5); + await expect( + gitHubImplementation.persistMedia(mediaFile, { commitMessage: 'Persisting media' }), + ).rejects.toThrowError(error); + + expect(persistFiles).toHaveBeenCalledTimes(1); + expect(createObjectURL).toHaveBeenCalledTimes(0); + expect(console.error).toHaveBeenCalledTimes(1); + expect(console.error).toHaveBeenCalledWith(error); + }); + }); + + describe('entriesByFolder', () => { + const listFiles = jest.fn(); + const readFile = jest.fn(); + const readFileMetadata = jest.fn(() => Promise.resolve({ author: '', updatedOn: '' })); + + const mockAPI = { + listFiles, + readFile, + readFileMetadata, + originRepoURL: 'originRepoURL', + } as unknown as API; + + it('should return entries and cursor', async () => { + const gitHubImplementation = new GitHubImplementation(config); + gitHubImplementation.api = mockAPI; + + const files = []; + const count = 1501; + for (let i = 0; i < count; i++) { + const id = `${i}`.padStart(`${count}`.length, '0'); + files.push({ + id, + path: `posts/post-${id}.md`, + }); + } + + listFiles.mockResolvedValue(files); + readFile.mockImplementation((_path, id) => Promise.resolve(`${id}`)); + + const expectedEntries = files + .slice(0, 20) + .map(({ id, path }) => ({ data: id, file: { path, id, author: '', updatedOn: '' } })); + + const expectedCursor = Cursor.create({ + actions: ['next', 'last'], + meta: { page: 1, count, pageSize: 20, pageCount: 76 }, + data: { files }, + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (expectedEntries as any)[CURSOR_COMPATIBILITY_SYMBOL] = expectedCursor; + + const result = await gitHubImplementation.entriesByFolder('posts', 'md', 1); + + expect(result).toEqual(expectedEntries); + expect(listFiles).toHaveBeenCalledTimes(1); + expect(listFiles).toHaveBeenCalledWith('posts', { depth: 1, repoURL: 'originRepoURL' }); + expect(readFile).toHaveBeenCalledTimes(20); + }); + }); + + describe('traverseCursor', () => { + const listFiles = jest.fn(); + const readFile = jest.fn((_path, id) => Promise.resolve(`${id}`)); + const readFileMetadata = jest.fn(() => Promise.resolve({})); + + const mockAPI = { + listFiles, + readFile, + originRepoURL: 'originRepoURL', + readFileMetadata, + } as unknown as API; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const files: any[] = []; + const count = 1501; + for (let i = 0; i < count; i++) { + const id = `${i}`.padStart(`${count}`.length, '0'); + files.push({ + id, + path: `posts/post-${id}.md`, + }); + } + + it('should handle next action', async () => { + const gitHubImplementation = new GitHubImplementation(config); + gitHubImplementation.api = mockAPI; + + const cursor = Cursor.create({ + actions: ['next', 'last'], + meta: { page: 1, count, pageSize: 20, pageCount: 76 }, + data: { files }, + }); + + const expectedEntries = files + .slice(20, 40) + .map(({ id, path }) => ({ data: id, file: { path, id } })); + + const expectedCursor = Cursor.create({ + actions: ['prev', 'first', 'next', 'last'], + meta: { page: 2, count, pageSize: 20, pageCount: 76 }, + data: { files }, + }); + + const result = await gitHubImplementation.traverseCursor(cursor, 'next'); + + expect(result).toEqual({ + entries: expectedEntries, + cursor: expectedCursor, + }); + }); + + it('should handle prev action', async () => { + const gitHubImplementation = new GitHubImplementation(config); + gitHubImplementation.api = mockAPI; + + const cursor = Cursor.create({ + actions: ['prev', 'first', 'next', 'last'], + meta: { page: 2, count, pageSize: 20, pageCount: 76 }, + data: { files }, + }); + + const expectedEntries = files + .slice(0, 20) + .map(({ id, path }) => ({ data: id, file: { path, id } })); + + const expectedCursor = Cursor.create({ + actions: ['next', 'last'], + meta: { page: 1, count, pageSize: 20, pageCount: 76 }, + data: { files }, + }); + + const result = await gitHubImplementation.traverseCursor(cursor, 'prev'); + + expect(result).toEqual({ + entries: expectedEntries, + cursor: expectedCursor, + }); + }); + + it('should handle last action', async () => { + const gitHubImplementation = new GitHubImplementation(config); + gitHubImplementation.api = mockAPI; + + const cursor = Cursor.create({ + actions: ['next', 'last'], + meta: { page: 1, count, pageSize: 20, pageCount: 76 }, + data: { files }, + }); + + const expectedEntries = files + .slice(1500) + .map(({ id, path }) => ({ data: id, file: { path, id } })); + + const expectedCursor = Cursor.create({ + actions: ['prev', 'first'], + meta: { page: 76, count, pageSize: 20, pageCount: 76 }, + data: { files }, + }); + + const result = await gitHubImplementation.traverseCursor(cursor, 'last'); + + expect(result).toEqual({ + entries: expectedEntries, + cursor: expectedCursor, + }); + }); + + it('should handle first action', async () => { + const gitHubImplementation = new GitHubImplementation(config); + gitHubImplementation.api = mockAPI; + + const cursor = Cursor.create({ + actions: ['prev', 'first'], + meta: { page: 76, count, pageSize: 20, pageCount: 76 }, + data: { files }, + }); + + const expectedEntries = files + .slice(0, 20) + .map(({ id, path }) => ({ data: id, file: { path, id } })); + + const expectedCursor = Cursor.create({ + actions: ['next', 'last'], + meta: { page: 1, count, pageSize: 20, pageCount: 76 }, + data: { files }, + }); + + const result = await gitHubImplementation.traverseCursor(cursor, 'first'); + + expect(result).toEqual({ + entries: expectedEntries, + cursor: expectedCursor, + }); + }); + }); +}); diff --git a/core/src/backends/gitlab/__tests__/API.spec.ts b/core/src/backends/gitlab/__tests__/API.spec.ts new file mode 100644 index 00000000..1bbde425 --- /dev/null +++ b/core/src/backends/gitlab/__tests__/API.spec.ts @@ -0,0 +1,166 @@ +import API, { getMaxAccess } from '../API'; + +global.fetch = jest.fn().mockRejectedValue(new Error('should not call fetch inside tests')); + +describe('GitLab API', () => { + beforeAll(() => { + // eslint-disable-next-line @typescript-eslint/no-empty-function + jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + + describe('hasWriteAccess', () => { + test('should return true on project access_level >= 30', async () => { + const api = new API({ repo: 'repo' }); + + api.requestJSON = jest + .fn() + .mockResolvedValueOnce({ permissions: { project_access: { access_level: 30 } } }); + + await expect(api.hasWriteAccess()).resolves.toBe(true); + }); + + test('should return false on project access_level < 30', async () => { + const api = new API({ repo: 'repo' }); + + api.requestJSON = jest + .fn() + .mockResolvedValueOnce({ permissions: { project_access: { access_level: 10 } } }); + + await expect(api.hasWriteAccess()).resolves.toBe(false); + }); + + test('should return true on group access_level >= 30', async () => { + const api = new API({ repo: 'repo' }); + + api.requestJSON = jest + .fn() + .mockResolvedValueOnce({ permissions: { group_access: { access_level: 30 } } }); + + await expect(api.hasWriteAccess()).resolves.toBe(true); + }); + + test('should return false on group access_level < 30', async () => { + const api = new API({ repo: 'repo' }); + + api.requestJSON = jest + .fn() + .mockResolvedValueOnce({ permissions: { group_access: { access_level: 10 } } }); + + await expect(api.hasWriteAccess()).resolves.toBe(false); + }); + + test('should return true on shared group access_level >= 40', async () => { + const api = new API({ repo: 'repo' }); + api.requestJSON = jest.fn().mockResolvedValueOnce({ + permissions: { project_access: null, group_access: null }, + shared_with_groups: [{ group_access_level: 10 }, { group_access_level: 40 }], + }); + + await expect(api.hasWriteAccess()).resolves.toBe(true); + + expect(api.requestJSON).toHaveBeenCalledTimes(1); + }); + + test('should return true on shared group access_level >= 30, developers can merge and push', async () => { + const api = new API({ repo: 'repo' }); + + const requestJSONMock = (api.requestJSON = jest.fn()); + requestJSONMock.mockResolvedValueOnce({ + permissions: { project_access: null, group_access: null }, + shared_with_groups: [{ group_access_level: 10 }, { group_access_level: 30 }], + }); + requestJSONMock.mockResolvedValueOnce({ + developers_can_merge: true, + developers_can_push: true, + }); + + await expect(api.hasWriteAccess()).resolves.toBe(true); + }); + + test('should return false on shared group access_level < 30,', async () => { + const api = new API({ repo: 'repo' }); + + const requestJSONMock = (api.requestJSON = jest.fn()); + requestJSONMock.mockResolvedValueOnce({ + permissions: { project_access: null, group_access: null }, + shared_with_groups: [{ group_access_level: 10 }, { group_access_level: 20 }], + }); + requestJSONMock.mockResolvedValueOnce({ + developers_can_merge: true, + developers_can_push: true, + }); + + await expect(api.hasWriteAccess()).resolves.toBe(false); + }); + + test("should return false on shared group access_level >= 30, developers can't merge", async () => { + const api = new API({ repo: 'repo' }); + + const requestJSONMock = (api.requestJSON = jest.fn()); + requestJSONMock.mockResolvedValueOnce({ + permissions: { project_access: null, group_access: null }, + shared_with_groups: [{ group_access_level: 10 }, { group_access_level: 30 }], + }); + requestJSONMock.mockResolvedValueOnce({ + developers_can_merge: false, + developers_can_push: true, + }); + + await expect(api.hasWriteAccess()).resolves.toBe(false); + }); + + test("should return false on shared group access_level >= 30, developers can't push", async () => { + const api = new API({ repo: 'repo' }); + + const requestJSONMock = (api.requestJSON = jest.fn()); + requestJSONMock.mockResolvedValueOnce({ + permissions: { project_access: null, group_access: null }, + shared_with_groups: [{ group_access_level: 10 }, { group_access_level: 30 }], + }); + requestJSONMock.mockResolvedValueOnce({ + developers_can_merge: true, + developers_can_push: false, + }); + + await expect(api.hasWriteAccess()).resolves.toBe(false); + }); + + test('should return false on shared group access_level >= 30, error getting branch', async () => { + const api = new API({ repo: 'repo' }); + + const requestJSONMock = (api.requestJSON = jest.fn()); + requestJSONMock.mockResolvedValueOnce({ + permissions: { project_access: null, group_access: null }, + shared_with_groups: [{ group_access_level: 10 }, { group_access_level: 30 }], + }); + + const error = new Error('Not Found'); + requestJSONMock.mockRejectedValue(error); + + await expect(api.hasWriteAccess()).resolves.toBe(false); + + expect(console.error).toHaveBeenCalledTimes(1); + expect(console.error).toHaveBeenCalledWith('Failed getting default branch', error); + }); + }); + + describe('getMaxAccess', () => { + it('should return group with max access level', () => { + const groups = [ + { group_access_level: 10 }, + { group_access_level: 5 }, + { group_access_level: 100 }, + { group_access_level: 1 }, + ]; + expect(getMaxAccess(groups)).toBe(groups[2]); + }); + }); +}); diff --git a/core/src/interface.ts b/core/src/interface.ts index 70d18519..483fa2bd 100644 --- a/core/src/interface.ts +++ b/core/src/interface.ts @@ -891,3 +891,9 @@ export enum CollectionType { FOLDER, FILES, } + +export type DeepPartial = T extends object + ? { + [P in keyof T]?: DeepPartial; + } + : T; diff --git a/core/src/lib/util/__tests__/object.util.ts b/core/src/lib/util/__tests__/object.util.spec.ts similarity index 100% rename from core/src/lib/util/__tests__/object.util.ts rename to core/src/lib/util/__tests__/object.util.spec.ts diff --git a/core/src/tsconfig.json b/core/src/tsconfig.json new file mode 100644 index 00000000..e7cb91eb --- /dev/null +++ b/core/src/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../tsconfig.dev.json" +} diff --git a/core/test/fileTransformer.js b/core/test/fileTransformer.js new file mode 100644 index 00000000..03d9be3b --- /dev/null +++ b/core/test/fileTransformer.js @@ -0,0 +1,9 @@ +const path = require('path'); + +module.exports = { + process(src, filename) { + return { + code: `module.exports = ${JSON.stringify(path.basename(filename))};`, + }; + }, +}; diff --git a/core/test/mockFetch.ts b/core/test/mockFetch.ts new file mode 100644 index 00000000..22edff1a --- /dev/null +++ b/core/test/mockFetch.ts @@ -0,0 +1,90 @@ +export const createMockRequest = ( + status: number, + data: { + json?: T; + text?: string; + }, + options: { + contentType?: string; + headers?: Record; + } = {}, +): Response => { + const { contentType = 'application/json', headers = {} } = options; + + const finalHeaders = (function () { + const data: Record = headers; + + return { + get: (key: string) => { + return data[key]; + }, + set: (key: string, value: string) => { + data[key] = value; + }, + }; + })(); + + finalHeaders.set('Content-Type', contentType); + + return { + status, + ok: status < 400, + headers: finalHeaders, + json: () => Promise.resolve(data.json), + text: () => Promise.resolve(data.text), + } as Response; +}; + +export interface MockFetch { + baseUrl: string; + mocks: Record; + when: (url: string) => { + reply: ( + status: number, + data: { + json?: T; + text?: string; + }, + options?: { + contentType?: string; + headers?: Record; + }, + ) => void; + }; + reset: () => void; +} + +const mockFetch = (baseUrl: string): MockFetch => { + const mockedFetch: MockFetch = { + baseUrl, + mocks: {}, + when(this: MockFetch, url: string) { + return { + reply: ( + status: number, + data: { + json?: T; + text?: string; + }, + options?: { + contentType?: string; + headers?: Record; + }, + ) => { + this.mocks[`${baseUrl}${url}`] = createMockRequest(status, data, options); + }, + }; + }, + reset(this: MockFetch) { + this.mocks = {}; + }, + }; + + global.fetch = jest.fn().mockImplementation((url: string) => { + return Promise.resolve(mockedFetch.mocks[url.split('?')[0]]); + }); + + return mockedFetch; +}; + +export default mockFetch; diff --git a/core/test/mockLocalStorage.ts b/core/test/mockLocalStorage.ts new file mode 100644 index 00000000..fed1b583 --- /dev/null +++ b/core/test/mockLocalStorage.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +const mockLocalStorage = (function () { + let store: Record = {}; + return { + getItem(key: string) { + return store[key] ?? null; + }, + setItem(key: string, value: any) { + store[key] = value.toString(); + }, + clear() { + store = {}; + }, + removeItem(key: string) { + delete store[key]; + }, + }; +})(); + +Object.defineProperty(window, 'localStorage', { value: mockLocalStorage }); + +export {}; diff --git a/core/test/setupEnv.js b/core/test/setupEnv.js new file mode 100644 index 00000000..5835880d --- /dev/null +++ b/core/test/setupEnv.js @@ -0,0 +1,14 @@ +global.TextEncoder = TextEncoder; +global.TextDecoder = TextDecoder; + +if (typeof window === 'undefined') { + global.window = { + URL: { + createObjectURL: jest.fn(), + }, + localStorage: { + removeItem: jest.fn(), + getItem: jest.fn(), + }, + }; +} diff --git a/core/tsconfig.base.json b/core/tsconfig.base.json new file mode 100644 index 00000000..d3528b9c --- /dev/null +++ b/core/tsconfig.base.json @@ -0,0 +1,59 @@ +{ + "compilerOptions": { + "declaration": true, + "declarationDir": "dist", + "emitDeclarationOnly": true, + "jsx": "react", + "target": "esnext", + "module": "esnext", + "moduleResolution": "node", + "esModuleInterop": true, + "preserveSymlinks": true, + "noEmit": false, + "strict": true, + "isolatedModules": true, + "skipLibCheck": true, + "allowJs": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "baseUrl": "./", + "lib": ["DOM", "es6", "ES2015"], + "paths": { + "@staticcms/boolean": ["./src/widgets/boolean"], + "@staticcms/boolean/*": ["./src/widgets/boolean/*"], + "@staticcms/code": ["./src/widgets/code"], + "@staticcms/code/*": ["./src/widgets/code/*"], + "@staticcms/colorstring": ["./src/widgets/colorstring"], + "@staticcms/colorstring/*": ["./src/widgets/colorstring/*"], + "@staticcms/datetime": ["./src/widgets/datetime"], + "@staticcms/datetime/*": ["./src/widgets/datetime/*"], + "@staticcms/file": ["./src/widgets/file"], + "@staticcms/file/*": ["./src/widgets/file/*"], + "@staticcms/image": ["./src/widgets/image"], + "@staticcms/image/*": ["./src/widgets/image/*"], + "@staticcms/list": ["./src/widgets/list"], + "@staticcms/list/*": ["./src/widgets/list/*"], + "@staticcms/map": ["./src/widgets/map"], + "@staticcms/map/*": ["./src/widgets/map/*"], + "@staticcms/markdown": ["./src/widgets/markdown"], + "@staticcms/markdown/*": ["./src/widgets/markdown/*"], + "@staticcms/number": ["./src/widgets/number"], + "@staticcms/number/*": ["./src/widgets/number/*"], + "@staticcms/object": ["./src/widgets/object"], + "@staticcms/object/*": ["./src/widgets/object/*"], + "@staticcms/relation": ["./src/widgets/relation"], + "@staticcms/relation/*": ["./src/widgets/relation/*"], + "@staticcms/select": ["./src/widgets/select"], + "@staticcms/select/*": ["./src/widgets/select/*"], + "@staticcms/string": ["./src/widgets/string"], + "@staticcms/string/*": ["./src/widgets/string/*"], + "@staticcms/text": ["./src/widgets/text"], + "@staticcms/text/*": ["./src/widgets/text/*"], + "@staticcms/test": ["./test"], + "@staticcms/test/*": ["./test/*"], + "@staticcms/core": ["./src"], + "@staticcms/core/*": ["./src/*"] + }, + "types": ["@emotion/react/types/css-prop", "@types/jest"] + } +} diff --git a/core/tsconfig.dev.json b/core/tsconfig.dev.json new file mode 100644 index 00000000..d69b290d --- /dev/null +++ b/core/tsconfig.dev.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.base.json", + "include": ["src/**/*"], + "exclude": ["node_modules"] +} diff --git a/core/tsconfig.json b/core/tsconfig.json index 0ee5be05..f7e1df79 100644 --- a/core/tsconfig.json +++ b/core/tsconfig.json @@ -1,59 +1,5 @@ { - "compilerOptions": { - "declaration": true, - "declarationDir": "dist", - "emitDeclarationOnly": true, - "jsx": "react", - "target": "esnext", - "module": "esnext", - "moduleResolution": "node", - "esModuleInterop": true, - "preserveSymlinks": true, - "noEmit": false, - "strict": true, - "isolatedModules": true, - "skipLibCheck": true, - "allowJs": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "baseUrl": "./", - "lib": ["DOM", "es6", "ES2015"], - "paths": { - "@staticcms/boolean": ["./src/widgets/boolean"], - "@staticcms/boolean/*": ["./src/widgets/boolean/*"], - "@staticcms/code": ["./src/widgets/code"], - "@staticcms/code/*": ["./src/widgets/code/*"], - "@staticcms/colorstring": ["./src/widgets/colorstring"], - "@staticcms/colorstring/*": ["./src/widgets/colorstring/*"], - "@staticcms/datetime": ["./src/widgets/datetime"], - "@staticcms/datetime/*": ["./src/widgets/datetime/*"], - "@staticcms/file": ["./src/widgets/file"], - "@staticcms/file/*": ["./src/widgets/file/*"], - "@staticcms/image": ["./src/widgets/image"], - "@staticcms/image/*": ["./src/widgets/image/*"], - "@staticcms/list": ["./src/widgets/list"], - "@staticcms/list/*": ["./src/widgets/list/*"], - "@staticcms/map": ["./src/widgets/map"], - "@staticcms/map/*": ["./src/widgets/map/*"], - "@staticcms/markdown": ["./src/widgets/markdown"], - "@staticcms/markdown/*": ["./src/widgets/markdown/*"], - "@staticcms/number": ["./src/widgets/number"], - "@staticcms/number/*": ["./src/widgets/number/*"], - "@staticcms/object": ["./src/widgets/object"], - "@staticcms/object/*": ["./src/widgets/object/*"], - "@staticcms/relation": ["./src/widgets/relation"], - "@staticcms/relation/*": ["./src/widgets/relation/*"], - "@staticcms/select": ["./src/widgets/select"], - "@staticcms/select/*": ["./src/widgets/select/*"], - "@staticcms/string": ["./src/widgets/string"], - "@staticcms/string/*": ["./src/widgets/string/*"], - "@staticcms/text": ["./src/widgets/text"], - "@staticcms/text/*": ["./src/widgets/text/*"], - "@staticcms/core": ["./src"], - "@staticcms/core/*": ["./src/*"], - }, - "types": ["@emotion/react/types/css-prop", "@types/jest"] - }, + "extends": "./tsconfig.base.json", "include": ["src/**/*"], "exclude": ["node_modules", "**/*.spec.ts"] } diff --git a/core/yarn.lock b/core/yarn.lock index b25fe929..1f72ab40 100644 --- a/core/yarn.lock +++ b/core/yarn.lock @@ -981,7 +981,7 @@ "@babel/helper-validator-option" "^7.18.6" "@babel/plugin-transform-typescript" "^7.18.6" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.6", "@babel/runtime@^7.18.9", "@babel/runtime@^7.19.0", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@7.20.6", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.6", "@babel/runtime@^7.18.9", "@babel/runtime@^7.19.0", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.20.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.6.tgz#facf4879bfed9b5326326273a64220f099b0fce3" integrity sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA== @@ -1027,7 +1027,7 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@codemirror/autocomplete@^6.0.0", "@codemirror/autocomplete@^6.3.2": +"@codemirror/autocomplete@6.3.4", "@codemirror/autocomplete@^6.0.0", "@codemirror/autocomplete@^6.3.2": version "6.3.4" resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-6.3.4.tgz#dd22f0a0220718e1f6e3b48319649859b6c929b6" integrity sha512-irxKsTSjS0OkfMWWt9YxtNK97++/E+XIHfKnRpSVfZyHzda/amYF0BR+T8mMkrGQWidx2zApxHx08GT13egyQA== @@ -1190,7 +1190,7 @@ "@lezer/common" "^1.0.0" "@lezer/xml" "^1.0.0" -"@codemirror/language-data@>=6.0.0": +"@codemirror/language-data@6.1.0", "@codemirror/language-data@>=6.0.0": version "6.1.0" resolved "https://registry.yarnpkg.com/@codemirror/language-data/-/language-data-6.1.0.tgz#479eff66289a6453493f7c8213d7b2ceb95c89f6" integrity sha512-g9V23fuLRI9AEbpM6bDy1oquqgpFlIDHTihUhL21NPmxp+x67ZJbsKk+V71W7/Bj8SCqEO1PtqQA/tDGgt1nfw== @@ -1211,7 +1211,7 @@ "@codemirror/language" "^6.0.0" "@codemirror/legacy-modes" "^6.1.0" -"@codemirror/language@^6.0.0", "@codemirror/language@^6.3.0": +"@codemirror/language@6.3.1", "@codemirror/language@^6.0.0", "@codemirror/language@^6.3.0": version "6.3.1" resolved "https://registry.yarnpkg.com/@codemirror/language/-/language-6.3.1.tgz#1d61f33aa5de9aa74a713ee1f5ce600adc74df6b" integrity sha512-MK+G1QKaGfSEUg9YEFaBkMBI6j1ge4VMBPZv9fDYotw7w695c42x5Ba1mmwBkesYnzYFBfte6Hh9TDcKa6xORQ== @@ -1223,14 +1223,14 @@ "@lezer/lr" "^1.0.0" style-mod "^4.0.0" -"@codemirror/legacy-modes@>=6.0.0", "@codemirror/legacy-modes@^6.1.0": +"@codemirror/legacy-modes@6.3.1", "@codemirror/legacy-modes@>=6.0.0", "@codemirror/legacy-modes@^6.1.0": version "6.3.1" resolved "https://registry.yarnpkg.com/@codemirror/legacy-modes/-/legacy-modes-6.3.1.tgz#77ab3f3db1ce3e47aad7a5baac3a4b12844734a5" integrity sha512-icXmCs4Mhst2F8mE0TNpmG6l7YTj1uxam3AbZaFaabINH5oWAdg2CfR/PVi+d/rqxJ+TuTnvkKK5GILHrNThtw== dependencies: "@codemirror/language" "^6.0.0" -"@codemirror/lint@^6.0.0": +"@codemirror/lint@6.1.0", "@codemirror/lint@^6.0.0": version "6.1.0" resolved "https://registry.yarnpkg.com/@codemirror/lint/-/lint-6.1.0.tgz#f006142d3a580fdb8ffc2faa3361b2232c08e079" integrity sha512-mdvDQrjRmYPvQ3WrzF6Ewaao+NWERYtpthJvoQ3tK3t/44Ynhk8ZGjTSL9jMEv8CgSMogmt75X8ceOZRDSXHtQ== @@ -1239,7 +1239,7 @@ "@codemirror/view" "^6.0.0" crelt "^1.0.5" -"@codemirror/search@^6.0.0": +"@codemirror/search@6.2.3", "@codemirror/search@^6.0.0": version "6.2.3" resolved "https://registry.yarnpkg.com/@codemirror/search/-/search-6.2.3.tgz#fab933fef1b1de8ef40cda275c73d9ac7a1ff40f" integrity sha512-V9n9233lopQhB1dyjsBK2Wc1i+8hcCqxl1wQ46c5HWWLePoe4FluV3TGHoZ04rBRlGjNyz9DTmpJErig8UE4jw== @@ -1248,12 +1248,12 @@ "@codemirror/view" "^6.0.0" crelt "^1.0.5" -"@codemirror/state@^6.0.0", "@codemirror/state@^6.1.1", "@codemirror/state@^6.1.4": +"@codemirror/state@6.1.4", "@codemirror/state@^6.0.0", "@codemirror/state@^6.1.1", "@codemirror/state@^6.1.4": version "6.1.4" resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.1.4.tgz#2b654ae233ac4f41ee89ce095509ea35ecdf1031" integrity sha512-g+3OJuRylV5qsXuuhrc6Cvs1NQluNioepYMM2fhnpYkNk7NgX+j0AFuevKSVKzTDmDyt9+Puju+zPdHNECzCNQ== -"@codemirror/theme-one-dark@^6.0.0": +"@codemirror/theme-one-dark@6.1.0", "@codemirror/theme-one-dark@^6.0.0": version "6.1.0" resolved "https://registry.yarnpkg.com/@codemirror/theme-one-dark/-/theme-one-dark-6.1.0.tgz#6f8b3c7fc22e9fec59edd573f4ba9546db42e007" integrity sha512-AiTHtFRu8+vWT9wWUWDM+cog6ZwgivJogB1Tm/g40NIpLwph7AnmxrSzWfvJN5fBVufsuwBxecQCNmdcR5D7Aw== @@ -1814,7 +1814,7 @@ resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== -"@lezer/common@^1.0.0": +"@lezer/common@1.0.2", "@lezer/common@^1.0.0": version "1.0.2" resolved "https://registry.yarnpkg.com/@lezer/common/-/common-1.0.2.tgz#8fb9b86bdaa2ece57e7d59e5ffbcb37d71815087" integrity sha512-SVgiGtMnMnW3ActR8SXgsDhw7a0w0ChHSYAyAUxxrOiJ1OqYWEKk/xJd84tTSPo1mo6DXLObAJALNnd0Hrv7Ng== @@ -2451,6 +2451,11 @@ dependencies: tippy.js "^6.3.1" +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + "@trysound/sax@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" @@ -2692,6 +2697,15 @@ resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138" integrity sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA== +"@types/jsdom@^20.0.0": + version "20.0.1" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808" + integrity sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ== + dependencies: + "@types/node" "*" + "@types/tough-cookie" "*" + parse5 "^7.0.0" + "@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" @@ -2918,6 +2932,11 @@ "@types/react" "*" csstype "^3.0.2" +"@types/tough-cookie@*": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" + integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== + "@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" @@ -3880,6 +3899,14 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" +acorn-globals@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" + integrity sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q== + dependencies: + acorn "^8.1.0" + acorn-walk "^8.0.2" + acorn-import-assertions@^1.7.6: version "1.8.0" resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" @@ -3890,11 +3917,23 @@ acorn-jsx@^5.0.0, acorn-jsx@^5.3.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn@^8.0.0, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0: +acorn-walk@^8.0.2: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^8.0.0, acorn@^8.1.0, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0, acorn@^8.8.1: version "8.8.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + ajv-errors@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-3.0.0.tgz#e54f299f3a3d30fe144161e5f0d8d51196c527bc" @@ -4524,6 +4563,13 @@ browserslist@^4.14.5, browserslist@^4.21.3, browserslist@^4.21.4: node-releases "^2.0.6" update-browserslist-db "^1.0.9" +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -5200,6 +5246,23 @@ csso@^4.0.2, csso@^4.2.0: dependencies: css-tree "^1.1.2" +cssom@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" + integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + csstype@^3.0.2, csstype@^3.0.6, csstype@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" @@ -5213,6 +5276,15 @@ d@1, d@^1.0.1: es5-ext "^0.10.50" type "^1.0.1" +data-urls@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" + integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== + dependencies: + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" + date-fns@2.29.3: version "2.29.3" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8" @@ -5225,6 +5297,13 @@ debug@2.6.9, debug@^2.2.0, debug@^2.6.8, debug@^2.6.9: dependencies: ms "2.0.0" +debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -5232,13 +5311,6 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - decache@^4.5.1: version "4.6.1" resolved "https://registry.yarnpkg.com/decache/-/decache-4.6.1.tgz#5928bfab97a6fcf22a65047a3d07999af36efaf0" @@ -5246,6 +5318,11 @@ decache@^4.5.1: dependencies: callsite "^1.0.0" +decimal.js@^10.4.2: + version "10.4.2" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.2.tgz#0341651d1d997d86065a2ce3a441fbd0d8e8b98e" + integrity sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA== + decode-named-character-reference@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e" @@ -5263,7 +5340,7 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== -deep-is@^0.1.3: +deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== @@ -5442,6 +5519,13 @@ domelementtype@^2.0.1, domelementtype@^2.2.0: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== + dependencies: + webidl-conversions "^7.0.0" + domhandler@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a" @@ -5544,6 +5628,11 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== +entities@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174" + integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA== + entities@~2.0: version "2.0.3" resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f" @@ -5685,6 +5774,18 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +escodegen@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + eslint-config-prettier@8.5.0: version "8.5.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" @@ -5911,7 +6012,7 @@ espree@^9.4.0: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.3.0" -esprima@^4.0.0: +esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -6144,12 +6245,12 @@ fast-glob@^3.2.11, fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== @@ -6792,6 +6893,15 @@ http-parser-js@>=0.5.1: resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + http-proxy-middleware@^2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" @@ -6831,6 +6941,14 @@ http-server@14.1.1: union "~0.5.0" url-join "^4.0.1" +https-proxy-agent@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -7183,6 +7301,11 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + is-reference@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-3.0.0.tgz#b1380c03d96ddf7089709781e3208fceb0c92cd6" @@ -7417,6 +7540,20 @@ jest-each@^29.3.1: jest-util "^29.3.1" pretty-format "^29.3.1" +jest-environment-jsdom@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.3.1.tgz#14ca63c3e0ef5c63c5bcb46033e50bc649e3b639" + integrity sha512-G46nKgiez2Gy4zvYNhayfMEAFlVHhWfncqvqS6yCd0i+a4NsSUD2WtrKSaYQrYiLQaupHXxCRi8xxVL2M9PbhA== + dependencies: + "@jest/environment" "^29.3.1" + "@jest/fake-timers" "^29.3.1" + "@jest/types" "^29.3.1" + "@types/jsdom" "^20.0.0" + "@types/node" "*" + jest-mock "^29.3.1" + jest-util "^29.3.1" + jsdom "^20.0.0" + jest-environment-node@^29.3.1: version "29.3.1" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.3.1.tgz#5023b32472b3fba91db5c799a0d5624ad4803e74" @@ -7613,7 +7750,7 @@ jest-snapshot@^29.3.1: pretty-format "^29.3.1" semver "^7.3.5" -jest-util@^29.3.1: +jest-util@^29.0.0, jest-util@^29.3.1: version "29.3.1" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.3.1.tgz#1dda51e378bbcb7e3bc9d8ab651445591ed373e1" integrity sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ== @@ -7740,6 +7877,38 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +jsdom@^20.0.0: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" + integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ== + dependencies: + abab "^2.0.6" + acorn "^8.8.1" + acorn-globals "^7.0.0" + cssom "^0.5.0" + cssstyle "^2.3.0" + data-urls "^3.0.2" + decimal.js "^10.4.2" + domexception "^4.0.0" + escodegen "^2.0.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.2" + parse5 "^7.1.1" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^4.1.2" + 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.11.0" + xml-name-validator "^4.0.0" + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -7785,11 +7954,6 @@ json-stringify-pretty-compact@^2.0.0: resolved "https://registry.yarnpkg.com/json-stringify-pretty-compact/-/json-stringify-pretty-compact-2.0.0.tgz#e77c419f52ff00c45a31f07f4c820c2433143885" integrity sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ== -json-stringify-safe@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== - json5@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" @@ -7881,6 +8045,14 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + li@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/li/-/li-1.3.0.tgz#22c59bcaefaa9a8ef359cf759784e4bf106aea1b" @@ -7977,6 +8149,11 @@ lodash.isplainobject@^4.0.6: resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== +lodash.memoize@4.x: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -8043,6 +8220,11 @@ make-dir@^3.0.0, make-dir@^3.0.2: dependencies: semver "^6.0.0" +make-error@1.x: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + makeerror@1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" @@ -8886,16 +9068,6 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -nock@13.2.9: - version "13.2.9" - resolved "https://registry.yarnpkg.com/nock/-/nock-13.2.9.tgz#4faf6c28175d36044da4cfa68e33e5a15086ad4c" - integrity sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA== - dependencies: - debug "^4.1.0" - json-stringify-safe "^5.0.1" - lodash "^4.17.21" - propagate "^2.0.0" - node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -8999,6 +9171,11 @@ nth-check@^2.0.1: dependencies: boolbase "^1.0.0" +nwsapi@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" + integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== + object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -9145,6 +9322,18 @@ opener@^1.5.1: resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + optionator@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" @@ -9303,6 +9492,13 @@ parse5@^6.0.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== +parse5@^7.0.0, parse5@^7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -9497,6 +9693,11 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== + prettier-linter-helpers@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" @@ -9550,11 +9751,6 @@ prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.5.8, object-assign "^4.1.1" react-is "^16.13.1" -propagate@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" - integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== - property-information@^6.0.0: version "6.2.0" resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.2.0.tgz#b74f522c31c097b5149e3c3cb8d7f3defd986a1d" @@ -9583,6 +9779,11 @@ proxy-from-env@^1.1.0: resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +psl@^1.1.33: + version "1.9.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -9596,7 +9797,7 @@ punycode@1.3.2: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== -punycode@^2.1.0: +punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== @@ -9637,6 +9838,11 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -10326,7 +10532,14 @@ sax@~1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -scheduler@^0.23.0: +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== + dependencies: + xmlchars "^2.2.0" + +scheduler@0.23.0, scheduler@^0.23.0: version "0.23.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== @@ -10404,18 +10617,18 @@ semaphore@1.1.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: +semver@7.x, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: version "7.3.8" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== dependencies: lru-cache "^6.0.0" +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + send@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" @@ -10673,7 +10886,7 @@ source-map@^0.5.7: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -source-map@^0.6.0, source-map@^0.6.1: +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -11072,6 +11285,11 @@ symbol-observable@4.0.0: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + synckit@^0.8.4: version "0.8.4" resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.4.tgz#0e6b392b73fafdafcde56692e3352500261d64ec" @@ -11214,6 +11432,23 @@ topbar@^0.1.3: resolved "https://registry.yarnpkg.com/topbar/-/topbar-0.1.4.tgz#d050e536ef5053d9629499ed71008b88cb2398fb" integrity sha512-P3n4WnN4GFd2mQXDo30rQmsAGe4V1bVkggtTreSbNyL50Fyc+eVkW5oatSLeGQmJoan2TLIgoXUZypN+6nw4MQ== +tough-cookie@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" + integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + +tr46@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" + integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== + dependencies: + punycode "^2.1.1" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -11251,6 +11486,20 @@ ts-easing@^0.2.0: resolved "https://registry.yarnpkg.com/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec" integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ== +ts-jest@29.0.3: + version "29.0.3" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.0.3.tgz#63ea93c5401ab73595440733cefdba31fcf9cb77" + integrity sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ== + dependencies: + bs-logger "0.x" + fast-json-stable-stringify "2.x" + jest-util "^29.0.0" + json5 "^2.2.1" + lodash.memoize "4.x" + make-error "1.x" + semver "7.x" + yargs-parser "^21.0.1" + ts-loader@9.4.2: version "9.4.2" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.4.2.tgz#80a45eee92dd5170b900b3d00abcfa14949aeb78" @@ -11313,6 +11562,13 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== + dependencies: + prelude-ls "~1.1.2" + type-detect@4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" @@ -11515,6 +11771,11 @@ universal-user-agent@^6.0.0: resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + universalify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" @@ -11568,6 +11829,14 @@ url-join@^4.0.1: resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + url@0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -11760,6 +12029,13 @@ w3c-keyname@^2.2.4: resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.6.tgz#8412046116bc16c5d73d4e612053ea10a189c85f" integrity sha512-f+fciywl1SJEniZHD6H+kUO8gOnwIr7f4ijKA6+ZvJFjeGi1r4PDLl53Ayud9O/rk64RqgoQine0feoeOU0kXg== +w3c-xmlserializer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" + integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== + dependencies: + xml-name-validator "^4.0.0" + walker@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" @@ -11823,6 +12099,11 @@ webidl-conversions@^3.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + webpack-cli@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.0.0.tgz#bd380a9653e0cd1a08916c4ff1adea17201ef68f" @@ -11974,6 +12255,19 @@ whatwg-encoding@^2.0.0: dependencies: iconv-lite "0.6.3" +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + +whatwg-url@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" + integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== + dependencies: + tr46 "^3.0.0" + webidl-conversions "^7.0.0" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -12019,7 +12313,7 @@ windows-release@^3.1.0: dependencies: execa "^1.0.0" -word-wrap@^1.2.3: +word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== @@ -12046,7 +12340,7 @@ write-file-atomic@^4.0.1: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@^8.4.2: +ws@^8.11.0, ws@^8.4.2: version "8.11.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== @@ -12056,11 +12350,21 @@ xcase@^2.0.1: resolved "https://registry.yarnpkg.com/xcase/-/xcase-2.0.1.tgz#c7fa72caa0f440db78fd5673432038ac984450b9" integrity sha512-UmFXIPU+9Eg3E9m/728Bii0lAIuoc+6nbrNUKaRPJOFp91ih44qqGlWtxMB6kXFrRD6po+86ksHM5XHCfk6iPw== +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + xml-utils@^1.0.2: version "1.3.0" resolved "https://registry.yarnpkg.com/xml-utils/-/xml-utils-1.3.0.tgz#f1043534e3ac3deda12ddab39f8442e16da98ebb" integrity sha512-i4PIrX33Wd66dvwo4syicwlwmnr6wuvvn4f2ku9hA67C2Uk62Xubczuhct+Evnd12/DV71qKNeDdJwES8HX1RA== +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" @@ -12081,7 +12385,7 @@ yaml@1.10.2, yaml@^1.10.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yargs-parser@^21.1.1: +yargs-parser@^21.0.1, yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==