From 3b72f90ca5037ef84c73c556f83265146fd53b1f Mon Sep 17 00:00:00 2001 From: "Kilu.He" <108015703+qinluhe@users.noreply.github.com> Date: Thu, 6 Jun 2024 17:48:58 +0800 Subject: [PATCH] feat: improve test coverage (#5481) --- .github/workflows/web_coverage.yaml | 16 +- frontend/appflowy_web_app/.nycrc | 5 +- frontend/appflowy_web_app/jest.config.cjs | 22 +- frontend/appflowy_web_app/package.json | 7 +- frontend/appflowy_web_app/pnpm-lock.yaml | 1067 +++++++++++------ .../scripts/merge-coverage.cjs | 31 - .../src/application/collab.type.ts | 14 +- .../database-yjs/__tests__/filter.test.ts | 10 + .../__tests__/fixtures/sorts.json | 20 + .../database-yjs/__tests__/group.test.ts | 76 +- .../database-yjs/__tests__/parse.test.ts | 72 ++ .../database-yjs/__tests__/selector.test.tsx | 283 +++++ .../database-yjs/__tests__/sort.test.ts | 83 ++ .../database-yjs/__tests__/withTestingCell.ts | 43 + .../database-yjs/__tests__/withTestingData.ts | 154 ++- .../__tests__/withTestingField.ts | 59 +- .../database-yjs/__tests__/withTestingRows.ts | 2 + .../__tests__/withTestingSorts.ts | 22 + .../database-yjs}/cell.parse.ts | 0 .../database-yjs}/cell.type.ts | 2 +- .../src/application/database-yjs/const.ts | 10 +- .../src/application/database-yjs/context.ts | 15 - .../database-yjs/fields/date/utils.test.ts | 21 + .../src/application/database-yjs/filter.ts | 23 +- .../src/application/database-yjs/selector.ts | 21 +- .../src/application/database-yjs/sort.ts | 9 +- .../slate-yjs/__tests__/applyRemoteEvents.ts | 49 + .../slate-yjs/__tests__/convert.test.ts | 268 +++++ .../{utils => }/__tests__/convert.ts | 33 +- .../slate-yjs/__tests__/index.test.ts | 67 ++ .../__tests__/withTestingYjsEditor.ts | 135 +++ .../utils/__tests__/applyRemoteEvents.ts | 67 -- .../slate-yjs/utils/__tests__/index.test.ts | 12 - .../utils/__tests__/withTestingYjsEditor.ts | 45 - .../application/slate-yjs/utils/convert.ts | 105 +- .../calendar/event/EventPaperTitle.tsx | 2 +- .../database/components/cell/Cell.tsx | 2 +- .../database/components/cell/cell.const.ts | 12 - .../components/cell/checkbox/CheckboxCell.tsx | 2 +- .../cell/checklist/ChecklistCell.tsx | 2 +- .../components/cell/date/DateTimeCell.tsx | 2 +- .../components/cell/number/NumberCell.tsx | 2 +- .../components/cell/primary/PrimaryCell.tsx | 2 +- .../components/cell/relation/RelationCell.tsx | 2 +- .../cell/relation/RelationItems.tsx | 2 +- .../cell/relation/RelationPrimaryValue.tsx | 2 +- .../cell/select-option/SelectOptionCell.tsx | 2 +- .../components/cell/text/TextCell.tsx | 2 +- .../database/components/cell/url/UrlCell.tsx | 2 +- .../components/grid/grid-cell/GridCell.tsx | 2 +- .../grid/grid-row/useRenderRows.tsx | 13 +- .../database/components/property/Property.tsx | 2 +- .../property/cheklist/ChecklistProperty.tsx | 2 +- .../components/property/text/TextProperty.tsx | 2 +- .../src/components/database/grid/Grid.tsx | 33 +- frontend/appflowy_web_app/src/utils/font.ts | 15 +- frontend/appflowy_web_app/vite.config.ts | 15 +- 57 files changed, 2274 insertions(+), 716 deletions(-) delete mode 100644 frontend/appflowy_web_app/scripts/merge-coverage.cjs create mode 100644 frontend/appflowy_web_app/src/application/database-yjs/__tests__/parse.test.ts create mode 100644 frontend/appflowy_web_app/src/application/database-yjs/__tests__/selector.test.tsx create mode 100644 frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingCell.ts rename frontend/appflowy_web_app/src/{components/database/components/cell => application/database-yjs}/cell.parse.ts (100%) rename frontend/appflowy_web_app/src/{components/database/components/cell => application/database-yjs}/cell.type.ts (99%) create mode 100644 frontend/appflowy_web_app/src/application/database-yjs/fields/date/utils.test.ts create mode 100644 frontend/appflowy_web_app/src/application/slate-yjs/__tests__/applyRemoteEvents.ts create mode 100644 frontend/appflowy_web_app/src/application/slate-yjs/__tests__/convert.test.ts rename frontend/appflowy_web_app/src/application/slate-yjs/{utils => }/__tests__/convert.ts (65%) create mode 100644 frontend/appflowy_web_app/src/application/slate-yjs/__tests__/index.test.ts create mode 100644 frontend/appflowy_web_app/src/application/slate-yjs/__tests__/withTestingYjsEditor.ts delete mode 100644 frontend/appflowy_web_app/src/application/slate-yjs/utils/__tests__/applyRemoteEvents.ts delete mode 100644 frontend/appflowy_web_app/src/application/slate-yjs/utils/__tests__/index.test.ts delete mode 100644 frontend/appflowy_web_app/src/application/slate-yjs/utils/__tests__/withTestingYjsEditor.ts diff --git a/.github/workflows/web_coverage.yaml b/.github/workflows/web_coverage.yaml index 258119664a..7803f719c9 100644 --- a/.github/workflows/web_coverage.yaml +++ b/.github/workflows/web_coverage.yaml @@ -6,6 +6,7 @@ on: - ".github/workflows/web2_ci.yaml" - "frontend/appflowy_web_app/**" - "frontend/resources/**" + env: NODE_VERSION: "18.16.0" PNPM_VERSION: "8.5.0" @@ -52,8 +53,13 @@ jobs: run: | pnpm run test:unit - - name: Generate and post coverage summary - working-directory: frontend/appflowy_web_app - run: | - pnpm run merge-coverage - + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v2 + with: + token: cf9245e0-e136-4e21-b0ee-35755fa0c493 + files: frontend/appflowy_web_app/coverage/jest/lcov.info,frontend/appflowy_web_app/coverage/cypress/lcov.info + flags: appflowy_web_app + name: frontend/appflowy_web_app + fail_ci_if_error: true + verbose: true + diff --git a/frontend/appflowy_web_app/.nycrc b/frontend/appflowy_web_app/.nycrc index 9d69b3c074..dc571c1abb 100644 --- a/frontend/appflowy_web_app/.nycrc +++ b/frontend/appflowy_web_app/.nycrc @@ -1,6 +1,6 @@ { "all": true, - "extends": "@istanbuljs/nyc-config-typescript", + "extends": "@istanbuljs/nyc-config-babel", "include": [ "src/**/*.ts", "src/**/*.tsx" @@ -15,7 +15,8 @@ "text", "html", "text-summary", - "json" + "json", + "lcov" ], "temp-dir": "coverage/.nyc_output", "report-dir": "coverage/cypress" diff --git a/frontend/appflowy_web_app/jest.config.cjs b/frontend/appflowy_web_app/jest.config.cjs index b21b3c1ae3..b226459c4b 100644 --- a/frontend/appflowy_web_app/jest.config.cjs +++ b/frontend/appflowy_web_app/jest.config.cjs @@ -5,7 +5,7 @@ const esModules = ['lodash-es', 'nanoid'].join('|'); /** @type {import('ts-jest').JestConfigWithTsJest} */ module.exports = { preset: 'ts-jest', - testEnvironment: 'node', + testEnvironment: 'jsdom', roots: [''], modulePaths: [compilerOptions.baseUrl], moduleNameMapper: { @@ -14,10 +14,28 @@ module.exports = { '^nanoid(/(.*)|$)': 'nanoid$1', }, 'transform': { + '^.+\\.(j|t)sx?$': 'ts-jest', '(.*)/node_modules/nanoid/.+\\.(j|t)sx?$': 'ts-jest', }, 'transformIgnorePatterns': [`/node_modules/(?!${esModules})`], - testMatch: ['**/*.test.ts'], + testMatch: ['**/*.test.ts', '**/*.test.tsx'], coverageDirectory: '/coverage/jest', collectCoverage: true, + coverageProvider: 'v8', + coveragePathIgnorePatterns: [ + '/cypress/', + '/coverage/', + '/node_modules/', + '/__tests__/', + '/__mocks__/', + '/__fixtures__/', + '/__helpers__/', + '/__utils__/', + '/__constants__/', + '/__types__/', + '/__mocks__/', + '/__stubs__/', + '/__fixtures__/', + '/application/folder-yjs/', + ], }; \ No newline at end of file diff --git a/frontend/appflowy_web_app/package.json b/frontend/appflowy_web_app/package.json index 09dbe21254..f822a6b0f9 100644 --- a/frontend/appflowy_web_app/package.json +++ b/frontend/appflowy_web_app/package.json @@ -21,8 +21,7 @@ "test:components": "cypress run --component --browser chrome --headless", "test:unit": "jest --coverage", "test:cy": "cypress run", - "merge-coverage": "node scripts/merge-coverage.cjs", - "coverage": "pnpm run test:unit && pnpm run test:components && pnpm run merge-coverage" + "coverage": "pnpm run test:unit && pnpm run test:components" }, "dependencies": { "@appflowyinc/client-api-wasm": "0.0.3", @@ -99,11 +98,15 @@ "yjs": "^13.6.14" }, "devDependencies": { + "@babel/preset-env": "^7.24.7", + "@babel/preset-react": "^7.24.7", + "@babel/preset-typescript": "^7.24.7", "@cypress/code-coverage": "^3.12.39", "@istanbuljs/nyc-config-babel": "^3.0.0", "@istanbuljs/nyc-config-typescript": "^1.0.2", "@svgr/plugin-svgo": "^8.0.1", "@tauri-apps/cli": "^1.5.11", + "@testing-library/react": "^16.0.0", "@types/google-protobuf": "^3.15.12", "@types/is-hotkey": "^0.1.7", "@types/jest": "^29.5.3", diff --git a/frontend/appflowy_web_app/pnpm-lock.yaml b/frontend/appflowy_web_app/pnpm-lock.yaml index 37a34b9470..194beaa5dd 100644 --- a/frontend/appflowy_web_app/pnpm-lock.yaml +++ b/frontend/appflowy_web_app/pnpm-lock.yaml @@ -219,9 +219,18 @@ dependencies: version: 13.6.15 devDependencies: + '@babel/preset-env': + specifier: ^7.24.7 + version: 7.24.7(@babel/core@7.24.3) + '@babel/preset-react': + specifier: ^7.24.7 + version: 7.24.7(@babel/core@7.24.3) + '@babel/preset-typescript': + specifier: ^7.24.7 + version: 7.24.7(@babel/core@7.24.3) '@cypress/code-coverage': specifier: ^3.12.39 - version: 3.12.39(@babel/core@7.24.3)(@babel/preset-env@7.24.6)(babel-loader@9.1.3)(cypress@13.7.2)(webpack@5.91.0) + version: 3.12.39(@babel/core@7.24.3)(@babel/preset-env@7.24.7)(babel-loader@9.1.3)(cypress@13.7.2)(webpack@5.91.0) '@istanbuljs/nyc-config-babel': specifier: ^3.0.0 version: 3.0.0(@babel/register@7.24.6)(babel-plugin-istanbul@6.1.1) @@ -234,6 +243,9 @@ devDependencies: '@tauri-apps/cli': specifier: ^1.5.11 version: 1.5.11 + '@testing-library/react': + specifier: ^16.0.0 + version: 16.0.0(@testing-library/dom@10.1.0)(@types/react-dom@18.2.22)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0) '@types/google-protobuf': specifier: ^3.15.12 version: 3.15.12 @@ -491,7 +503,7 @@ packages: /@atlaskit/platform-feature-flags@0.2.5: resolution: {integrity: sha512-0fD2aDxn2mE59D4acUhVib+YF2HDYuuPH50aYwpQdcV/CsVkAaJsMKy8WhWSulcRFeMYp72kfIfdy0qGdRB7Uw==} dependencies: - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.6 dev: false /@atlaskit/primitives@5.7.0(@types/react@18.2.66)(react@18.2.0): @@ -552,11 +564,11 @@ packages: '@babel/highlight': 7.24.2 picocolors: 1.0.0 - /@babel/code-frame@7.24.6: - resolution: {integrity: sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==} + /@babel/code-frame@7.24.7: + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/highlight': 7.24.6 + '@babel/highlight': 7.24.7 picocolors: 1.0.0 dev: true @@ -564,8 +576,8 @@ packages: resolution: {integrity: sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==} engines: {node: '>=6.9.0'} - /@babel/compat-data@7.24.6: - resolution: {integrity: sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ==} + /@babel/compat-data@7.24.7: + resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} engines: {node: '>=6.9.0'} dev: true @@ -600,18 +612,31 @@ packages: '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 - /@babel/helper-annotate-as-pure@7.24.6: - resolution: {integrity: sha512-DitEzDfOMnd13kZnDqns1ccmftwJTS9DMkyn9pYTxulS7bZxUxpMly3Nf23QQ6NwA4UB8lAqjbqWtyvElEMAkg==} + /@babel/generator@7.24.7: + resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.6 + '@babel/types': 7.24.7 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 dev: true - /@babel/helper-builder-binary-assignment-operator-visitor@7.24.6: - resolution: {integrity: sha512-+wnfqc5uHiMYtvRX7qu80Toef8BXeh4HHR1SPeonGb1SKPniNEd4a/nlaJJMv/OIEYvIVavvo0yR7u10Gqz0Iw==} + /@babel/helper-annotate-as-pure@7.24.7: + resolution: {integrity: sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.6 + '@babel/types': 7.24.7 + dev: true + + /@babel/helper-builder-binary-assignment-operator-visitor@7.24.7: + resolution: {integrity: sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true /@babel/helper-compilation-targets@7.23.6: @@ -624,33 +649,35 @@ packages: lru-cache: 5.1.1 semver: 6.3.1 - /@babel/helper-compilation-targets@7.24.6: - resolution: {integrity: sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg==} + /@babel/helper-compilation-targets@7.24.7: + resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/compat-data': 7.24.6 - '@babel/helper-validator-option': 7.24.6 + '@babel/compat-data': 7.24.7 + '@babel/helper-validator-option': 7.24.7 browserslist: 4.23.0 lru-cache: 5.1.1 semver: 6.3.1 dev: true - /@babel/helper-create-class-features-plugin@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-djsosdPJVZE6Vsw3kk7IPRWethP94WHGOhQTc67SNXE0ZzMhHgALw8iGmYS0TD1bbMM0VDROy43od7/hN6WYcA==} + /@babel/helper-create-class-features-plugin@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-annotate-as-pure': 7.24.6 - '@babel/helper-environment-visitor': 7.24.6 - '@babel/helper-function-name': 7.24.6 - '@babel/helper-member-expression-to-functions': 7.24.6 - '@babel/helper-optimise-call-expression': 7.24.6 - '@babel/helper-replace-supers': 7.24.6(@babel/core@7.24.3) - '@babel/helper-skip-transparent-expression-wrappers': 7.24.6 - '@babel/helper-split-export-declaration': 7.24.6 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-member-expression-to-functions': 7.24.7 + '@babel/helper-optimise-call-expression': 7.24.7 + '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.3) + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 semver: 6.3.1 + transitivePeerDependencies: + - supports-color dev: true /@babel/helper-create-regexp-features-plugin@7.24.6(@babel/core@7.24.3): @@ -660,7 +687,19 @@ packages: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-annotate-as-pure': 7.24.6 + '@babel/helper-annotate-as-pure': 7.24.7 + regexpu-core: 5.3.2 + semver: 6.3.1 + dev: true + + /@babel/helper-create-regexp-features-plugin@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-annotate-as-pure': 7.24.7 regexpu-core: 5.3.2 semver: 6.3.1 dev: true @@ -671,8 +710,8 @@ packages: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-compilation-targets': 7.24.6 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 debug: 4.3.4(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.8 @@ -684,9 +723,11 @@ packages: resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} engines: {node: '>=6.9.0'} - /@babel/helper-environment-visitor@7.24.6: - resolution: {integrity: sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g==} + /@babel/helper-environment-visitor@7.24.7: + resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.7 dev: true /@babel/helper-function-name@7.23.0: @@ -696,12 +737,12 @@ packages: '@babel/template': 7.24.0 '@babel/types': 7.24.0 - /@babel/helper-function-name@7.24.6: - resolution: {integrity: sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w==} + /@babel/helper-function-name@7.24.7: + resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.24.6 - '@babel/types': 7.24.6 + '@babel/template': 7.24.7 + '@babel/types': 7.24.7 dev: true /@babel/helper-hoist-variables@7.22.5: @@ -710,18 +751,21 @@ packages: dependencies: '@babel/types': 7.24.0 - /@babel/helper-hoist-variables@7.24.6: - resolution: {integrity: sha512-SF/EMrC3OD7dSta1bLJIlrsVxwtd0UpjRJqLno6125epQMJ/kyFmpTT4pbvPbdQHzCHg+biQ7Syo8lnDtbR+uA==} + /@babel/helper-hoist-variables@7.24.7: + resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.6 + '@babel/types': 7.24.7 dev: true - /@babel/helper-member-expression-to-functions@7.24.6: - resolution: {integrity: sha512-OTsCufZTxDUsv2/eDXanw/mUZHWOxSbEmC3pP8cgjcy5rgeVPWWMStnv274DV60JtHxTk0adT0QrCzC4M9NWGg==} + /@babel/helper-member-expression-to-functions@7.24.7: + resolution: {integrity: sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.6 + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true /@babel/helper-module-imports@7.24.3: @@ -730,11 +774,14 @@ packages: dependencies: '@babel/types': 7.24.0 - /@babel/helper-module-imports@7.24.6: - resolution: {integrity: sha512-a26dmxFJBF62rRO9mmpgrfTLsAuyHk4e1hKTUkD/fcMfynt8gvEKwQPQDVxWhca8dHoDck+55DFt42zV0QMw5g==} + /@babel/helper-module-imports@7.24.7: + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.6 + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.3): @@ -750,58 +797,63 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/helper-validator-identifier': 7.22.20 - /@babel/helper-module-transforms@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-Y/YMPm83mV2HJTbX1Qh2sjgjqcacvOlhbzdCCsSlblOKjSYmQqEbO6rUniWQyRo9ncyfjT8hnUjlG06RXDEmcA==} + /@babel/helper-module-transforms@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-environment-visitor': 7.24.6 - '@babel/helper-module-imports': 7.24.6 - '@babel/helper-simple-access': 7.24.6 - '@babel/helper-split-export-declaration': 7.24.6 - '@babel/helper-validator-identifier': 7.24.6 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true - /@babel/helper-optimise-call-expression@7.24.6: - resolution: {integrity: sha512-3SFDJRbx7KuPRl8XDUr8O7GAEB8iGyWPjLKJh/ywP/Iy9WOmEfMrsWbaZpvBu2HSYn4KQygIsz0O7m8y10ncMA==} + /@babel/helper-optimise-call-expression@7.24.7: + resolution: {integrity: sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.6 + '@babel/types': 7.24.7 dev: true /@babel/helper-plugin-utils@7.24.0: resolution: {integrity: sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==} engines: {node: '>=6.9.0'} - /@babel/helper-plugin-utils@7.24.6: - resolution: {integrity: sha512-MZG/JcWfxybKwsA9N9PmtF2lOSFSEMVCpIRrbxccZFLJPrJciJdG/UhSh5W96GEteJI2ARqm5UAHxISwRDLSNg==} + /@babel/helper-plugin-utils@7.24.7: + resolution: {integrity: sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==} engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-remap-async-to-generator@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-1Qursq9ArRZPAMOZf/nuzVW8HgJLkTB9y9LfP4lW2MVp4e9WkLJDovfKBxoDcCk6VuzIxyqWHyBoaCtSRP10yg==} + /@babel/helper-remap-async-to-generator@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-annotate-as-pure': 7.24.6 - '@babel/helper-environment-visitor': 7.24.6 - '@babel/helper-wrap-function': 7.24.6 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-wrap-function': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true - /@babel/helper-replace-supers@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-mRhfPwDqDpba8o1F8ESxsEkJMQkUF8ZIWrAc0FtWhxnjfextxMWxr22RtFizxxSYLjVHDeMgVsRq8BBZR2ikJQ==} + /@babel/helper-replace-supers@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-environment-visitor': 7.24.6 - '@babel/helper-member-expression-to-functions': 7.24.6 - '@babel/helper-optimise-call-expression': 7.24.6 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-member-expression-to-functions': 7.24.7 + '@babel/helper-optimise-call-expression': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true /@babel/helper-simple-access@7.22.5: @@ -810,18 +862,24 @@ packages: dependencies: '@babel/types': 7.24.0 - /@babel/helper-simple-access@7.24.6: - resolution: {integrity: sha512-nZzcMMD4ZhmB35MOOzQuiGO5RzL6tJbsT37Zx8M5L/i9KSrukGXWTjLe1knIbb/RmxoJE9GON9soq0c0VEMM5g==} + /@babel/helper-simple-access@7.24.7: + resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.6 + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true - /@babel/helper-skip-transparent-expression-wrappers@7.24.6: - resolution: {integrity: sha512-jhbbkK3IUKc4T43WadP96a27oYti9gEf1LdyGSP2rHGH77kwLwfhO7TgwnWvxxQVmke0ImmCSS47vcuxEMGD3Q==} + /@babel/helper-skip-transparent-expression-wrappers@7.24.7: + resolution: {integrity: sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.6 + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true /@babel/helper-split-export-declaration@7.22.6: @@ -830,11 +888,11 @@ packages: dependencies: '@babel/types': 7.24.0 - /@babel/helper-split-export-declaration@7.24.6: - resolution: {integrity: sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==} + /@babel/helper-split-export-declaration@7.24.7: + resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.6 + '@babel/types': 7.24.7 dev: true /@babel/helper-string-parser@7.24.1: @@ -846,12 +904,17 @@ packages: engines: {node: '>=6.9.0'} dev: true + /@babel/helper-string-parser@7.24.7: + resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/helper-validator-identifier@7.22.20: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-identifier@7.24.6: - resolution: {integrity: sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==} + /@babel/helper-validator-identifier@7.24.7: + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} dev: true @@ -859,18 +922,21 @@ packages: resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-option@7.24.6: - resolution: {integrity: sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ==} + /@babel/helper-validator-option@7.24.7: + resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-wrap-function@7.24.6: - resolution: {integrity: sha512-f1JLrlw/jbiNfxvdrfBgio/gRBk3yTAEJWirpAkiJG2Hb22E7cEYKHWo0dFPTv/niPovzIdPdEDetrv6tC6gPQ==} + /@babel/helper-wrap-function@7.24.7: + resolution: {integrity: sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-function-name': 7.24.6 - '@babel/template': 7.24.6 - '@babel/types': 7.24.6 + '@babel/helper-function-name': 7.24.7 + '@babel/template': 7.24.7 + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true /@babel/helpers@7.24.1: @@ -892,11 +958,11 @@ packages: js-tokens: 4.0.0 picocolors: 1.0.0 - /@babel/highlight@7.24.6: - resolution: {integrity: sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==} + /@babel/highlight@7.24.7: + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-validator-identifier': 7.24.6 + '@babel/helper-validator-identifier': 7.24.7 chalk: 2.4.2 js-tokens: 4.0.0 picocolors: 1.0.0 @@ -909,56 +975,58 @@ packages: dependencies: '@babel/types': 7.24.0 - /@babel/parser@7.24.6: - resolution: {integrity: sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==} + /@babel/parser@7.24.7: + resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.24.6 + '@babel/types': 7.24.7 dev: true - /@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-bYndrJ6Ph6Ar+GaB5VAc0JPoP80bQCm4qon6JEzXfRl5QZyQ8Ur1K6k7htxWmPA5z+k7JQvaMUrtXlqclWYzKw==} + /@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-environment-visitor': 7.24.6 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-iVuhb6poq5ikqRq2XWU6OQ+R5o9wF+r/or9CeUyovgptz0UlnK4/seOQ1Istu/XybYjAhQv1FRSSfHHufIku5Q==} + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-c8TER5xMDYzzFcGqOEp9l4hvB7dcbhcGjcLVwxWfe4P5DOafdwjsBJZKsmv+o3aXh7NhopvayQIovHrh2zSRUQ==} + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.6 - '@babel/plugin-transform-optional-chaining': 7.24.6(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.3) + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-z8zEjYmwBUHN/pCF3NuWBhHQjJCrd33qAi8MgANfMrAvn72k2cImT8VjK9LJFu4ysOLJqhfkYYb3MvwANRUNZQ==} + /@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-environment-visitor': 7.24.6 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 dev: true /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.3): @@ -976,7 +1044,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.7 /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.3): resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} @@ -992,7 +1060,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.7 /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.3): resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} @@ -1001,7 +1069,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.3): @@ -1010,7 +1078,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.3): @@ -1019,27 +1087,27 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-syntax-import-assertions@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-BE6o2BogJKJImTmGpkmOic4V0hlRRxVtzqxiSPa8TIFxyhi4EFjHm08nq1M4STK4RytuLMgnSz0/wfflvGFNOg==} + /@babel/plugin-syntax-import-assertions@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-syntax-import-attributes@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-D+CfsVZousPXIdudSII7RGy52+dYRtbyKAZcvtQKq/NpsivyMVduepzcLqG5pMBugtMdedxdC8Ramdpcne9ZWQ==} + /@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.3): @@ -1067,13 +1135,23 @@ packages: '@babel/core': 7.24.3 '@babel/helper-plugin-utils': 7.24.0 + /@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-plugin-utils': 7.24.7 + dev: true + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.3): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.7 /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.3): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} @@ -1122,7 +1200,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.3): @@ -1143,6 +1221,16 @@ packages: '@babel/core': 7.24.3 '@babel/helper-plugin-utils': 7.24.0 + /@babel/plugin-syntax-typescript@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-plugin-utils': 7.24.7 + dev: true + /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.3): resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} engines: {node: '>=6.9.0'} @@ -1151,423 +1239,475 @@ packages: dependencies: '@babel/core': 7.24.3 '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-arrow-functions@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-jSSSDt4ZidNMggcLx8SaKsbGNEfIl0PHx/4mFEulorE7bpYLbN0d3pDW3eJ7Y5Z3yPhy3L3NaPCYyTUY7TuugQ==} + /@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-async-generator-functions@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-VEP2o4iR2DqQU6KPgizTW2mnMx6BG5b5O9iQdrW9HesLkv8GIA8x2daXBQxw1MrsIkFQGA/iJ204CKoQ8UcnAA==} + /@babel/plugin-transform-async-generator-functions@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-environment-visitor': 7.24.6 - '@babel/helper-plugin-utils': 7.24.6 - '@babel/helper-remap-async-to-generator': 7.24.6(@babel/core@7.24.3) + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.3) '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.3) + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-transform-async-to-generator@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-NTBA2SioI3OsHeIn6sQmhvXleSl9T70YY/hostQLveWs0ic+qvbA3fa0kwAwQ0OA/XGaAerNZRQGJyRfhbJK4g==} + /@babel/plugin-transform-async-to-generator@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-module-imports': 7.24.6 - '@babel/helper-plugin-utils': 7.24.6 - '@babel/helper-remap-async-to-generator': 7.24.6(@babel/core@7.24.3) + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.3) + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-transform-block-scoped-functions@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-XNW7jolYHW9CwORrZgA/97tL/k05qe/HL0z/qqJq1mdWhwwCM6D4BJBV7wAz9HgFziN5dTOG31znkVIzwxv+vw==} + /@babel/plugin-transform-block-scoped-functions@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-block-scoping@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-S/t1Xh4ehW7sGA7c1j/hiOBLnEYCp/c2sEG4ZkL8kI1xX9tW2pqJTCHKtdhe/jHKt8nG0pFCrDHUXd4DvjHS9w==} + /@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-class-properties@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-j6dZ0Z2Z2slWLR3kt9aOmSIrBvnntWjMDN/TVcMPxhXMLmJVqX605CBRlcGI4b32GMbfifTEsdEjGjiE+j/c3A==} + /@babel/plugin-transform-class-properties@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-transform-class-static-block@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-1QSRfoPI9RoLRa8Mnakc6v3e0gJxiZQTYrMfLn+mD0sz5+ndSzwymp2hDcYJTyT0MOn0yuWzj8phlIvO72gTHA==} + /@babel/plugin-transform-class-static-block@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.7 '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.3) + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-transform-classes@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-+fN+NO2gh8JtRmDSOB6gaCVo36ha8kfCW1nMq2Gc0DABln0VcHN4PrALDvF5/diLzIRKptC7z/d7Lp64zk92Fg==} + /@babel/plugin-transform-classes@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-annotate-as-pure': 7.24.6 - '@babel/helper-compilation-targets': 7.24.6 - '@babel/helper-environment-visitor': 7.24.6 - '@babel/helper-function-name': 7.24.6 - '@babel/helper-plugin-utils': 7.24.6 - '@babel/helper-replace-supers': 7.24.6(@babel/core@7.24.3) - '@babel/helper-split-export-declaration': 7.24.6 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.3) + '@babel/helper-split-export-declaration': 7.24.7 globals: 11.12.0 + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-transform-computed-properties@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-cRzPobcfRP0ZtuIEkA8QzghoUpSB3X3qSH5W2+FzG+VjWbJXExtx0nbRqwumdBN1x/ot2SlTNQLfBCnPdzp6kg==} + /@babel/plugin-transform-computed-properties@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 - '@babel/template': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/template': 7.24.7 dev: true - /@babel/plugin-transform-destructuring@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-YLW6AE5LQpk5npNXL7i/O+U9CE4XsBCuRPgyjl1EICZYKmcitV+ayuuUGMJm2lC1WWjXYszeTnIxF/dq/GhIZQ==} + /@babel/plugin-transform-destructuring@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-dotall-regex@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-rCXPnSEKvkm/EjzOtLoGvKseK+dS4kZwx1HexO3BtRtgL0fQ34awHn34aeSHuXtZY2F8a1X8xqBBPRtOxDVmcA==} + /@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-duplicate-keys@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-/8Odwp/aVkZwPFJMllSbawhDAO3UJi65foB00HYnK/uXvvCPm0TAXSByjz1mpRmp0q6oX2SIxpkUOpPFHk7FLA==} + /@babel/plugin-transform-duplicate-keys@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-dynamic-import@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-vpq8SSLRTBLOHUZHSnBqVo0AKX3PBaoPs2vVzYVWslXDTDIpwAcCDtfhUcHSQQoYoUvcFPTdC8TZYXu9ZnLT/w==} + /@babel/plugin-transform-dynamic-import@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.3) dev: true - /@babel/plugin-transform-exponentiation-operator@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-EemYpHtmz0lHE7hxxxYEuTYOOBZ43WkDgZ4arQ4r+VX9QHuNZC+WH3wUWmRNvR8ECpTRne29aZV6XO22qpOtdA==} + /@babel/plugin-transform-exponentiation-operator@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-builder-binary-assignment-operator-visitor': 7.24.6 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-transform-export-namespace-from@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-inXaTM1SVrIxCkIJ5gqWiozHfFMStuGbGJAxZFBoHcRRdDP0ySLb3jH6JOwmfiinPwyMZqMBX+7NBDCO4z0NSA==} + /@babel/plugin-transform-export-namespace-from@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.3) dev: true - /@babel/plugin-transform-for-of@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-n3Sf72TnqK4nw/jziSqEl1qaWPbCRw2CziHH+jdRYvw4J6yeCzsj4jdw8hIntOEeDGTmHVe2w4MVL44PN0GMzg==} + /@babel/plugin-transform-for-of@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-transform-function-name@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-sOajCu6V0P1KPljWHKiDq6ymgqB+vfo3isUS4McqW1DZtvSVU2v/wuMhmRmkg3sFoq6GMaUUf8W4WtoSLkOV/Q==} + /@babel/plugin-transform-function-name@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-compilation-targets': 7.24.6 - '@babel/helper-function-name': 7.24.6 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-json-strings@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-Uvgd9p2gUnzYJxVdBLcU0KurF8aVhkmVyMKW4MIY1/BByvs3EBpv45q01o7pRTVmTvtQq5zDlytP3dcUgm7v9w==} + /@babel/plugin-transform-json-strings@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.3) dev: true - /@babel/plugin-transform-literals@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-f2wHfR2HF6yMj+y+/y07+SLqnOSwRp8KYLpQKOzS58XLVlULhXbiYcygfXQxJlMbhII9+yXDwOUFLf60/TL5tw==} + /@babel/plugin-transform-literals@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-logical-assignment-operators@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-EKaWvnezBCMkRIHxMJSIIylzhqK09YpiJtDbr2wsXTwnO0TxyjMUkaw4RlFIZMIS0iDj0KyIg7H7XCguHu/YDA==} + /@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.3) dev: true - /@babel/plugin-transform-member-expression-literals@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-9g8iV146szUo5GWgXpRbq/GALTnY+WnNuRTuRHWWFfWGbP9ukRL0aO/jpu9dmOPikclkxnNsjY8/gsWl6bmZJQ==} + /@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-modules-amd@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-eAGogjZgcwqAxhyFgqghvoHRr+EYRQPFjUXrTYKBRb5qPnAVxOOglaxc4/byHqjvq/bqO2F3/CGwTHsgKJYHhQ==} + /@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-transform-modules-commonjs@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-JEV8l3MHdmmdb7S7Cmx6rbNEjRCgTQMZxllveHO0mx6uiclB0NflCawlQQ6+o5ZrwjUBYPzHm2XoK4wqGVUFuw==} + /@babel/plugin-transform-modules-commonjs@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.6 - '@babel/helper-simple-access': 7.24.6 + '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-transform-modules-systemjs@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-xg1Z0J5JVYxtpX954XqaaAT6NpAY6LtZXvYFCJmGFJWwtlz2EmJoR8LycFRGNE8dBKizGWkGQZGegtkV8y8s+w==} + /@babel/plugin-transform-modules-systemjs@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-hoist-variables': 7.24.6 - '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.6 - '@babel/helper-validator-identifier': 7.24.6 + '@babel/helper-hoist-variables': 7.24.7 + '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-transform-modules-umd@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-esRCC/KsSEUvrSjv5rFYnjZI6qv4R1e/iHQrqwbZIoRJqk7xCvEUiN7L1XrmW5QSmQe3n1XD88wbgDTWLbVSyg==} + /@babel/plugin-transform-modules-umd@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-transform-named-capturing-groups-regex@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-6DneiCiu91wm3YiNIGDWZsl6GfTTbspuj/toTEqLh9d4cx50UIzSdg+T96p8DuT7aJOBRhFyaE9ZvTHkXrXr6Q==} + /@babel/plugin-transform-named-capturing-groups-regex@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-new-target@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-f8liz9JG2Va8A4J5ZBuaSdwfPqN6axfWRK+y66fjKYbwf9VBLuq4WxtinhJhvp1w6lamKUwLG0slK2RxqFgvHA==} + /@babel/plugin-transform-new-target@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-nullish-coalescing-operator@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-+QlAiZBMsBK5NqrBWFXCYeXyiU1y7BQ/OYaiPAcQJMomn5Tyg+r5WuVtyEuvTbpV7L25ZSLfE+2E9ywj4FD48A==} + /@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.3) dev: true - /@babel/plugin-transform-numeric-separator@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-6voawq8T25Jvvnc4/rXcWZQKKxUNZcKMS8ZNrjxQqoRFernJJKjE3s18Qo6VFaatG5aiX5JV1oPD7DbJhn0a4Q==} + /@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.3) dev: true - /@babel/plugin-transform-object-rest-spread@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-OKmi5wiMoRW5Smttne7BwHM8s/fb5JFs+bVGNSeHWzwZkWXWValR1M30jyXo1s/RaqgwwhEC62u4rFH/FBcBPg==} + /@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-compilation-targets': 7.24.6 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-transform-parameters': 7.24.6(@babel/core@7.24.3) + '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.3) dev: true - /@babel/plugin-transform-object-super@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-N/C76ihFKlZgKfdkEYKtaRUtXZAgK7sOY4h2qrbVbVTXPrKGIi8aww5WGe/+Wmg8onn8sr2ut6FXlsbu/j6JHg==} + /@babel/plugin-transform-object-super@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 - '@babel/helper-replace-supers': 7.24.6(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.3) + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-transform-optional-catch-binding@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-L5pZ+b3O1mSzJ71HmxSCmTVd03VOT2GXOigug6vDYJzE5awLI7P1g0wFcdmGuwSDSrQ0L2rDOe/hHws8J1rv3w==} + /@babel/plugin-transform-optional-catch-binding@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.3) dev: true - /@babel/plugin-transform-optional-chaining@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-cHbqF6l1QP11OkYTYQ+hhVx1E017O5ZcSPXk9oODpqhcAD1htsWG2NpHrrhthEO2qZomLK0FXS+u7NfrkF5aOQ==} + /@babel/plugin-transform-optional-chaining@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.3) + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-transform-parameters@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-ST7guE8vLV+vI70wmAxuZpIKzVjvFX9Qs8bl5w6tN/6gOypPWUmMQL2p7LJz5E63vEGrDhAiYetniJFyBH1RkA==} + /@babel/plugin-transform-parameters@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-private-methods@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-T9LtDI0BgwXOzyXrvgLTT8DFjCC/XgWLjflczTLXyvxbnSR/gpv0hbmzlHE/kmh9nOvlygbamLKRo6Op4yB6aw==} + /@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-transform-private-property-in-object@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-Qu/ypFxCY5NkAnEhCF86Mvg3NSabKsh/TPpBVswEdkGl7+FbsYHy1ziRqJpwGH4thBdQHh8zx+z7vMYmcJ7iaQ==} + /@babel/plugin-transform-private-property-in-object@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-annotate-as-pure': 7.24.6 - '@babel/helper-create-class-features-plugin': 7.24.6(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.7 '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.3) + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-transform-property-literals@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-oARaglxhRsN18OYsnPTpb8TcKQWDYNsPNmTnx5++WOAsUJ0cSC/FZVlIJCKvPbU4yn/UXsS0551CFKJhN0CaMw==} + /@babel/plugin-transform-property-literals@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 + dev: true + + /@babel/plugin-transform-react-display-name@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-plugin-utils': 7.24.7 + dev: true + + /@babel/plugin-transform-react-jsx-development@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.3 + '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.3) + transitivePeerDependencies: + - supports-color dev: true /@babel/plugin-transform-react-jsx-self@7.24.1(@babel/core@7.24.3): @@ -1590,144 +1730,188 @@ packages: '@babel/helper-plugin-utils': 7.24.0 dev: true - /@babel/plugin-transform-regenerator@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-SMDxO95I8WXRtXhTAc8t/NFQUT7VYbIWwJCJgEli9ml4MhqUMh4S6hxgH6SmAC3eAQNWCDJFxcFeEt9w2sDdXg==} + /@babel/plugin-transform-react-jsx@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.24.3) + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-react-pure-annotations@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + dev: true + + /@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-plugin-utils': 7.24.7 regenerator-transform: 0.15.2 dev: true - /@babel/plugin-transform-reserved-words@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-DcrgFXRRlK64dGE0ZFBPD5egM2uM8mgfrvTMOSB2yKzOtjpGegVYkzh3s1zZg1bBck3nkXiaOamJUqK3Syk+4A==} + /@babel/plugin-transform-reserved-words@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-shorthand-properties@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-xnEUvHSMr9eOWS5Al2YPfc32ten7CXdH7Zwyyk7IqITg4nX61oHj+GxpNvl+y5JHjfN3KXE2IV55wAWowBYMVw==} + /@babel/plugin-transform-shorthand-properties@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-spread@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-h/2j7oIUDjS+ULsIrNZ6/TKG97FgmEk1PXryk/HQq6op4XUUUwif2f69fJrzK0wza2zjCS1xhXmouACaWV5uPA==} + /@babel/plugin-transform-spread@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-transform-sticky-regex@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-fN8OcTLfGmYv7FnDrsjodYBo1DhPL3Pze/9mIIE2MGCT1KgADYIOD7rEglpLHZj8PZlC/JFX5WcD+85FLAQusw==} + /@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-template-literals@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-BJbEqJIcKwrqUP+KfUIkxz3q8VzXe2R8Wv8TaNgO1cx+nNavxn/2+H8kp9tgFSOL6wYPPEgFvU6IKS4qoGqhmg==} + /@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-typeof-symbol@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-IshCXQ+G9JIFJI7bUpxTE/oA2lgVLAIK8q1KdJNoPXOpvRaNjMySGuvLfBw/Xi2/1lLo953uE8hyYSDW3TSYig==} + /@babel/plugin-transform-typeof-symbol@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-unicode-escapes@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-bKl3xxcPbkQQo5eX9LjjDpU2xYHeEeNQbOhj0iPvetSzA+Tu9q/o5lujF4Sek60CM6MgYvOS/DJuwGbiEYAnLw==} + /@babel/plugin-transform-typescript@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-iLD3UNkgx2n/HrjBesVbYX6j0yqn/sJktvbtKKgcaLIQ4bTTQ8obAypc1VpyHPD2y4Phh9zHOaAt8e/L14wCpw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.24.3) + transitivePeerDependencies: + - supports-color dev: true - /@babel/plugin-transform-unicode-property-regex@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-8EIgImzVUxy15cZiPii9GvLZwsy7Vxc+8meSlR3cXFmBIl5W5Tn9LGBf7CDKkHj4uVfNXCJB8RsVfnmY61iedA==} + /@babel/plugin-transform-unicode-escapes@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-unicode-regex@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-pssN6ExsvxaKU638qcWb81RrvvgZom3jDgU/r5xFZ7TONkZGFf4MhI2ltMb8OcQWhHyxgIavEU+hgqtbKOmsPA==} + /@babel/plugin-transform-unicode-property-regex@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-unicode-sets-regex@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-quiMsb28oXWIDK0gXLALOJRXLgICLiulqdZGOaPPd0vRT7fQp74NtdADAVu+D8s00C+0Xs0MxVP0VKF/sZEUgw==} + /@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.7 + dev: true + + /@babel/plugin-transform-unicode-sets-regex@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-create-regexp-features-plugin': 7.24.6(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/preset-env@7.24.6(@babel/core@7.24.3): - resolution: {integrity: sha512-CrxEAvN7VxfjOG8JNF2Y/eMqMJbZPZ185amwGUBp8D9USK90xQmv7dLdFSa+VbD7fdIqcy/Mfv7WtzG8+/qxKg==} + /@babel/preset-env@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.24.6 + '@babel/compat-data': 7.24.7 '@babel/core': 7.24.3 - '@babel/helper-compilation-targets': 7.24.6 - '@babel/helper-plugin-utils': 7.24.6 - '@babel/helper-validator-option': 7.24.6 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.6(@babel/core@7.24.3) + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-validator-option': 7.24.7 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.7(@babel/core@7.24.3) '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.3) '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.3) '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.3) '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.3) '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.3) '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-import-assertions': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-syntax-import-attributes': 7.24.6(@babel/core@7.24.3) + '@babel/plugin-syntax-import-assertions': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-syntax-import-attributes': 7.24.7(@babel/core@7.24.3) '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.3) '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.3) '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.3) @@ -1739,54 +1923,54 @@ packages: '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.3) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.3) '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.3) - '@babel/plugin-transform-arrow-functions': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-async-generator-functions': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-async-to-generator': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-block-scoped-functions': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-block-scoping': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-class-properties': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-class-static-block': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-classes': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-computed-properties': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-destructuring': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-dotall-regex': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-duplicate-keys': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-dynamic-import': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-exponentiation-operator': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-export-namespace-from': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-for-of': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-function-name': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-json-strings': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-literals': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-logical-assignment-operators': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-member-expression-literals': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-modules-amd': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-modules-commonjs': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-modules-systemjs': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-modules-umd': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-named-capturing-groups-regex': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-new-target': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-nullish-coalescing-operator': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-numeric-separator': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-object-rest-spread': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-object-super': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-optional-catch-binding': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-optional-chaining': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-parameters': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-private-methods': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-private-property-in-object': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-property-literals': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-regenerator': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-reserved-words': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-shorthand-properties': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-spread': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-sticky-regex': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-template-literals': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-typeof-symbol': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-unicode-escapes': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-unicode-property-regex': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-unicode-regex': 7.24.6(@babel/core@7.24.3) - '@babel/plugin-transform-unicode-sets-regex': 7.24.6(@babel/core@7.24.3) + '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-async-generator-functions': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-block-scoped-functions': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-class-properties': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-class-static-block': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-classes': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-destructuring': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-dotall-regex': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-duplicate-keys': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-dynamic-import': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-exponentiation-operator': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-export-namespace-from': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-for-of': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-json-strings': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-logical-assignment-operators': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-member-expression-literals': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-modules-amd': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-modules-systemjs': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-modules-umd': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-new-target': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-numeric-separator': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-object-rest-spread': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-object-super': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-optional-catch-binding': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-property-literals': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-regenerator': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-reserved-words': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-typeof-symbol': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-unicode-escapes': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-unicode-property-regex': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-unicode-sets-regex': 7.24.7(@babel/core@7.24.3) '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.3) babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.3) babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.3) @@ -1803,11 +1987,44 @@ packages: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 dependencies: '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.6 + '@babel/helper-plugin-utils': 7.24.7 '@babel/types': 7.24.6 esutils: 2.0.3 dev: true + /@babel/preset-react@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-validator-option': 7.24.7 + '@babel/plugin-transform-react-display-name': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-react-jsx-development': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-react-pure-annotations': 7.24.7(@babel/core@7.24.3) + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/preset-typescript@7.24.7(@babel/core@7.24.3): + resolution: {integrity: sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-validator-option': 7.24.7 + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.3) + '@babel/plugin-transform-typescript': 7.24.7(@babel/core@7.24.3) + transitivePeerDependencies: + - supports-color + dev: true + /@babel/register@7.24.6(@babel/core@7.24.3): resolution: {integrity: sha512-WSuFCc2wCqMeXkz/i3yfAAsxwWflEgbVkZzivgAmXl/MxrXeoYFZOOPllbC8R8WTF7u61wSRQtDVZ1879cdu6w==} engines: {node: '>=6.9.0'} @@ -1852,13 +2069,13 @@ packages: '@babel/parser': 7.24.1 '@babel/types': 7.24.0 - /@babel/template@7.24.6: - resolution: {integrity: sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw==} + /@babel/template@7.24.7: + resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.24.6 - '@babel/parser': 7.24.6 - '@babel/types': 7.24.6 + '@babel/code-frame': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 dev: true /@babel/traverse@7.24.1: @@ -1878,6 +2095,24 @@ packages: transitivePeerDependencies: - supports-color + /@babel/traverse@7.24.7: + resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-hoist-variables': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + debug: 4.3.4(supports-color@8.1.1) + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/types@7.24.0: resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} engines: {node: '>=6.9.0'} @@ -1891,7 +2126,16 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/helper-string-parser': 7.24.6 - '@babel/helper-validator-identifier': 7.24.6 + '@babel/helper-validator-identifier': 7.24.7 + to-fast-properties: 2.0.0 + dev: true + + /@babel/types@7.24.7: + resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 dev: true @@ -1921,7 +2165,7 @@ packages: '@jridgewell/trace-mapping': 0.3.9 dev: true - /@cypress/code-coverage@3.12.39(@babel/core@7.24.3)(@babel/preset-env@7.24.6)(babel-loader@9.1.3)(cypress@13.7.2)(webpack@5.91.0): + /@cypress/code-coverage@3.12.39(@babel/core@7.24.3)(@babel/preset-env@7.24.7)(babel-loader@9.1.3)(cypress@13.7.2)(webpack@5.91.0): resolution: {integrity: sha512-ja7I/GRmkSAW9e3O7pideWcNUEHao0WT6sRyXQEURoxkJUASJssJ7Kb/bd3eMYmkUCiD5CRFqWR5BGF4mWVaUw==} peerDependencies: '@babel/core': ^7.0.1 @@ -1931,8 +2175,8 @@ packages: webpack: ^4 || ^5 dependencies: '@babel/core': 7.24.3 - '@babel/preset-env': 7.24.6(@babel/core@7.24.3) - '@cypress/webpack-preprocessor': 6.0.1(@babel/core@7.24.3)(@babel/preset-env@7.24.6)(babel-loader@9.1.3)(webpack@5.91.0) + '@babel/preset-env': 7.24.7(@babel/core@7.24.3) + '@cypress/webpack-preprocessor': 6.0.1(@babel/core@7.24.3)(@babel/preset-env@7.24.7)(babel-loader@9.1.3)(webpack@5.91.0) babel-loader: 9.1.3(@babel/core@7.24.3)(webpack@5.91.0) chalk: 4.1.2 cypress: 13.7.2 @@ -1972,7 +2216,7 @@ packages: uuid: 8.3.2 dev: true - /@cypress/webpack-preprocessor@6.0.1(@babel/core@7.24.3)(@babel/preset-env@7.24.6)(babel-loader@9.1.3)(webpack@5.91.0): + /@cypress/webpack-preprocessor@6.0.1(@babel/core@7.24.3)(@babel/preset-env@7.24.7)(babel-loader@9.1.3)(webpack@5.91.0): resolution: {integrity: sha512-WVNeFVSnFKxE3WZNRIriduTgqJRpevaiJIPlfqYTTzfXRD7X1Pv4woDE+G4caPV9bJqVKmVFiwzrXMRNeJxpxA==} peerDependencies: '@babel/core': ^7.0.1 @@ -1981,7 +2225,7 @@ packages: webpack: ^4 || ^5 dependencies: '@babel/core': 7.24.3 - '@babel/preset-env': 7.24.6(@babel/core@7.24.3) + '@babel/preset-env': 7.24.7(@babel/core@7.24.3) babel-loader: 9.1.3(@babel/core@7.24.3)(webpack@5.91.0) bluebird: 3.7.1 debug: 4.3.4(supports-color@8.1.1) @@ -3689,6 +3933,43 @@ packages: '@tauri-apps/cli-win32-x64-msvc': 1.5.11 dev: true + /@testing-library/dom@10.1.0: + resolution: {integrity: sha512-wdsYKy5zupPyLCW2Je5DLHSxSfbIp6h80WoHOQc+RPtmPGA52O9x5MJEkv92Sjonpq+poOAtUKhh1kBGAXBrNA==} + engines: {node: '>=18'} + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/runtime': 7.24.6 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + pretty-format: 27.5.1 + dev: true + + /@testing-library/react@16.0.0(@testing-library/dom@10.1.0)(@types/react-dom@18.2.22)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-guuxUKRWQ+FgNX0h0NS0FIq3Q3uLtWVpBzcLOggmfMoUpgBnzBzvLLd4fbm6yS8ydJd94cIfY4yP9qUQjM2KwQ==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 + '@types/react-dom': ^18.0.0 + react: ^18.0.0 + react-dom: ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.6 + '@testing-library/dom': 10.1.0 + '@types/react': 18.2.66 + '@types/react-dom': 18.2.22 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + /@tootallnate/once@2.0.0: resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} @@ -3715,6 +3996,10 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true + /@types/aria-query@5.0.4: + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + dev: true + /@types/babel__core@7.20.5: resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} dependencies: @@ -4494,6 +4779,12 @@ packages: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true + /aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + dependencies: + dequal: 2.0.3 + dev: true + /array-buffer-byte-length@1.0.1: resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} engines: {node: '>= 0.4'} @@ -4717,7 +5008,7 @@ packages: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} dependencies: - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.6 cosmiconfig: 7.1.0 resolve: 1.22.8 dev: false @@ -4727,7 +5018,7 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/compat-data': 7.24.6 + '@babel/compat-data': 7.24.7 '@babel/core': 7.24.3 '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.3) semver: 6.3.1 @@ -5554,7 +5845,6 @@ packages: /dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - dev: false /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} @@ -5613,6 +5903,10 @@ packages: esutils: 2.0.3 dev: true + /dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + dev: true + /dom-css@2.1.0: resolution: {integrity: sha512-w9kU7FAbaSh3QKijL6n59ofAhkkmMJ31GclJIz/vyQdjogfyxcB6Zf8CZyibOERI5o0Hxz30VmJS7+7r5fEj2Q==} dependencies: @@ -7937,6 +8231,11 @@ packages: engines: {node: '>=12'} dev: false + /lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + dev: true + /magic-string@0.30.8: resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} engines: {node: '>=12'} @@ -8695,6 +8994,15 @@ packages: engines: {node: '>=6'} dev: true + /pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + dev: true + /pretty-format@29.7.0: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -8990,7 +9298,7 @@ packages: peerDependencies: react: ^16.3.0 dependencies: - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.6 prop-types: 15.8.1 react: 18.2.0 warning: 4.0.3 @@ -9038,7 +9346,6 @@ packages: /react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - dev: false /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} diff --git a/frontend/appflowy_web_app/scripts/merge-coverage.cjs b/frontend/appflowy_web_app/scripts/merge-coverage.cjs deleted file mode 100644 index f059048a0f..0000000000 --- a/frontend/appflowy_web_app/scripts/merge-coverage.cjs +++ /dev/null @@ -1,31 +0,0 @@ -const { execSync } = require('child_process'); -const fs = require('fs'); -const path = require('path'); -const jestCoverageFile = path.join(__dirname, '../coverage/jest/coverage-final.json'); -const cypressCoverageFile = path.join(__dirname, '../coverage/cypress/coverage-final.json'); -// const cypressComponentCoverageFile = path.join(__dirname, '../coverage/cypress-component/coverage-final.json'); -const nycOutputDir = path.join(__dirname, '../coverage/.nyc_output'); -// Ensure .nyc_output directory exists -if (!fs.existsSync(nycOutputDir)) { - fs.mkdirSync(nycOutputDir, { recursive: true }); -} -// Copy Jest coverage file -fs.copyFileSync(jestCoverageFile, path.join(nycOutputDir, 'jest-coverage.json')); -// Copy Cypress E2E coverage file -fs.copyFileSync(cypressCoverageFile, path.join(nycOutputDir, 'cypress-coverage.json')); -// Copy Cypress Component coverage file -// fs.copyFileSync(cypressComponentCoverageFile, path.join(nycOutputDir, 'cypress-component-coverage.json')); -// Merge coverage files -execSync('nyc merge ./coverage/.nyc_output ./coverage/merged/coverage-final.json', { stdio: 'inherit' }); -// Generate final merged report -execSync('nyc report --reporter=html --reporter=text-summary --report-dir=coverage/merged --temp-dir=coverage/.nyc_output', { stdio: 'inherit' }); -console.log(`Merged coverage report written to coverage/merged`); - -const GITHUB_STEP_SUMMARY = process.env.GITHUB_STEP_SUMMARY; - -if (GITHUB_STEP_SUMMARY) { - const coverageSummary = execSync('nyc report --reporter=html --reporter=text-summary --report-dir=coverage/merged --temp-dir=coverage/.nyc_output').toString(); - - fs.appendFileSync(GITHUB_STEP_SUMMARY, `### Coverage Report\n\`\`\`\n${coverageSummary}\n\`\`\`\n`); -} - diff --git a/frontend/appflowy_web_app/src/application/collab.type.ts b/frontend/appflowy_web_app/src/application/collab.type.ts index 4f61246aab..567be3b4ed 100644 --- a/frontend/appflowy_web_app/src/application/collab.type.ts +++ b/frontend/appflowy_web_app/src/application/collab.type.ts @@ -369,7 +369,19 @@ export interface YDocument extends Y.Map { } export interface YBlocks extends Y.Map { - get(key: BlockId): Y.Map; + get(key: BlockId): YBlock; +} + +export interface YBlock extends Y.Map { + get(key: YjsEditorKey.block_id | YjsEditorKey.block_parent): BlockId; + + get(key: YjsEditorKey.block_type): BlockType; + + get(key: YjsEditorKey.block_data): string; + + get(key: YjsEditorKey.block_children): ChildrenId; + + get(key: YjsEditorKey.block_external_id): ExternalId; } export interface YMeta extends Y.Map { diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/filter.test.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/filter.test.ts index 5b2ed3d37e..979105a982 100644 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/filter.test.ts +++ b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/filter.test.ts @@ -27,6 +27,7 @@ import { filterBy, } from '../filter'; import { expect } from '@jest/globals'; +import * as Y from 'yjs'; describe('Text filter check', () => { const text = 'Hello, world!'; @@ -540,6 +541,15 @@ describe('Database filterBy', () => { expect(result).toBe('1,2,3,4,5,6,7,8,9,10'); }); + it('should return all rows for empty rowMap', () => { + const { filters, fields } = withTestingData(); + const rowMap = new Y.Map() as Y.Map; + const result = filterBy(rows, filters, fields, rowMap) + .map((row) => row.id) + .join(','); + expect(result).toBe('1,2,3,4,5,6,7,8,9,10'); + }); + it('should return rows that match text filter', () => { const { filters, fields, rowMap } = withTestingData(); const filter = withRichTextFilter(); diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/fixtures/sorts.json b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/fixtures/sorts.json index e07c1647ce..11ae36cf60 100644 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/fixtures/sorts.json +++ b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/fixtures/sorts.json @@ -78,5 +78,25 @@ "field_id": "url_field", "condition": "desc", "id": "sort_desc_url_field" + }, + "sort_asc_created_at": { + "field_id": "created_at_field", + "condition": "asc", + "id": "sort_asc_created_at" + }, + "sort_desc_created_at": { + "field_id": "created_at_field", + "condition": "desc", + "id": "sort_desc_created_at" + }, + "sort_asc_updated_at": { + "field_id": "last_modified_field", + "condition": "asc", + "id": "sort_asc_updated_at" + }, + "sort_desc_updated_at": { + "field_id": "last_modified_field", + "condition": "desc", + "id": "sort_desc_updated_at" } } \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/group.test.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/group.test.ts index 518918138b..adbe80aaa3 100644 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/group.test.ts +++ b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/group.test.ts @@ -1,8 +1,17 @@ -import { Row } from '@/application/database-yjs'; +import { FieldType, Row } from '@/application/database-yjs'; import { withTestingData } from '@/application/database-yjs/__tests__/withTestingData'; import { withTestingRows } from '@/application/database-yjs/__tests__/withTestingRows'; import { expect } from '@jest/globals'; import { groupByField } from '../group'; +import * as Y from 'yjs'; +import { + YDatabaseField, + YDatabaseFieldTypeOption, + YjsDatabaseKey, + YjsEditorKey, + YMapFieldTypeOption, +} from '@/application/collab.type'; +import { YjsEditor } from '@/application/slate-yjs'; describe('Database group', () => { let rows: Row[]; @@ -95,4 +104,69 @@ describe('Database group', () => { ]); expect(result).toEqual(expectRes); }); + + it('should not group if no options', () => { + const { fields, rowMap } = withTestingData(); + const field = new Y.Map() as YDatabaseField; + const typeOption = new Y.Map() as YDatabaseFieldTypeOption; + const now = Date.now().toString(); + + field.set(YjsDatabaseKey.name, 'Single Select Field'); + field.set(YjsDatabaseKey.id, 'another_single_select_field'); + field.set(YjsDatabaseKey.type, String(FieldType.SingleSelect)); + field.set(YjsDatabaseKey.last_modified, now.valueOf()); + field.set(YjsDatabaseKey.type_option, typeOption); + fields.set('another_single_select_field', field); + expect(groupByField(rows, rowMap, field)).toBeUndefined(); + + const selectTypeOption = new Y.Map() as YMapFieldTypeOption; + + typeOption.set(String(FieldType.SingleSelect), selectTypeOption); + selectTypeOption.set(YjsDatabaseKey.content, JSON.stringify({ disable_color: false, options: [] })); + const expectRes = new Map([['another_single_select_field', rows]]); + expect(groupByField(rows, rowMap, field)).toEqual(expectRes); + }); + + it('should handle empty selected ids', () => { + const { fields, rowMap } = withTestingData(); + const cell = rowMap + .get('1') + ?.getMap(YjsEditorKey.data_section) + ?.get(YjsEditorKey.database_row) + ?.get(YjsDatabaseKey.cells) + ?.get('single_select_field'); + cell?.set(YjsDatabaseKey.data, null); + + const field = fields.get('single_select_field'); + const result = groupByField(rows, rowMap, field); + expect(result).toEqual( + new Map([ + ['single_select_field', [{ id: '1', height: 37 }]], + [ + '2', + [ + { id: '2', height: 37 }, + { id: '5', height: 37 }, + { id: '8', height: 37 }, + ], + ], + [ + '3', + [ + { id: '3', height: 37 }, + { id: '6', height: 37 }, + { id: '9', height: 37 }, + ], + ], + [ + '1', + [ + { id: '4', height: 37 }, + { id: '7', height: 37 }, + { id: '10', height: 37 }, + ], + ], + ]) + ); + }); }); diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/parse.test.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/parse.test.ts new file mode 100644 index 0000000000..190a4846a1 --- /dev/null +++ b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/parse.test.ts @@ -0,0 +1,72 @@ +import { parseYDatabaseCellToCell } from '@/application/database-yjs/cell.parse'; +import { expect } from '@jest/globals'; +import { withTestingCheckboxCell, withTestingDateCell } from '@/application/database-yjs/__tests__/withTestingCell'; +import * as Y from 'yjs'; +import { + FieldType, + parseSelectOptionTypeOptions, + parseRelationTypeOption, + parseNumberTypeOptions, +} from '@/application/database-yjs'; +import { YDatabaseField, YDatabaseFieldTypeOption, YjsDatabaseKey } from '@/application/collab.type'; +import { withNumberTestingField, withRelationTestingField } from '@/application/database-yjs/__tests__/withTestingField'; + +describe('parseYDatabaseCellToCell', () => { + it('should parse a DateTime cell', () => { + const doc = new Y.Doc(); + const cell = withTestingDateCell(); + doc.getMap('cells').set('date_field', cell); + const parsedCell = parseYDatabaseCellToCell(cell); + expect(parsedCell.data).not.toBe(undefined); + expect(parsedCell.createdAt).not.toBe(undefined); + expect(parsedCell.lastModified).not.toBe(undefined); + expect(parsedCell.fieldType).toBe(Number(FieldType.DateTime)); + }); + it('should parse a Checkbox cell', () => { + const doc = new Y.Doc(); + const cell = withTestingCheckboxCell(); + doc.getMap('cells').set('checkbox_field', cell); + const parsedCell = parseYDatabaseCellToCell(cell); + expect(parsedCell.data).toBe(true); + expect(parsedCell.createdAt).not.toBe(undefined); + expect(parsedCell.lastModified).not.toBe(undefined); + expect(parsedCell.fieldType).toBe(Number(FieldType.Checkbox)); + }); +}); + +describe('Select option field parse', () => { + it('should parse select option type options', () => { + const doc = new Y.Doc(); + const field = new Y.Map() as YDatabaseField; + const typeOption = new Y.Map() as YDatabaseFieldTypeOption; + const now = Date.now().toString(); + + field.set(YjsDatabaseKey.name, 'Single Select Field'); + field.set(YjsDatabaseKey.id, 'single_select_field'); + field.set(YjsDatabaseKey.type, String(FieldType.SingleSelect)); + field.set(YjsDatabaseKey.last_modified, now.valueOf()); + field.set(YjsDatabaseKey.type_option, typeOption); + doc.getMap('fields').set('single_select_field', field); + expect(parseSelectOptionTypeOptions(field)).toEqual(null); + }); +}); + +describe('number field parse', () => { + it('should parse number field', () => { + const doc = new Y.Doc(); + const field = withNumberTestingField(); + doc.getMap('fields').set('number_field', field); + expect(parseNumberTypeOptions(field)).toEqual({ + format: 0, + }); + }); +}); + +describe('relation field parse', () => { + it('should parse relation field', () => { + const doc = new Y.Doc(); + const field = withRelationTestingField(); + doc.getMap('fields').set('relation_field', field); + expect(parseRelationTypeOption(field)).toEqual(undefined); + }); +}); diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/selector.test.tsx b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/selector.test.tsx new file mode 100644 index 0000000000..23c8bc8221 --- /dev/null +++ b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/selector.test.tsx @@ -0,0 +1,283 @@ +import { renderHook } from '@testing-library/react'; +import { + useCalendarEventsSelector, + useCellSelector, + useFieldSelector, + useFieldsSelector, + useFilterSelector, + useFiltersSelector, + useGroup, + useGroupsSelector, + usePrimaryFieldId, + useRowDataSelector, + useRowDocMapSelector, + useRowMetaSelector, + useRowOrdersSelector, + useRowsByGroup, + useSortSelector, + useSortsSelector, +} from '../selector'; +import { useDatabaseViewId } from '../context'; +import { IdProvider } from '@/components/_shared/context-provider/IdProvider'; +import { DatabaseContextProvider } from '@/components/database/DatabaseContext'; +import { withTestingDatabase } from '@/application/database-yjs/__tests__/withTestingData'; +import { expect } from '@jest/globals'; +import { YDoc, YjsDatabaseKey, YjsEditorKey, YSharedRoot } from '@/application/collab.type'; +import * as Y from 'yjs'; +import { withNumberTestingField, withTestingFields } from '@/application/database-yjs/__tests__/withTestingField'; +import { withTestingRows } from '@/application/database-yjs/__tests__/withTestingRows'; + +const wrapperCreator = + (viewId: string, doc: YDoc, rowDocMap: Y.Map) => + ({ children }: { children: React.ReactNode }) => { + return ( + + + {children} + + + ); + }; + +describe('Database selector', () => { + let wrapper: ({ children }: { children: React.ReactNode }) => JSX.Element; + let rowDocMap: Y.Map; + let doc: YDoc; + + beforeEach(() => { + const data = withTestingDatabase('1'); + + doc = data.doc; + rowDocMap = data.rowDocMap; + wrapper = wrapperCreator('1', doc, rowDocMap); + }); + + it('should select a field', () => { + const { result } = renderHook(() => useFieldSelector('number_field'), { wrapper }); + + const tempDoc = new Y.Doc(); + const field = withNumberTestingField(); + + tempDoc.getMap().set('number_field', field); + + expect(result.current.field?.toJSON()).toEqual(field.toJSON()); + }); + + it('should select all fields', () => { + const { result } = renderHook(() => useFieldsSelector(), { wrapper }); + + expect(result.current.map((item) => item.fieldId)).toEqual(Array.from(withTestingFields().keys())); + }); + + it('should select all filters', () => { + const { result } = renderHook(() => useFiltersSelector(), { wrapper }); + + expect(result.current).toEqual(['filter_multi_select_field']); + }); + + it('should select a filter', () => { + const { result } = renderHook(() => useFilterSelector('filter_multi_select_field'), { wrapper }); + + expect(result.current).toEqual({ + content: '1,3', + condition: 2, + fieldId: 'multi_select_field', + id: 'filter_multi_select_field', + filterType: NaN, + optionIds: ['1', '3'], + }); + }); + + it('should select all sorts', () => { + const { result } = renderHook(() => useSortsSelector(), { wrapper }); + + expect(result.current).toEqual(['sort_asc_text_field']); + }); + + it('should select a sort', () => { + const { result } = renderHook(() => useSortSelector('sort_asc_text_field'), { wrapper }); + + expect(result.current).toEqual({ + fieldId: 'text_field', + id: 'sort_asc_text_field', + condition: 0, + }); + }); + + it('should select all groups', () => { + const { result } = renderHook(() => useGroupsSelector(), { wrapper }); + + expect(result.current).toEqual(['g:single_select_field']); + }); + + it('should select a group', () => { + const { result } = renderHook(() => useGroup('g:single_select_field'), { wrapper }); + + expect(result.current).toEqual({ + fieldId: 'single_select_field', + columns: [ + { + id: '1', + visible: true, + }, + { + id: 'single_select_field', + visible: true, + }, + ], + }); + }); + + it('should select rows by group', () => { + const { result } = renderHook(() => useRowsByGroup('g:single_select_field'), { wrapper }); + + const { fieldId, columns, notFound, groupResult } = result.current; + + expect(fieldId).toEqual('single_select_field'); + expect(columns).toEqual([ + { + id: '1', + visible: true, + }, + { + id: 'single_select_field', + visible: true, + }, + ]); + expect(notFound).toBeFalsy(); + + expect(groupResult).toEqual( + new Map([ + [ + '1', + [ + { id: '1', height: 37 }, + { id: '7', height: 37 }, + ], + ], + [ + '2', + [ + { id: '2', height: 37 }, + { id: '8', height: 37 }, + { id: '5', height: 37 }, + ], + ], + [ + '3', + [ + { id: '9', height: 37 }, + { id: '3', height: 37 }, + { id: '6', height: 37 }, + ], + ], + ]) + ); + }); + + it('should select all row orders', () => { + const { result } = renderHook(() => useRowOrdersSelector(), { wrapper }); + + expect(result.current?.map((item) => item.id).join(',')).toEqual('9,2,3,1,6,8,5,7'); + }); + + it('should select all row doc map', () => { + const { result } = renderHook(() => useRowDocMapSelector(), { wrapper }); + + expect(result.current.rows).toEqual(rowDocMap); + }); + + it('should select a row data', () => { + const rows = withTestingRows(); + const { result } = renderHook(() => useRowDataSelector(rows[0].id), { wrapper }); + + expect(result.current.row.toJSON()).toEqual( + rowDocMap.get(rows[0].id)?.getMap(YjsEditorKey.data_section)?.get(YjsEditorKey.database_row)?.toJSON() + ); + }); + + it('should select a cell', () => { + const rows = withTestingRows(); + const { result } = renderHook( + () => + useCellSelector({ + rowId: rows[0].id, + fieldId: 'number_field', + }), + { wrapper } + ); + + expect(result.current).toEqual({ + createdAt: NaN, + data: 123, + fieldType: 1, + lastModified: NaN, + }); + }); + + it('should select a primary field id', () => { + const { result } = renderHook(() => usePrimaryFieldId(), { wrapper }); + + expect(result.current).toEqual('text_field'); + }); + + it('should select a row meta', () => { + const rows = withTestingRows(); + const { result } = renderHook(() => useRowMetaSelector(rows[0].id), { wrapper }); + + expect(result.current?.documentId).not.toBeNull(); + }); + + it('should select all calendar events', () => { + const { result } = renderHook(() => useCalendarEventsSelector(), { wrapper }); + + expect(result.current.events.length).toEqual(8); + expect(result.current.emptyEvents.length).toEqual(0); + }); + + it('should select view id', () => { + const { result } = renderHook(() => useDatabaseViewId(), { wrapper }); + + expect(result.current).toEqual('1'); + }); + + it('should select all rows if filter is not found', () => { + const view = (doc.get(YjsEditorKey.data_section) as YSharedRoot) + .get(YjsEditorKey.database) + .get(YjsDatabaseKey.views) + .get('1'); + + view.set(YjsDatabaseKey.filters, new Y.Array()); + + const { result } = renderHook(() => useRowOrdersSelector(), { wrapper }); + + expect(result.current?.map((item) => item.id).join(',')).toEqual('9,2,3,4,1,6,10,8,5,7'); + }); + + it('should select original row orders if sorts is not found', () => { + const view = (doc.get(YjsEditorKey.data_section) as YSharedRoot) + .get(YjsEditorKey.database) + .get(YjsDatabaseKey.views) + .get('1'); + + view.set(YjsDatabaseKey.sorts, new Y.Array()); + + const { result } = renderHook(() => useRowOrdersSelector(), { wrapper }); + + expect(result.current?.map((item) => item.id).join(',')).toEqual('1,2,3,5,6,7,8,9'); + }); + + it('should select all rows if filters and sorts are not found', () => { + const view = (doc.get(YjsEditorKey.data_section) as YSharedRoot) + .get(YjsEditorKey.database) + .get(YjsDatabaseKey.views) + .get('1'); + + view.set(YjsDatabaseKey.filters, new Y.Array()); + view.set(YjsDatabaseKey.sorts, new Y.Array()); + + const { result } = renderHook(() => useRowOrdersSelector(), { wrapper }); + + expect(result.current?.map((item) => item.id).join(',')).toEqual('1,2,3,4,5,6,7,8,9,10'); + }); +}); diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/sort.test.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/sort.test.ts index 39ffa2abe5..e790b05fdd 100644 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/sort.test.ts +++ b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/sort.test.ts @@ -4,7 +4,9 @@ import { withTestingRows } from '@/application/database-yjs/__tests__/withTestin import { withCheckboxSort, withChecklistSort, + withCreatedAtSort, withDateTimeSort, + withLastModifiedSort, withMultiSelectOptionSort, withNumberSort, withRichTextSort, @@ -19,10 +21,12 @@ import { withSelectOptionTestingField, withURLTestingField, withChecklistTestingField, + withRelationTestingField, } from './withTestingField'; import { sortBy, parseCellDataForSort } from '../sort'; import * as Y from 'yjs'; import { expect } from '@jest/globals'; +import { YjsDatabaseKey, YjsEditorKey } from '@/application/collab.type'; describe('parseCellDataForSort', () => { it('should parse data correctly based on field type', () => { @@ -127,6 +131,17 @@ describe('parseCellDataForSort', () => { expect(result).toBe(0); }); + + it('should return empty string for Relation field', () => { + const doc = new Y.Doc(); + const field = withRelationTestingField(); + doc.getMap().set('field', field); + const data = ''; + + const result = parseCellDataForSort(field, data); + + expect(result).toBe(''); + }); }); describe('Database sortBy', () => { @@ -136,6 +151,53 @@ describe('Database sortBy', () => { rows = withTestingRows(); }); + it('should not sort rows if no sort is provided', () => { + const { sorts, fields, rowMap } = withTestingData(); + + const sortedRows = sortBy(rows, sorts, fields, rowMap) + .map((row) => row.id) + .join(','); + expect(sortedRows).toBe('1,2,3,4,5,6,7,8,9,10'); + }); + + it('should not sort rows if no rows are provided', () => { + const { sorts, fields } = withTestingData(); + const rowMap = new Y.Map() as Y.Map; + const sortedRows = sortBy(rows, sorts, fields, rowMap) + .map((row) => row.id) + .join(','); + expect(sortedRows).toBe('1,2,3,4,5,6,7,8,9,10'); + }); + + it('should return default data if rowMeta is not found', () => { + const { sorts, fields, rowMap } = withTestingData(); + const sort = withNumberSort(); + sorts.push([sort]); + rowMap.delete('1'); + + const sortedRows = sortBy(rows, sorts, fields, rowMap) + .map((row) => row.id) + .join(','); + expect(sortedRows).toBe('1,2,3,4,5,6,7,8,9,10'); + }); + + it('should return default data if cell is not found', () => { + const { sorts, fields, rowMap } = withTestingData(); + const sort = withNumberSort(); + sorts.push([sort]); + const rowDoc = rowMap.get('1'); + rowDoc + ?.getMap(YjsEditorKey.data_section) + .get(YjsEditorKey.database_row) + ?.get(YjsDatabaseKey.cells) + .delete('number_field'); + + const sortedRows = sortBy(rows, sorts, fields, rowMap) + .map((row) => row.id) + .join(','); + expect(sortedRows).toBe('1,2,3,4,5,6,7,8,9,10'); + }); + it('should sort by number field in ascending order', () => { const { sorts, fields, rowMap } = withTestingData(); const sort = withNumberSort(); @@ -311,4 +373,25 @@ describe('Database sortBy', () => { .join(','); expect(sortedRows).toBe('3,9,1,2,5,6,7,8,4,10'); }); + + it('should sort by CreatedAt field in ascending order', () => { + const { sorts, fields, rowMap } = withTestingData(); + const sort = withCreatedAtSort(); + sorts.push([sort]); + + const sortedRows = sortBy(rows, sorts, fields, rowMap) + .map((row) => row.id) + .join(','); + expect(sortedRows).toBe('1,2,3,4,5,6,7,8,9,10'); + }); + + it('should sort by LastEditedTime field', () => { + const { sorts, fields, rowMap } = withTestingData(); + const sort = withLastModifiedSort(); + sorts.push([sort]); + const sortedRows = sortBy(rows, sorts, fields, rowMap) + .map((row) => row.id) + .join(','); + expect(sortedRows).toBe('1,2,3,4,5,6,7,8,9,10'); + }); }); diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingCell.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingCell.ts new file mode 100644 index 0000000000..4021903b36 --- /dev/null +++ b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingCell.ts @@ -0,0 +1,43 @@ +import * as Y from 'yjs'; +import { YDatabaseCell, YjsDatabaseKey } from '@/application/collab.type'; +import { FieldType } from '@/application/database-yjs'; + +export function withTestingDateCell() { + const cell = new Y.Map() as YDatabaseCell; + + cell.set(YjsDatabaseKey.id, 'date_field'); + cell.set(YjsDatabaseKey.data, Date.now()); + cell.set(YjsDatabaseKey.field_type, Number(FieldType.DateTime)); + cell.set(YjsDatabaseKey.created_at, Date.now()); + cell.set(YjsDatabaseKey.last_modified, Date.now()); + cell.set(YjsDatabaseKey.end_timestamp, Date.now() + 1000); + cell.set(YjsDatabaseKey.include_time, true); + cell.set(YjsDatabaseKey.is_range, true); + cell.set(YjsDatabaseKey.reminder_id, 'reminderId'); + + return cell; +} + +export function withTestingCheckboxCell() { + const cell = new Y.Map() as YDatabaseCell; + + cell.set(YjsDatabaseKey.id, 'checkbox_field'); + cell.set(YjsDatabaseKey.data, 'Yes'); + cell.set(YjsDatabaseKey.field_type, Number(FieldType.Checkbox)); + cell.set(YjsDatabaseKey.created_at, Date.now()); + cell.set(YjsDatabaseKey.last_modified, Date.now()); + + return cell; +} + +export function withTestingSingleOptionCell() { + const cell = new Y.Map() as YDatabaseCell; + + cell.set(YjsDatabaseKey.id, 'single_select_field'); + cell.set(YjsDatabaseKey.data, 'optionId'); + cell.set(YjsDatabaseKey.field_type, Number(FieldType.SingleSelect)); + cell.set(YjsDatabaseKey.created_at, Date.now()); + cell.set(YjsDatabaseKey.last_modified, Date.now()); + + return cell; +} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingData.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingData.ts index 40633223be..3ff4a32b12 100644 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingData.ts +++ b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingData.ts @@ -1,7 +1,29 @@ -import { YDatabaseFields, YDatabaseFilters, YDatabaseSorts } from '@/application/collab.type'; +import { + YDatabase, + YDatabaseField, + YDatabaseFields, + YDatabaseFilters, + YDatabaseGroup, + YDatabaseGroupColumn, + YDatabaseGroupColumns, + YDatabaseLayoutSettings, + YDatabaseSorts, + YDatabaseView, + YDatabaseViews, + YDoc, + YjsDatabaseKey, + YjsEditorKey, +} from '@/application/collab.type'; import { withTestingFields } from '@/application/database-yjs/__tests__/withTestingField'; -import { withTestingRowDataMap } from '@/application/database-yjs/__tests__/withTestingRows'; +import { + withTestingRowData, + withTestingRowDataMap, + withTestingRows, +} from '@/application/database-yjs/__tests__/withTestingRows'; import * as Y from 'yjs'; +import { withMultiSelectOptionFilter } from '@/application/database-yjs/__tests__/withTestingFilters'; +import { withRichTextSort } from '@/application/database-yjs/__tests__/withTestingSorts'; +import { metaIdFromRowId, RowMetaKey } from '@/application/database-yjs'; export function withTestingData() { const doc = new Y.Doc(); @@ -27,5 +49,133 @@ export function withTestingData() { rowMap, sorts, filters, + doc, + }; +} + +export function withTestingDatabase(viewId: string) { + const doc = new Y.Doc(); + const sharedRoot = doc.getMap(YjsEditorKey.data_section); + const database = new Y.Map() as YDatabase; + + sharedRoot.set(YjsEditorKey.database, database); + + const fields = withTestingFields() as YDatabaseFields; + + database.set(YjsDatabaseKey.fields, fields); + database.set(YjsDatabaseKey.id, viewId); + + const metas = new Y.Map(); + + database.set(YjsDatabaseKey.metas, metas); + metas.set(YjsDatabaseKey.iid, viewId); + + const views = new Y.Map() as YDatabaseViews; + + database.set(YjsDatabaseKey.views, views); + + const view = new Y.Map() as YDatabaseView; + + views.set('1', view); + view.set(YjsDatabaseKey.id, viewId); + view.set(YjsDatabaseKey.layout, 0); + view.set(YjsDatabaseKey.name, 'View 1'); + view.set(YjsDatabaseKey.database_id, viewId); + + const layoutSetting = new Y.Map() as YDatabaseLayoutSettings; + + const calendarSetting = new Y.Map(); + + calendarSetting.set(YjsDatabaseKey.field_id, 'date_field'); + layoutSetting.set('2', calendarSetting); + + view.set(YjsDatabaseKey.layout_settings, layoutSetting); + + const filters = new Y.Array() as YDatabaseFilters; + const filter = withMultiSelectOptionFilter(); + + filters.push([filter]); + + const sorts = new Y.Array() as YDatabaseSorts; + const sort = withRichTextSort(); + + sorts.push([sort]); + + const groups = new Y.Array(); + const group = new Y.Map() as YDatabaseGroup; + + groups.push([group]); + group.set(YjsDatabaseKey.id, 'g:single_select_field'); + group.set(YjsDatabaseKey.field_id, 'single_select_field'); + group.set(YjsDatabaseKey.type, '3'); + group.set(YjsDatabaseKey.content, ''); + + const groupColumns = new Y.Array() as YDatabaseGroupColumns; + + group.set(YjsDatabaseKey.groups, groupColumns); + + const column1 = new Y.Map() as YDatabaseGroupColumn; + const column2 = new Y.Map() as YDatabaseGroupColumn; + + column1.set(YjsDatabaseKey.id, '1'); + column1.set(YjsDatabaseKey.visible, true); + column2.set(YjsDatabaseKey.id, 'single_select_field'); + column2.set(YjsDatabaseKey.visible, true); + + groupColumns.push([column1]); + groupColumns.push([column2]); + + view.set(YjsDatabaseKey.filters, filters); + view.set(YjsDatabaseKey.sorts, sorts); + view.set(YjsDatabaseKey.groups, groups); + + const fieldSettings = new Y.Map(); + const fieldOrder = new Y.Array(); + const rowOrders = new Y.Array(); + + Array.from(fields).forEach(([fieldId, field]) => { + const setting = new Y.Map(); + + if (fieldId === 'text_field') { + (field as YDatabaseField).set(YjsDatabaseKey.is_primary, true); + } + + fieldOrder.push([fieldId]); + fieldSettings.set(fieldId, setting); + setting.set(YjsDatabaseKey.visibility, 0); + }); + const rows = withTestingRows(); + + rows.forEach(({ id, height }) => { + const row = new Y.Map(); + + row.set(YjsDatabaseKey.id, id); + row.set(YjsDatabaseKey.height, height); + rowOrders.push([row]); + }); + + view.set(YjsDatabaseKey.field_settings, fieldSettings); + view.set(YjsDatabaseKey.field_orders, fieldOrder); + view.set(YjsDatabaseKey.row_orders, rowOrders); + + const rowMapDoc = new Y.Doc(); + + const rowMapFolder = rowMapDoc.getMap(); + + rows.forEach((row, index) => { + const rowDoc = new Y.Doc(); + const rowData = withTestingRowData(row.id, index); + const rowMeta = new Y.Map(); + const parser = metaIdFromRowId('281e76fb-712e-59e2-8370-678bf0788355'); + + rowMeta.set(parser(RowMetaKey.IconId), '😊'); + rowDoc.getMap(YjsEditorKey.data_section).set(YjsEditorKey.meta, rowMeta); + rowDoc.getMap(YjsEditorKey.data_section).set(YjsEditorKey.database_row, rowData); + rowMapFolder.set(row.id, rowDoc); + }); + + return { + rowDocMap: rowMapFolder as Y.Map, + doc: doc as YDoc, }; } diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingField.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingField.ts index 417a3b99a7..869acfe55e 100644 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingField.ts +++ b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingField.ts @@ -4,7 +4,8 @@ import { YjsDatabaseKey, YMapFieldTypeOption, } from '@/application/collab.type'; -import { FieldType, SelectOptionColor } from '@/application/database-yjs'; +import { FieldType } from '@/application/database-yjs'; +import { SelectOptionColor } from '@/application/database-yjs/fields/select-option'; import * as Y from 'yjs'; export function withTestingFields() { @@ -39,6 +40,14 @@ export function withTestingFields() { fields.set('checklist_field', checklistField); + const createdAtField = withCreatedAtTestingField(); + + fields.set('created_at_field', createdAtField); + + const lastModifiedField = withLastModifiedTestingField(); + + fields.set('last_modified_field', lastModifiedField); + return fields; } @@ -56,13 +65,31 @@ export function withRichTextTestingField() { export function withNumberTestingField() { const field = new Y.Map() as YDatabaseField; - - const now = Date.now().toString(); - + field.set(YjsDatabaseKey.name, 'Number Field'); field.set(YjsDatabaseKey.id, 'number_field'); field.set(YjsDatabaseKey.type, String(FieldType.Number)); + const typeOption = new Y.Map() as YDatabaseFieldTypeOption; + + const numberTypeOption = new Y.Map() as YMapFieldTypeOption; + + typeOption.set(String(FieldType.Number), numberTypeOption); + numberTypeOption.set(YjsDatabaseKey.format, '0'); + field.set(YjsDatabaseKey.type_option, typeOption); + + return field; +} + +export function withRelationTestingField() { + const field = new Y.Map() as YDatabaseField; + const typeOption = new Y.Map() as YDatabaseFieldTypeOption; + const now = Date.now().toString(); + + field.set(YjsDatabaseKey.name, 'Relation Field'); + field.set(YjsDatabaseKey.id, 'relation_field'); + field.set(YjsDatabaseKey.type, String(FieldType.Relation)); field.set(YjsDatabaseKey.last_modified, now.valueOf()); + field.set(YjsDatabaseKey.type_option, typeOption); return field; } @@ -151,3 +178,27 @@ export function withChecklistTestingField() { return field; } + +export function withCreatedAtTestingField() { + const field = new Y.Map() as YDatabaseField; + const now = Date.now().toString(); + + field.set(YjsDatabaseKey.name, 'Created At Field'); + field.set(YjsDatabaseKey.id, 'created_at_field'); + field.set(YjsDatabaseKey.type, String(FieldType.CreatedTime)); + field.set(YjsDatabaseKey.last_modified, now.valueOf()); + + return field; +} + +export function withLastModifiedTestingField() { + const field = new Y.Map() as YDatabaseField; + const now = Date.now().toString(); + + field.set(YjsDatabaseKey.name, 'Last Modified Field'); + field.set(YjsDatabaseKey.id, 'last_modified_field'); + field.set(YjsDatabaseKey.type, String(FieldType.LastEditedTime)); + field.set(YjsDatabaseKey.last_modified, now.valueOf()); + + return field; +} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingRows.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingRows.ts index fc4c8c4f4c..3ed75b409c 100644 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingRows.ts +++ b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingRows.ts @@ -39,6 +39,8 @@ export function withTestingRowData(id: string, index: number) { rowData.set(YjsDatabaseKey.id, id); rowData.set(YjsDatabaseKey.height, 37); + rowData.set(YjsDatabaseKey.last_modified, Date.now() + index * 1000); + rowData.set(YjsDatabaseKey.created_at, Date.now() + index * 1000); const cells = new Y.Map() as YDatabaseCells; diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingSorts.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingSorts.ts index f5c1fd5a16..d97c6f4f71 100644 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingSorts.ts +++ b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingSorts.ts @@ -89,3 +89,25 @@ export function withChecklistSort(isAscending: boolean = true) { return sort; } + +export function withCreatedAtSort(isAscending: boolean = true) { + const sort = new Y.Map() as YDatabaseSort; + const sortJSON = isAscending ? sortsJson.sort_asc_created_at : sortsJson.sort_desc_created_at; + + sort.set(YjsDatabaseKey.id, sortJSON.id); + sort.set(YjsDatabaseKey.field_id, sortJSON.field_id); + sort.set(YjsDatabaseKey.condition, sortJSON.condition === 'asc' ? '0' : '1'); + + return sort; +} + +export function withLastModifiedSort(isAscending: boolean = true) { + const sort = new Y.Map() as YDatabaseSort; + const sortJSON = isAscending ? sortsJson.sort_asc_updated_at : sortsJson.sort_desc_updated_at; + + sort.set(YjsDatabaseKey.id, sortJSON.id); + sort.set(YjsDatabaseKey.field_id, sortJSON.field_id); + sort.set(YjsDatabaseKey.condition, sortJSON.condition === 'asc' ? '0' : '1'); + + return sort; +} diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/cell.parse.ts b/frontend/appflowy_web_app/src/application/database-yjs/cell.parse.ts similarity index 100% rename from frontend/appflowy_web_app/src/components/database/components/cell/cell.parse.ts rename to frontend/appflowy_web_app/src/application/database-yjs/cell.parse.ts diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/cell.type.ts b/frontend/appflowy_web_app/src/application/database-yjs/cell.type.ts similarity index 99% rename from frontend/appflowy_web_app/src/components/database/components/cell/cell.type.ts rename to frontend/appflowy_web_app/src/application/database-yjs/cell.type.ts index 1c82465a84..9e4bf77737 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/cell.type.ts +++ b/frontend/appflowy_web_app/src/application/database-yjs/cell.type.ts @@ -1,5 +1,5 @@ import { FieldId, RowId } from '@/application/collab.type'; -import { DateFormat, TimeFormat } from '@/application/database-yjs'; +import { DateFormat, TimeFormat } from '@/application/database-yjs/index'; import { FieldType } from '@/application/database-yjs/database.type'; import React from 'react'; import { YArray } from 'yjs/dist/src/types/YArray'; diff --git a/frontend/appflowy_web_app/src/application/database-yjs/const.ts b/frontend/appflowy_web_app/src/application/database-yjs/const.ts index c67b9edec8..436f28ef91 100644 --- a/frontend/appflowy_web_app/src/application/database-yjs/const.ts +++ b/frontend/appflowy_web_app/src/application/database-yjs/const.ts @@ -18,7 +18,15 @@ export const getCellData = (rowId: string, fieldId: string, rowMetas: Y.Map { - const namespace = uuidParse(rowId); + let namespace: Uint8Array; + + try { + namespace = uuidParse(rowId); + } catch (e) { + namespace = uuidParse(generateUUID()); + } return (key: RowMetaKey) => uuidv5(key, namespace).toString(); }; + +export const generateUUID = () => uuidv5(Date.now().toString(), uuidv5.URL); diff --git a/frontend/appflowy_web_app/src/application/database-yjs/context.ts b/frontend/appflowy_web_app/src/application/database-yjs/context.ts index 4e0824762c..5d51001976 100644 --- a/frontend/appflowy_web_app/src/application/database-yjs/context.ts +++ b/frontend/appflowy_web_app/src/application/database-yjs/context.ts @@ -1,5 +1,4 @@ import { YDatabase, YDatabaseRow, YDoc, YjsDatabaseKey, YjsEditorKey } from '@/application/collab.type'; -import { Row } from '@/application/database-yjs/selector'; import { createContext, useContext } from 'react'; import * as Y from 'yjs'; @@ -72,17 +71,3 @@ export function useDatabaseFields() { return database.get(YjsDatabaseKey.fields); } - -export interface RowsState { - rowOrders: Row[]; -} - -export const RowsContext = createContext(null); - -export function useRowsContext() { - return useContext(RowsContext); -} - -export function useRows() { - return useRowsContext()?.rowOrders; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/date/utils.test.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/date/utils.test.ts new file mode 100644 index 0000000000..9d3821ba1c --- /dev/null +++ b/frontend/appflowy_web_app/src/application/database-yjs/fields/date/utils.test.ts @@ -0,0 +1,21 @@ +import { getTimeFormat, getDateFormat } from './utils'; +import { expect } from '@jest/globals'; +import { DateFormat, TimeFormat } from '@/application/database-yjs'; + +describe('DateFormat', () => { + it('should return time format', () => { + expect(getTimeFormat(TimeFormat.TwelveHour)).toEqual('h:mm A'); + expect(getTimeFormat(TimeFormat.TwentyFourHour)).toEqual('HH:mm'); + expect(getTimeFormat(56)).toEqual('HH:mm'); + }); + + it('should return date format', () => { + expect(getDateFormat(DateFormat.US)).toEqual('YYYY/MM/DD'); + expect(getDateFormat(DateFormat.ISO)).toEqual('YYYY-MM-DD'); + expect(getDateFormat(DateFormat.Friendly)).toEqual('MMM DD, YYYY'); + expect(getDateFormat(DateFormat.Local)).toEqual('MM/DD/YYYY'); + expect(getDateFormat(DateFormat.DayMonthYear)).toEqual('DD/MM/YYYY'); + + expect(getDateFormat(56)).toEqual('YYYY-MM-DD'); + }); +}); diff --git a/frontend/appflowy_web_app/src/application/database-yjs/filter.ts b/frontend/appflowy_web_app/src/application/database-yjs/filter.ts index 235897b6c5..0bf25e4ca8 100644 --- a/frontend/appflowy_web_app/src/application/database-yjs/filter.ts +++ b/frontend/appflowy_web_app/src/application/database-yjs/filter.ts @@ -78,6 +78,9 @@ function createPredicate(conditions: ((row: Row) => boolean)[]) { export function filterBy(rows: Row[], filters: YDatabaseFilters, fields: YDatabaseFields, rowMetas: Y.Map) { const filterArray = filters.toArray(); + + if (filterArray.length === 0 || rowMetas.size === 0 || fields.size === 0) return rows; + const conditions = filterArray.map((filter) => { return (row: { id: string }) => { const fieldId = filter.get(YjsDatabaseKey.field_id); @@ -142,12 +145,12 @@ export function textFilterCheck(data: string, content: string, condition: TextFi export function numberFilterCheck(data: string, content: string, condition: number) { if (isNaN(Number(data)) || isNaN(Number(content)) || data === '' || content === '') { - if (condition === NumberFilterCondition.NumberIsEmpty && data === '') { - return true; + if (condition === NumberFilterCondition.NumberIsEmpty) { + return data === ''; } - if (condition === NumberFilterCondition.NumberIsNotEmpty && data !== '') { - return true; + if (condition === NumberFilterCondition.NumberIsNotEmpty) { + return data !== ''; } return false; @@ -169,10 +172,6 @@ export function numberFilterCheck(data: string, content: string, condition: numb return decimal < filterDecimal; case NumberFilterCondition.LessThanOrEqualTo: return decimal <= filterDecimal; - case NumberFilterCondition.NumberIsEmpty: - return data === ''; - case NumberFilterCondition.NumberIsNotEmpty: - return data !== ''; default: return false; } @@ -228,14 +227,6 @@ export function selectOptionFilterCheck(data: string, content: string, condition case SelectOptionFilterCondition.OptionDoesNotContain: return some(filterOptionIds, (option) => !selectedOptionIds.includes(option)); - // Ensure selectedOptionIds is empty - case SelectOptionFilterCondition.OptionIsEmpty: - return selectedOptionIds.length === 0; - - // Ensure selectedOptionIds is not empty - case SelectOptionFilterCondition.OptionIsNotEmpty: - return selectedOptionIds.length !== 0; - // Default case, if no conditions match default: return false; diff --git a/frontend/appflowy_web_app/src/application/database-yjs/selector.ts b/frontend/appflowy_web_app/src/application/database-yjs/selector.ts index c7d1272c9d..49fceb885c 100644 --- a/frontend/appflowy_web_app/src/application/database-yjs/selector.ts +++ b/frontend/appflowy_web_app/src/application/database-yjs/selector.ts @@ -14,18 +14,17 @@ import { useDatabaseView, useIsDatabaseRowPage, useRowDocMap, - useRows, useViewId, } from '@/application/database-yjs/context'; import { filterBy, parseFilter } from '@/application/database-yjs/filter'; import { groupByField } from '@/application/database-yjs/group'; import { sortBy } from '@/application/database-yjs/sort'; import { useViewsIdSelector } from '@/application/folder-yjs'; -import { parseYDatabaseCellToCell } from '@/components/database/components/cell/cell.parse'; -import { DateTimeCell } from '@/components/database/components/cell/cell.type'; -import dayjs from 'dayjs'; +import { parseYDatabaseCellToCell } from '@/application/database-yjs/cell.parse'; +import { DateTimeCell } from '@/application/database-yjs/cell.type'; +import * as dayjs from 'dayjs'; import { throttle } from 'lodash-es'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import Y from 'yjs'; import { CalendarLayoutSetting, FieldType, FieldVisibility, Filter, RowMetaKey, SortCondition } from './database.type'; @@ -149,12 +148,6 @@ export function useFieldsSelector(visibilitys: FieldVisibility[] = defaultVisibl return columns; } -export function useRowsSelector() { - const rowOrders = useRows(); - - return useMemo(() => rowOrders ?? [], [rowOrders]); -} - export function useFieldSelector(fieldId: string) { const database = useDatabase(); const [field, setField] = useState(null); @@ -403,7 +396,7 @@ export function useRowsByGroup(groupId: string) { if (!fieldId || !rowOrders || !rows) return; const onConditionsChange = () => { - if (rows.size !== rowOrders?.length) return; + if (rows.size < rowOrders?.length) return; const newResult = new Map(); @@ -456,7 +449,7 @@ export function useRowOrdersSelector() { if (!originalRowOrders || !rows) return; - if (originalRowOrders.length !== rows.size && !isDatabaseRowPage) return; + if (originalRowOrders.length > rows.size && !isDatabaseRowPage) return; if (sorts?.length === 0 && filters?.length === 0) { setRowOrders(originalRowOrders); return; @@ -691,7 +684,7 @@ export function useCalendarLayoutSetting() { export function usePrimaryFieldId() { const database = useDatabase(); - const [primaryFieldId, setPrimaryFieldId] = React.useState(null); + const [primaryFieldId, setPrimaryFieldId] = useState(null); useEffect(() => { const fields = database?.get(YjsDatabaseKey.fields); diff --git a/frontend/appflowy_web_app/src/application/database-yjs/sort.ts b/frontend/appflowy_web_app/src/application/database-yjs/sort.ts index 43ba0408a5..cead275830 100644 --- a/frontend/appflowy_web_app/src/application/database-yjs/sort.ts +++ b/frontend/appflowy_web_app/src/application/database-yjs/sort.ts @@ -15,6 +15,8 @@ import * as Y from 'yjs'; export function sortBy(rows: Row[], sorts: YDatabaseSorts, fields: YDatabaseFields, rowMetas: Y.Map) { const sortArray = sorts.toArray(); + + if (sortArray.length === 0 || rowMetas.size === 0 || fields.size === 0) return rows; const iteratees = sortArray.map((sort) => { return (row: { id: string }) => { const fieldId = sort.get(YjsDatabaseKey.field_id); @@ -26,8 +28,7 @@ export function sortBy(rows: Row[], sorts: YDatabaseSorts, fields: YDatabaseFiel const defaultData = parseCellDataForSort(field, ''); - if (!rowMeta) return defaultData; - const meta = rowMeta.getMap(YjsEditorKey.data_section).get(YjsEditorKey.database_row) as YDatabaseRow; + const meta = rowMeta?.getMap(YjsEditorKey.data_section).get(YjsEditorKey.database_row) as YDatabaseRow; if (!meta) return defaultData; if (fieldType === FieldType.LastEditedTime) { @@ -69,9 +70,9 @@ export function parseCellDataForSort(field: YDatabaseField, data: string | boole return data === 'Yes'; case FieldType.SingleSelect: case FieldType.MultiSelect: - return parseSelectOptionCellData(field, typeof data === 'string' ? data : ''); + return parseSelectOptionCellData(field, data as string); case FieldType.Checklist: - return parseChecklistData(typeof data === 'string' ? data : '')?.percentage ?? 0; + return parseChecklistData(data as string)?.percentage ?? 0; case FieldType.DateTime: return Number(data); case FieldType.Relation: diff --git a/frontend/appflowy_web_app/src/application/slate-yjs/__tests__/applyRemoteEvents.ts b/frontend/appflowy_web_app/src/application/slate-yjs/__tests__/applyRemoteEvents.ts new file mode 100644 index 0000000000..62c24c12b8 --- /dev/null +++ b/frontend/appflowy_web_app/src/application/slate-yjs/__tests__/applyRemoteEvents.ts @@ -0,0 +1,49 @@ +import { CollabOrigin } from '@/application/collab.type'; +import { yDocToSlateContent } from '@/application/slate-yjs/utils/convert'; +import { generateId, insertBlock, withTestingYDoc, withTestingYjsEditor } from './withTestingYjsEditor'; +import { createEditor } from 'slate'; +import { expect } from '@jest/globals'; +import * as Y from 'yjs'; + +export async function runApplyRemoteEventsTest() { + const pageId = generateId(); + const remoteDoc = withTestingYDoc(pageId); + const remote = withTestingYjsEditor(createEditor(), remoteDoc); + + const localDoc = new Y.Doc(); + + Y.applyUpdateV2(localDoc, Y.encodeStateAsUpdateV2(remoteDoc)); + const editor = withTestingYjsEditor(createEditor(), localDoc); + + editor.connect(); + expect(editor.children).toEqual(remote.children); + + // update remote doc + const id = generateId(); + + const { applyDelta } = insertBlock({ + doc: remoteDoc, + blockObject: { + id, + ty: 'paragraph', + relation_id: id, + text_id: id, + data: JSON.stringify({ level: 1 }), + }, + }); + + applyDelta([{ insert: 'Hello ' }, { insert: 'World', attributes: { bold: true } }]); + + remote.children = yDocToSlateContent(remoteDoc)?.children ?? []; + + // apply remote changes to local doc + Y.transact( + localDoc, + () => { + Y.applyUpdateV2(localDoc, Y.encodeStateAsUpdateV2(remoteDoc)); + }, + CollabOrigin.Remote + ); + + expect(editor.children).toEqual(remote.children); +} diff --git a/frontend/appflowy_web_app/src/application/slate-yjs/__tests__/convert.test.ts b/frontend/appflowy_web_app/src/application/slate-yjs/__tests__/convert.test.ts new file mode 100644 index 0000000000..0e473517d8 --- /dev/null +++ b/frontend/appflowy_web_app/src/application/slate-yjs/__tests__/convert.test.ts @@ -0,0 +1,268 @@ +import { generateId, getTestingDocData, insertBlock, withTestingYDoc } from './withTestingYjsEditor'; +import { yDocToSlateContent, deltaInsertToSlateNode, yDataToSlateContent } from '@/application/slate-yjs/utils/convert'; +import { expect } from '@jest/globals'; +import * as Y from 'yjs'; + +describe('convert yjs data to slate content', () => { + it('should return undefined if root block is not exist', () => { + const doc = new Y.Doc(); + + expect(() => yDocToSlateContent(doc)).toThrowError(); + + const doc2 = withTestingYDoc('1'); + const { blocks, childrenMap, textMap, pageId } = getTestingDocData(doc2); + expect(yDataToSlateContent({ blocks, rootId: '2', childrenMap, textMap })).toBeUndefined(); + + blocks.delete(pageId); + + expect(yDataToSlateContent({ blocks, rootId: pageId, childrenMap, textMap })).toBeUndefined(); + }); + it('should match empty array', () => { + const doc = withTestingYDoc('1'); + const slateContent = yDocToSlateContent(doc)!; + + expect(slateContent).not.toBeUndefined(); + expect(slateContent.children).toMatchObject([]); + }); + it('should match single paragraph', () => { + const doc = withTestingYDoc('1'); + const id = generateId(); + + const { applyDelta } = insertBlock({ + doc, + blockObject: { + id, + ty: 'paragraph', + relation_id: id, + text_id: id, + data: JSON.stringify({ level: 1 }), + }, + }); + + applyDelta([{ insert: 'Hello ' }, { insert: 'World', attributes: { bold: true } }]); + const slateContent = yDocToSlateContent(doc)!; + + expect(slateContent).not.toBeUndefined(); + expect(slateContent.children).toEqual([ + { + blockId: id, + relationId: id, + type: 'paragraph', + data: { level: 1 }, + children: [ + { + textId: id, + type: 'text', + children: [{ text: 'Hello ' }, { text: 'World', bold: true }], + }, + ], + }, + ]); + }); + it('should match nesting paragraphs', () => { + const doc = withTestingYDoc('1'); + const id1 = generateId(); + const id2 = generateId(); + + const { applyDelta, appendChild } = insertBlock({ + doc, + blockObject: { + id: id1, + ty: 'paragraph', + relation_id: id1, + text_id: id1, + data: '', + }, + }); + + applyDelta([{ insert: 'Hello ' }, { insert: 'World', attributes: { bold: true } }]); + appendChild({ + id: id2, + ty: 'paragraph', + relation_id: id2, + text_id: id2, + data: '', + }).applyDelta([{ insert: 'I am nested' }]); + + const slateContent = yDocToSlateContent(doc)!; + + expect(slateContent).not.toBeUndefined(); + expect(slateContent.children).toEqual([ + { + blockId: id1, + relationId: id1, + type: 'paragraph', + data: {}, + children: [ + { + textId: id1, + type: 'text', + children: [{ text: 'Hello ' }, { text: 'World', bold: true }], + }, + { + blockId: id2, + relationId: id2, + type: 'paragraph', + data: {}, + children: [{ textId: id2, type: 'text', children: [{ text: 'I am nested' }] }], + }, + ], + }, + ]); + }); + it('should compatible with delta in data', () => { + const doc = withTestingYDoc('1'); + const id = generateId(); + + insertBlock({ + doc, + blockObject: { + id, + ty: 'paragraph', + relation_id: id, + text_id: id, + data: JSON.stringify({ + delta: [ + { insert: 'Hello ' }, + { insert: 'World', attributes: { bold: true } }, + { insert: ' ', attributes: { code: true } }, + ], + }), + }, + }); + + const slateContent = yDocToSlateContent(doc)!; + + expect(slateContent).not.toBeUndefined(); + expect(slateContent.children).toEqual([ + { + blockId: id, + relationId: id, + type: 'paragraph', + data: { + delta: [ + { insert: 'Hello ' }, + { insert: 'World', attributes: { bold: true } }, + { + insert: ' ', + attributes: { code: true }, + }, + ], + }, + children: [ + { + textId: id, + type: 'text', + children: [{ text: 'Hello ' }, { text: 'World', bold: true }, { text: ' ', code: true }], + }, + { + text: '', + }, + ], + }, + ]); + }); + it('should return undefined if data is invalid', () => { + const doc = withTestingYDoc('1'); + const id = generateId(); + + insertBlock({ + doc, + blockObject: { + id, + ty: 'paragraph', + relation_id: id, + text_id: id, + data: 'invalid', + }, + }); + + const slateContent = yDocToSlateContent(doc)!; + + expect(slateContent).not.toBeUndefined(); + expect(slateContent.children).toEqual([undefined]); + }); + it('should return a normalize node if the delta is not exist', () => { + const doc = withTestingYDoc('1'); + const id = generateId(); + + insertBlock({ + doc, + blockObject: { + id, + ty: 'paragraph', + relation_id: id, + text_id: id, + data: JSON.stringify({}), + }, + }); + + const slateContent = yDocToSlateContent(doc)!; + + expect(slateContent).not.toBeUndefined(); + expect(slateContent.children).toEqual([ + { + blockId: id, + relationId: id, + type: 'paragraph', + data: {}, + children: [{ text: '' }], + }, + ]); + }); +}); + +describe('test deltaInsertToSlateNode', () => { + it('should match text node', () => { + const node = deltaInsertToSlateNode({ insert: 'Hello' }); + + expect(node).toEqual({ text: 'Hello' }); + }); + + it('should match text node with attributes', () => { + const node = deltaInsertToSlateNode({ insert: 'Hello', attributes: { bold: true } }); + + expect(node).toEqual({ text: 'Hello', bold: true }); + }); + + it('should delete empty string attributes', () => { + const node = deltaInsertToSlateNode({ insert: 'Hello', attributes: { bold: false, font_color: '' } }); + + expect(node).toEqual({ text: 'Hello' }); + }); + + it('should generate formula inline node', () => { + const node = deltaInsertToSlateNode({ + insert: '$$', + attributes: { formula: 'world' }, + }); + + expect(node).toEqual([ + { + type: 'formula', + data: 'world', + children: [{ text: '$' }], + }, + { + type: 'formula', + data: 'world', + children: [{ text: '$' }], + }, + ]); + }); + + it('should generate mention inline node', () => { + const node = deltaInsertToSlateNode({ + insert: '@', + attributes: { mention: 'world' }, + }); + + expect(node).toEqual([ + { + type: 'mention', + data: 'world', + children: [{ text: '@' }], + }, + ]); + }); +}); diff --git a/frontend/appflowy_web_app/src/application/slate-yjs/utils/__tests__/convert.ts b/frontend/appflowy_web_app/src/application/slate-yjs/__tests__/convert.ts similarity index 65% rename from frontend/appflowy_web_app/src/application/slate-yjs/utils/__tests__/convert.ts rename to frontend/appflowy_web_app/src/application/slate-yjs/__tests__/convert.ts index ef0c26b054..6aa830b6b0 100644 --- a/frontend/appflowy_web_app/src/application/slate-yjs/utils/__tests__/convert.ts +++ b/frontend/appflowy_web_app/src/application/slate-yjs/__tests__/convert.ts @@ -1,5 +1,5 @@ import { withTestingYDoc, withTestingYjsEditor } from './withTestingYjsEditor'; -import { yDocToSlateContent } from '../convert'; +import { yDocToSlateContent } from '../utils/convert'; import { createEditor, Editor } from 'slate'; import { expect } from '@jest/globals'; import * as Y from 'yjs'; @@ -39,3 +39,34 @@ export async function runCollaborationTest() { expect(yjsEditor.children).toEqual(remote.children); expect(normalizedSlateDoc(doc)).toEqual(yjsEditor.children); } + +export function runLocalChangeTest() { + const doc = withTestingYDoc('1'); + const editor = withTestingYjsEditor(createEditor(), doc); + + editor.connect(); + + editor.insertNode( + { + type: 'paragraph', + blockId: '1', + children: [ + { + textId: '1', + type: 'text', + children: [{ text: 'Hello' }], + }, + ], + }, + { + at: [0], + } + ); + + editor.apply({ + type: 'set_selection', + properties: {}, + newProperties: { anchor: { path: [0, 0], offset: 5 }, focus: { path: [0, 0], offset: 5 } }, + }); + // expect(editor.children).toEqual(yDocToSlateContent(doc)?.children); +} diff --git a/frontend/appflowy_web_app/src/application/slate-yjs/__tests__/index.test.ts b/frontend/appflowy_web_app/src/application/slate-yjs/__tests__/index.test.ts new file mode 100644 index 0000000000..f261b2244f --- /dev/null +++ b/frontend/appflowy_web_app/src/application/slate-yjs/__tests__/index.test.ts @@ -0,0 +1,67 @@ +import { runCollaborationTest, runLocalChangeTest } from './convert'; +import { runApplyRemoteEventsTest } from './applyRemoteEvents'; +import { + getTestingDocData, + withTestingYDoc, + withTestingYjsEditor, +} from '@/application/slate-yjs/__tests__/withTestingYjsEditor'; +import { createEditor } from 'slate'; +import Y from 'yjs'; +import { expect } from '@jest/globals'; +import { YjsEditor } from '@/application/slate-yjs'; + +describe('slate-yjs adapter', () => { + it('should pass the collaboration test', async () => { + await runCollaborationTest(); + }); + + it('should pass the apply remote events test', async () => { + await runApplyRemoteEventsTest(); + }); + + it('should store local changes', () => { + runLocalChangeTest(); + }); + + it('should throw error when already connected', () => { + const doc = withTestingYDoc('1'); + const editor = withTestingYjsEditor(createEditor(), doc); + editor.connect(); + expect(() => editor.connect()).toThrowError(); + }); + + it('should re connect after disconnect', () => { + const doc = withTestingYDoc('1'); + const editor = withTestingYjsEditor(createEditor(), doc); + editor.connect(); + editor.disconnect(); + expect(() => editor.connect()).not.toThrowError(); + }); + + it('should ensure the editor is connected before disconnecting', () => { + const doc = withTestingYDoc('1'); + const editor = withTestingYjsEditor(createEditor(), doc); + expect(() => editor.disconnect()).toThrowError(); + }); + + it('should have been called', () => { + const doc = withTestingYDoc('1'); + const editor = withTestingYjsEditor(createEditor(), doc); + editor.connect = jest.fn(); + YjsEditor.connect(editor); + expect(editor.connect).toHaveBeenCalled(); + + editor.disconnect = jest.fn(); + YjsEditor.disconnect(editor); + expect(editor.disconnect).toHaveBeenCalled(); + }); + + it('should can not be converted to slate content', () => { + const doc = withTestingYDoc('1'); + const { blocks, childrenMap, textMap, pageId } = getTestingDocData(doc); + blocks.delete(pageId); + const editor = withTestingYjsEditor(createEditor(), doc); + YjsEditor.connect(editor); + expect(editor.children).toEqual([]); + }); +}); diff --git a/frontend/appflowy_web_app/src/application/slate-yjs/__tests__/withTestingYjsEditor.ts b/frontend/appflowy_web_app/src/application/slate-yjs/__tests__/withTestingYjsEditor.ts new file mode 100644 index 0000000000..81ffce4f9e --- /dev/null +++ b/frontend/appflowy_web_app/src/application/slate-yjs/__tests__/withTestingYjsEditor.ts @@ -0,0 +1,135 @@ +import { + CollabOrigin, + YBlocks, + YChildrenMap, + YjsEditorKey, + YMeta, + YSharedRoot, + YTextMap, +} from '@/application/collab.type'; +import { withYjs } from '@/application/slate-yjs'; +import { YDelta } from '@/application/slate-yjs/utils/convert'; +import { Editor } from 'slate'; +import * as Y from 'yjs'; +import { v4 as uuidv4 } from 'uuid'; + +export function generateId() { + return uuidv4(); +} + +export function withTestingYjsEditor(editor: Editor, doc: Y.Doc) { + const yjdEditor = withYjs(editor, doc, { + localOrigin: CollabOrigin.LocalSync, + }); + + return yjdEditor; +} + +export function getTestingDocData(doc: Y.Doc) { + const sharedRoot = doc.getMap(YjsEditorKey.data_section) as YSharedRoot; + const document = sharedRoot.get(YjsEditorKey.document); + const blocks = document.get(YjsEditorKey.blocks) as YBlocks; + const meta = document.get(YjsEditorKey.meta) as YMeta; + const childrenMap = meta.get(YjsEditorKey.children_map) as YChildrenMap; + const textMap = meta.get(YjsEditorKey.text_map) as YTextMap; + const pageId = document.get(YjsEditorKey.page_id) as string; + + return { + sharedRoot, + document, + blocks, + meta, + childrenMap, + textMap, + pageId, + }; +} + +export function withTestingYDoc(docId: string) { + const doc = new Y.Doc(); + const sharedRoot = doc.getMap(YjsEditorKey.data_section) as YSharedRoot; + const document = new Y.Map(); + const blocks = new Y.Map(); + const meta = new Y.Map(); + const children_map = new Y.Map(); + const text_map = new Y.Map(); + const rootBlock = new Y.Map(); + const blockOrders = new Y.Array(); + const pageId = docId; + + sharedRoot.set(YjsEditorKey.document, document); + document.set(YjsEditorKey.page_id, pageId); + document.set(YjsEditorKey.blocks, blocks); + document.set(YjsEditorKey.meta, meta); + meta.set(YjsEditorKey.children_map, children_map); + meta.set(YjsEditorKey.text_map, text_map); + children_map.set(pageId, blockOrders); + blocks.set(pageId, rootBlock); + rootBlock.set(YjsEditorKey.block_id, pageId); + rootBlock.set(YjsEditorKey.block_children, pageId); + rootBlock.set(YjsEditorKey.block_type, 'page'); + rootBlock.set(YjsEditorKey.block_data, '{}'); + rootBlock.set(YjsEditorKey.block_external_id, ''); + return doc; +} + +export interface BlockObject { + id: string; + ty: string; + relation_id: string; + text_id: string; + data: string; +} + +export function insertBlock({ + doc, + parentBlockId, + prevBlockId, + blockObject, +}: { + doc: Y.Doc; + parentBlockId?: string; + prevBlockId?: string; + blockObject: BlockObject; +}) { + const { blocks, childrenMap, textMap, pageId } = getTestingDocData(doc); + const block = new Y.Map(); + const { id, ty, relation_id, text_id, data } = blockObject; + + block.set(YjsEditorKey.block_id, id); + block.set(YjsEditorKey.block_type, ty); + block.set(YjsEditorKey.block_children, relation_id); + block.set(YjsEditorKey.block_external_id, text_id); + block.set(YjsEditorKey.block_data, data); + blocks.set(id, block); + + const blockParentId = parentBlockId || pageId; + const blockParentChildren = childrenMap.get(blockParentId); + const index = prevBlockId ? blockParentChildren.toArray().indexOf(prevBlockId) + 1 : 0; + + blockParentChildren.insert(index, [id]); + + return { + applyDelta: (delta: YDelta[]) => { + let text = textMap.get(text_id); + + if (!text) { + text = new Y.Text(); + textMap.set(text_id, text); + } + + text.applyDelta(delta); + }, + appendChild: (childBlock: BlockObject) => { + if (!childrenMap.has(relation_id)) { + childrenMap.set(relation_id, new Y.Array()); + } + + return insertBlock({ + doc, + parentBlockId: id, + blockObject: childBlock, + }); + }, + }; +} diff --git a/frontend/appflowy_web_app/src/application/slate-yjs/utils/__tests__/applyRemoteEvents.ts b/frontend/appflowy_web_app/src/application/slate-yjs/utils/__tests__/applyRemoteEvents.ts deleted file mode 100644 index 7348ce0029..0000000000 --- a/frontend/appflowy_web_app/src/application/slate-yjs/utils/__tests__/applyRemoteEvents.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { - CollabOrigin, - YBlocks, - YChildrenMap, - YjsEditorKey, - YMeta, - YSharedRoot, - YTextMap, -} from '@/application/collab.type'; -import { yDocToSlateContent } from '@/application/slate-yjs/utils/convert'; -import { generateId, withTestingYDoc, withTestingYjsEditor } from './withTestingYjsEditor'; -import { createEditor } from 'slate'; -import { expect } from '@jest/globals'; -import * as Y from 'yjs'; - -export async function runApplyRemoteEventsTest() { - const pageId = generateId(); - const remoteDoc = withTestingYDoc(pageId); - const remote = withTestingYjsEditor(createEditor(), remoteDoc); - - const localDoc = new Y.Doc(); - - Y.applyUpdateV2(localDoc, Y.encodeStateAsUpdateV2(remoteDoc)); - const editor = withTestingYjsEditor(createEditor(), localDoc); - - editor.connect(); - expect(editor.children).toEqual(remote.children); - - // update remote doc - insertBlock(remoteDoc, generateId(), pageId, 0); - remote.children = yDocToSlateContent(remoteDoc)?.children ?? []; - - // apply remote changes to local doc - Y.transact( - localDoc, - () => { - Y.applyUpdateV2(localDoc, Y.encodeStateAsUpdateV2(remoteDoc)); - }, - CollabOrigin.Remote - ); - - expect(editor.children).toEqual(remote.children); -} - -function insertBlock(doc: Y.Doc, blockId: string, parentId: string, index: number) { - const sharedRoot = doc.getMap(YjsEditorKey.data_section) as YSharedRoot; - const document = sharedRoot.get(YjsEditorKey.document); - const blocks = document.get(YjsEditorKey.blocks) as YBlocks; - const meta = document.get(YjsEditorKey.meta) as YMeta; - const childrenMap = meta.get(YjsEditorKey.children_map) as YChildrenMap; - const textMap = meta.get(YjsEditorKey.text_map) as YTextMap; - - const block = new Y.Map(); - - block.set(YjsEditorKey.block_id, blockId); - block.set(YjsEditorKey.block_children, blockId); - block.set(YjsEditorKey.block_type, 'paragraph'); - block.set(YjsEditorKey.block_data, '{}'); - block.set(YjsEditorKey.block_external_id, blockId); - blocks.set(blockId, block); - childrenMap.set(blockId, new Y.Array()); - childrenMap.get(parentId).insert(index, [blockId]); - const text = new Y.Text(); - - text.insert(0, 'Hello, World!'); - textMap.set(blockId, text); -} diff --git a/frontend/appflowy_web_app/src/application/slate-yjs/utils/__tests__/index.test.ts b/frontend/appflowy_web_app/src/application/slate-yjs/utils/__tests__/index.test.ts deleted file mode 100644 index bcbd87176f..0000000000 --- a/frontend/appflowy_web_app/src/application/slate-yjs/utils/__tests__/index.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { runCollaborationTest } from './convert'; -import { runApplyRemoteEventsTest } from './applyRemoteEvents'; - -describe('slate-yjs adapter', () => { - it('should pass the collaboration test', async () => { - await runCollaborationTest(); - }); - - it('should pass the apply remote events test', async () => { - await runApplyRemoteEventsTest(); - }); -}); diff --git a/frontend/appflowy_web_app/src/application/slate-yjs/utils/__tests__/withTestingYjsEditor.ts b/frontend/appflowy_web_app/src/application/slate-yjs/utils/__tests__/withTestingYjsEditor.ts deleted file mode 100644 index 9d6922ad62..0000000000 --- a/frontend/appflowy_web_app/src/application/slate-yjs/utils/__tests__/withTestingYjsEditor.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { CollabOrigin, YjsEditorKey, YSharedRoot } from '@/application/collab.type'; -import { withYjs } from '@/application/slate-yjs'; -import { Editor } from 'slate'; -import * as Y from 'yjs'; -import { v4 as uuidv4 } from 'uuid'; - -export function generateId() { - return uuidv4(); -} - -export function withTestingYjsEditor(editor: Editor, doc: Y.Doc) { - const yjdEditor = withYjs(editor, doc, { - localOrigin: CollabOrigin.LocalSync, - }); - - return yjdEditor; -} - -export function withTestingYDoc(docId: string) { - const doc = new Y.Doc(); - const sharedRoot = doc.getMap(YjsEditorKey.data_section) as YSharedRoot; - const document = new Y.Map(); - const blocks = new Y.Map(); - const meta = new Y.Map(); - const children_map = new Y.Map(); - const text_map = new Y.Map(); - const rootBlock = new Y.Map(); - const blockOrders = new Y.Array(); - const pageId = docId; - - sharedRoot.set(YjsEditorKey.document, document); - document.set(YjsEditorKey.page_id, pageId); - document.set(YjsEditorKey.blocks, blocks); - document.set(YjsEditorKey.meta, meta); - meta.set(YjsEditorKey.children_map, children_map); - meta.set(YjsEditorKey.text_map, text_map); - children_map.set(pageId, blockOrders); - blocks.set(pageId, rootBlock); - rootBlock.set(YjsEditorKey.block_id, pageId); - rootBlock.set(YjsEditorKey.block_children, pageId); - rootBlock.set(YjsEditorKey.block_type, 'page'); - rootBlock.set(YjsEditorKey.block_data, '{}'); - rootBlock.set(YjsEditorKey.block_external_id, ''); - return doc; -} diff --git a/frontend/appflowy_web_app/src/application/slate-yjs/utils/convert.ts b/frontend/appflowy_web_app/src/application/slate-yjs/utils/convert.ts index 0cc5f372e9..67defd6acc 100644 --- a/frontend/appflowy_web_app/src/application/slate-yjs/utils/convert.ts +++ b/frontend/appflowy_web_app/src/application/slate-yjs/utils/convert.ts @@ -11,21 +11,19 @@ import { BlockType, } from '@/application/collab.type'; import { BlockJson } from '@/application/slate-yjs/utils/types'; -import { getFontFamily } from '@/utils/font'; -import { uniq } from 'lodash-es'; import { Element, Text } from 'slate'; -export function yDocToSlateContent(doc: YDoc): Element | undefined { - const sharedRoot = doc.getMap(YjsEditorKey.data_section) as YSharedRoot; - - const document = sharedRoot.get(YjsEditorKey.document); - const pageId = document.get(YjsEditorKey.page_id) as string; - const blocks = document.get(YjsEditorKey.blocks) as YBlocks; - const meta = document.get(YjsEditorKey.meta) as YMeta; - const childrenMap = meta.get(YjsEditorKey.children_map) as YChildrenMap; - const textMap = meta.get(YjsEditorKey.text_map) as YTextMap; - const fontFamilys: string[] = []; - +export function yDataToSlateContent({ + blocks, + rootId, + childrenMap, + textMap, +}: { + blocks: YBlocks; + childrenMap: YChildrenMap; + textMap: YTextMap; + rootId: string; +}): Element | undefined { function traverse(id: string) { const block = blocks.get(id).toJSON() as BlockJson; const childrenId = block.children as string; @@ -44,7 +42,9 @@ export function yDocToSlateContent(doc: YDoc): Element | undefined { let delta; - if (!textId) { + const yText = textId ? textMap.get(textId) : undefined; + + if (!yText) { if (children.length === 0) { children.push({ text: '', @@ -64,18 +64,12 @@ export function yDocToSlateContent(doc: YDoc): Element | undefined { } } } else { - delta = textMap.get(textId)?.toDelta(); + delta = yText.toDelta(); } try { const slateDelta = delta.flatMap(deltaInsertToSlateNode); - // collect font family - slateDelta.forEach((node: Text) => { - if (node.font_family) { - fontFamilys.push(getFontFamily(node.font_family)); - } - }); const textNode: Element = { textId, type: YjsEditorKey.text, @@ -85,30 +79,39 @@ export function yDocToSlateContent(doc: YDoc): Element | undefined { children.unshift(textNode); return slateNode; } catch (e) { - console.error(e); return; } } - const root = blocks.get(pageId); + const root = blocks.get(rootId); if (!root) return; - const result = traverse(pageId); + const result = traverse(rootId); if (!result) return; - if (fontFamilys.length > 0) { - window.WebFont?.load({ - google: { - families: uniq(fontFamilys), - }, - }); - } - return result; } +export function yDocToSlateContent(doc: YDoc): Element | undefined { + const sharedRoot = doc.getMap(YjsEditorKey.data_section) as YSharedRoot; + + const document = sharedRoot.get(YjsEditorKey.document); + const pageId = document.get(YjsEditorKey.page_id) as string; + const blocks = document.get(YjsEditorKey.blocks) as YBlocks; + const meta = document.get(YjsEditorKey.meta) as YMeta; + const childrenMap = meta.get(YjsEditorKey.children_map) as YChildrenMap; + const textMap = meta.get(YjsEditorKey.text_map) as YTextMap; + + return yDataToSlateContent({ + blocks, + rootId: pageId, + childrenMap, + textMap, + }); +} + export function blockToSlateNode(block: BlockJson): Element { const data = block.data; let blockData; @@ -116,7 +119,7 @@ export function blockToSlateNode(block: BlockJson): Element { try { blockData = data ? JSON.parse(data) : {}; } catch (e) { - blockData = {}; + // do nothing } return { @@ -128,13 +131,12 @@ export function blockToSlateNode(block: BlockJson): Element { }; } -export function deltaInsertToSlateNode({ - attributes, - insert, -}: { +export interface YDelta { insert: string; - attributes: Record; -}): Element | Text | Element[] { + attributes?: Record; +} + +export function deltaInsertToSlateNode({ attributes, insert }: YDelta): Element | Text | Element[] { const matchInlines = transformToInlineElement({ insert, attributes, @@ -145,17 +147,7 @@ export function deltaInsertToSlateNode({ } if (attributes) { - if ('font_color' in attributes && attributes['font_color'] === '') { - delete attributes['font_color']; - } - - if ('bg_color' in attributes && attributes['bg_color'] === '') { - delete attributes['bg_color']; - } - - if ('code' in attributes && !attributes['code']) { - delete attributes['code']; - } + dealWithEmptyAttribute(attributes); } return { @@ -164,10 +156,15 @@ export function deltaInsertToSlateNode({ }; } -export function transformToInlineElement(op: { - insert: string; - attributes: Record; -}): Element[] { +function dealWithEmptyAttribute(attributes: Record) { + for (const key in attributes) { + if (!attributes[key]) { + delete attributes[key]; + } + } +} + +export function transformToInlineElement(op: YDelta): Element[] { const attributes = op.attributes; if (!attributes) return []; diff --git a/frontend/appflowy_web_app/src/components/database/components/calendar/event/EventPaperTitle.tsx b/frontend/appflowy_web_app/src/components/database/components/calendar/event/EventPaperTitle.tsx index 6ede73fd99..0ab976c13e 100644 --- a/frontend/appflowy_web_app/src/components/database/components/calendar/event/EventPaperTitle.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/calendar/event/EventPaperTitle.tsx @@ -1,5 +1,5 @@ import { useCellSelector } from '@/application/database-yjs'; -import { TextCell } from '@/components/database/components/cell/cell.type'; +import { TextCell } from '@/application/database-yjs/cell.type'; import { TextProperty } from '@/components/database/components/property/text'; import React from 'react'; diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/Cell.tsx b/frontend/appflowy_web_app/src/components/database/components/cell/Cell.tsx index d234397606..3835db12ff 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/Cell.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/cell/Cell.tsx @@ -10,7 +10,7 @@ import { CheckboxCell } from '@/components/database/components/cell/checkbox'; import { SelectOptionCell } from '@/components/database/components/cell/select-option'; import { DateTimeCell } from '@/components/database/components/cell/date'; import { ChecklistCell } from '@/components/database/components/cell/checklist'; -import { CellProps, Cell as CellType } from '@/components/database/components/cell/cell.type'; +import { CellProps, Cell as CellType } from '@/application/database-yjs/cell.type'; import { RelationCell } from '@/components/database/components/cell/relation'; export function Cell(props: CellProps) { diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/cell.const.ts b/frontend/appflowy_web_app/src/components/database/components/cell/cell.const.ts index d9e3564096..b358ed6e49 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/cell.const.ts +++ b/frontend/appflowy_web_app/src/components/database/components/cell/cell.const.ts @@ -11,15 +11,3 @@ export const SelectOptionColorMap = { [SelectOptionColor.Aqua]: '--tint-aqua', [SelectOptionColor.Blue]: '--tint-blue', }; - -export const SelectOptionColorTextMap = { - [SelectOptionColor.Purple]: 'purpleColor', - [SelectOptionColor.Pink]: 'pinkColor', - [SelectOptionColor.LightPink]: 'lightPinkColor', - [SelectOptionColor.Orange]: 'orangeColor', - [SelectOptionColor.Yellow]: 'yellowColor', - [SelectOptionColor.Lime]: 'limeColor', - [SelectOptionColor.Green]: 'greenColor', - [SelectOptionColor.Aqua]: 'aquaColor', - [SelectOptionColor.Blue]: 'blueColor', -} as const; diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/checkbox/CheckboxCell.tsx b/frontend/appflowy_web_app/src/components/database/components/cell/checkbox/CheckboxCell.tsx index c2b3e7ac68..3b480f946a 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/checkbox/CheckboxCell.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/cell/checkbox/CheckboxCell.tsx @@ -1,7 +1,7 @@ import { ReactComponent as CheckboxCheckSvg } from '$icons/16x/check_filled.svg'; import { ReactComponent as CheckboxUncheckSvg } from '$icons/16x/uncheck.svg'; import { FieldType } from '@/application/database-yjs'; -import { CellProps, CheckboxCell as CheckboxCellType } from '@/components/database/components/cell/cell.type'; +import { CellProps, CheckboxCell as CheckboxCellType } from '@/application/database-yjs/cell.type'; export function CheckboxCell({ cell, style }: CellProps) { const checked = cell?.data; diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/checklist/ChecklistCell.tsx b/frontend/appflowy_web_app/src/components/database/components/cell/checklist/ChecklistCell.tsx index 618562b373..e3c927a607 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/checklist/ChecklistCell.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/cell/checklist/ChecklistCell.tsx @@ -1,5 +1,5 @@ import { FieldType, parseChecklistData } from '@/application/database-yjs'; -import { CellProps, ChecklistCell as ChecklistCellType } from '@/components/database/components/cell/cell.type'; +import { CellProps, ChecklistCell as ChecklistCellType } from '@/application/database-yjs/cell.type'; import LinearProgressWithLabel from '@/components/_shared/progress/LinearProgressWithLabel'; import React, { useMemo } from 'react'; diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/date/DateTimeCell.tsx b/frontend/appflowy_web_app/src/components/database/components/cell/date/DateTimeCell.tsx index c64c0f2d01..ca3ab41957 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/date/DateTimeCell.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/cell/date/DateTimeCell.tsx @@ -1,6 +1,6 @@ import { FieldType } from '@/application/database-yjs'; import { useDateTypeCellDispatcher } from '@/components/database/components/cell/Cell.hooks'; -import { CellProps, DateTimeCell as DateTimeCellType } from '@/components/database/components/cell/cell.type'; +import { CellProps, DateTimeCell as DateTimeCellType } from '@/application/database-yjs/cell.type'; import React, { useMemo } from 'react'; import { ReactComponent as ReminderSvg } from '$icons/16x/clock_alarm.svg'; diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/number/NumberCell.tsx b/frontend/appflowy_web_app/src/components/database/components/cell/number/NumberCell.tsx index f7312bfbd8..4217c880c4 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/number/NumberCell.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/cell/number/NumberCell.tsx @@ -5,7 +5,7 @@ import { parseNumberTypeOptions, FieldType, } from '@/application/database-yjs'; -import { CellProps, NumberCell as NumberCellType } from '@/components/database/components/cell/cell.type'; +import { CellProps, NumberCell as NumberCellType } from '@/application/database-yjs/cell.type'; import React, { useMemo } from 'react'; import Decimal from 'decimal.js'; diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/primary/PrimaryCell.tsx b/frontend/appflowy_web_app/src/components/database/components/cell/primary/PrimaryCell.tsx index 09287d48b5..7c2eb6c648 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/primary/PrimaryCell.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/cell/primary/PrimaryCell.tsx @@ -1,5 +1,5 @@ import { useNavigateToRow, useRowMetaSelector } from '@/application/database-yjs'; -import { TextCell as CellType, CellProps } from '@/components/database/components/cell/cell.type'; +import { TextCell as CellType, CellProps } from '@/application/database-yjs/cell.type'; import { TextCell } from '@/components/database/components/cell/text'; import OpenAction from '@/components/database/components/database-row/OpenAction'; import { getPlatform } from '@/utils/platform'; diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationCell.tsx b/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationCell.tsx index 47ac405966..90beed4732 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationCell.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationCell.tsx @@ -1,5 +1,5 @@ import { FieldType } from '@/application/database-yjs'; -import { CellProps, RelationCell as RelationCellType } from '@/components/database/components/cell/cell.type'; +import { CellProps, RelationCell as RelationCellType } from '@/application/database-yjs/cell.type'; import RelationItems from '@/components/database/components/cell/relation/RelationItems'; import React from 'react'; diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationItems.tsx b/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationItems.tsx index fd63c28196..259447e0ae 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationItems.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationItems.tsx @@ -6,7 +6,7 @@ import { useFieldSelector, useNavigateToRow, } from '@/application/database-yjs'; -import { RelationCell, RelationCellData } from '@/components/database/components/cell/cell.type'; +import { RelationCell, RelationCellData } from '@/application/database-yjs/cell.type'; import { RelationPrimaryValue } from '@/components/database/components/cell/relation/RelationPrimaryValue'; import { useGetDatabaseDispatch } from '@/components/database/Database.hooks'; import React, { useEffect, useMemo, useState } from 'react'; diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationPrimaryValue.tsx b/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationPrimaryValue.tsx index 0c33397eb8..a6ae613dd5 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationPrimaryValue.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationPrimaryValue.tsx @@ -1,5 +1,5 @@ import { FieldId, YDatabaseRow, YDoc, YjsDatabaseKey, YjsEditorKey } from '@/application/collab.type'; -import { parseYDatabaseCellToCell } from '@/components/database/components/cell/cell.parse'; +import { parseYDatabaseCellToCell } from '@/application/database-yjs/cell.parse'; import React, { useEffect, useState } from 'react'; export function RelationPrimaryValue({ rowDoc, fieldId }: { rowDoc: YDoc; fieldId: FieldId }) { diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/select-option/SelectOptionCell.tsx b/frontend/appflowy_web_app/src/components/database/components/cell/select-option/SelectOptionCell.tsx index 4d3318297f..9538582f11 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/select-option/SelectOptionCell.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/cell/select-option/SelectOptionCell.tsx @@ -1,7 +1,7 @@ import { useFieldSelector, parseSelectOptionTypeOptions } from '@/application/database-yjs'; import { Tag } from '@/components/_shared/tag'; import { SelectOptionColorMap } from '@/components/database/components/cell/cell.const'; -import { CellProps, SelectOptionCell as SelectOptionCellType } from '@/components/database/components/cell/cell.type'; +import { CellProps, SelectOptionCell as SelectOptionCellType } from '@/application/database-yjs/cell.type'; import React, { useCallback, useMemo } from 'react'; export function SelectOptionCell({ cell, fieldId, style, placeholder }: CellProps) { diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/text/TextCell.tsx b/frontend/appflowy_web_app/src/components/database/components/cell/text/TextCell.tsx index b4d048b14f..4e78093c08 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/text/TextCell.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/cell/text/TextCell.tsx @@ -1,5 +1,5 @@ import { useReadOnly } from '@/application/database-yjs'; -import { CellProps, TextCell as TextCellType } from '@/components/database/components/cell/cell.type'; +import { CellProps, TextCell as TextCellType } from '@/application/database-yjs/cell.type'; import React from 'react'; export function TextCell({ cell, style, placeholder }: CellProps) { diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/url/UrlCell.tsx b/frontend/appflowy_web_app/src/components/database/components/cell/url/UrlCell.tsx index 0ee2c1d5bf..6a30c4c9d2 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/url/UrlCell.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/cell/url/UrlCell.tsx @@ -1,5 +1,5 @@ import { useReadOnly } from '@/application/database-yjs'; -import { CellProps, UrlCell as UrlCellType } from '@/components/database/components/cell/cell.type'; +import { CellProps, UrlCell as UrlCellType } from '@/application/database-yjs/cell.type'; import { openUrl, processUrl } from '@/utils/url'; import React, { useMemo } from 'react'; diff --git a/frontend/appflowy_web_app/src/components/database/components/grid/grid-cell/GridCell.tsx b/frontend/appflowy_web_app/src/components/database/components/grid/grid-cell/GridCell.tsx index 0d3c7dfc11..5840109be7 100644 --- a/frontend/appflowy_web_app/src/components/database/components/grid/grid-cell/GridCell.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/grid/grid-cell/GridCell.tsx @@ -2,7 +2,7 @@ import { FieldId, YjsDatabaseKey } from '@/application/collab.type'; import { useCellSelector } from '@/application/database-yjs'; import { useFieldSelector } from '@/application/database-yjs/selector'; import { Cell } from '@/components/database/components/cell'; -import { CellProps, Cell as CellType } from '@/components/database/components/cell/cell.type'; +import { CellProps, Cell as CellType } from '@/application/database-yjs/cell.type'; import { PrimaryCell } from '@/components/database/components/cell/primary'; import React, { useEffect, useMemo, useRef } from 'react'; diff --git a/frontend/appflowy_web_app/src/components/database/components/grid/grid-row/useRenderRows.tsx b/frontend/appflowy_web_app/src/components/database/components/grid/grid-row/useRenderRows.tsx index 8b2e6597b8..b1de9ea0de 100644 --- a/frontend/appflowy_web_app/src/components/database/components/grid/grid-row/useRenderRows.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/grid/grid-row/useRenderRows.tsx @@ -1,4 +1,4 @@ -import { DEFAULT_ROW_HEIGHT, useReadOnly, useRowsSelector } from '@/application/database-yjs'; +import { DEFAULT_ROW_HEIGHT, useReadOnly, useRowOrdersSelector } from '@/application/database-yjs'; import { useMemo } from 'react'; @@ -15,16 +15,19 @@ export type RenderRow = { }; export function useRenderRows() { - const rows = useRowsSelector(); + const rows = useRowOrdersSelector(); const readOnly = useReadOnly(); const renderRows = useMemo(() => { - return [ - ...rows.map((row) => ({ + const rowItems = + rows?.map((row) => ({ type: RenderRowType.Row, rowId: row.id, height: row.height, - })), + })) ?? []; + + return [ + ...rowItems, !readOnly && { type: RenderRowType.NewRow, diff --git a/frontend/appflowy_web_app/src/components/database/components/property/Property.tsx b/frontend/appflowy_web_app/src/components/database/components/property/Property.tsx index b1e4662b2d..c8e5f34a43 100644 --- a/frontend/appflowy_web_app/src/components/database/components/property/Property.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/property/Property.tsx @@ -1,6 +1,6 @@ import { YjsDatabaseKey } from '@/application/collab.type'; import { FieldType, useCellSelector, useFieldSelector } from '@/application/database-yjs'; -import { Cell as CellType, CellProps } from '@/components/database/components/cell/cell.type'; +import { Cell as CellType, CellProps } from '@/application/database-yjs/cell.type'; import { CheckboxCell } from '@/components/database/components/cell/checkbox'; import { RowCreateModifiedTime } from '@/components/database/components/cell/created-modified'; import { DateTimeCell } from '@/components/database/components/cell/date'; diff --git a/frontend/appflowy_web_app/src/components/database/components/property/cheklist/ChecklistProperty.tsx b/frontend/appflowy_web_app/src/components/database/components/property/cheklist/ChecklistProperty.tsx index fabe862659..a8ed3ae2e7 100644 --- a/frontend/appflowy_web_app/src/components/database/components/property/cheklist/ChecklistProperty.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/property/cheklist/ChecklistProperty.tsx @@ -1,5 +1,5 @@ import { parseChecklistData } from '@/application/database-yjs'; -import { CellProps, ChecklistCell as CellType } from '@/components/database/components/cell/cell.type'; +import { CellProps, ChecklistCell as CellType } from '@/application/database-yjs/cell.type'; import { ChecklistCell } from '@/components/database/components/cell/checklist'; import React, { useMemo } from 'react'; import { ReactComponent as CheckboxCheckSvg } from '$icons/16x/check_filled.svg'; diff --git a/frontend/appflowy_web_app/src/components/database/components/property/text/TextProperty.tsx b/frontend/appflowy_web_app/src/components/database/components/property/text/TextProperty.tsx index 64589c0ea3..99b54b08a7 100644 --- a/frontend/appflowy_web_app/src/components/database/components/property/text/TextProperty.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/property/text/TextProperty.tsx @@ -1,4 +1,4 @@ -import { CellProps, TextCell } from '@/components/database/components/cell/cell.type'; +import { CellProps, TextCell } from '@/application/database-yjs/cell.type'; import { TextField } from '@mui/material'; import React from 'react'; diff --git a/frontend/appflowy_web_app/src/components/database/grid/Grid.tsx b/frontend/appflowy_web_app/src/components/database/grid/Grid.tsx index 521fa1cac0..15aaa23e51 100644 --- a/frontend/appflowy_web_app/src/components/database/grid/Grid.tsx +++ b/frontend/appflowy_web_app/src/components/database/grid/Grid.tsx @@ -1,4 +1,4 @@ -import { RowsContext, useDatabase, useRowOrdersSelector, useViewId } from '@/application/database-yjs'; +import { useDatabase, useViewId } from '@/application/database-yjs'; import { useRenderFields, GridHeader, GridTable } from '@/components/database/components/grid'; import { CircularProgress } from '@mui/material'; import React, { useEffect, useState } from 'react'; @@ -9,13 +9,12 @@ export function Grid() { const [scrollLeft, setScrollLeft] = useState(0); const { fields, columnWidth } = useRenderFields(); - const rowOrders = useRowOrdersSelector(); useEffect(() => { setScrollLeft(0); }, [viewId]); - if (!database || !rowOrders) { + if (!database) { return (
@@ -24,24 +23,18 @@ export function Grid() { } return ( - -
- -
- -
+
+ +
+
- +
); } diff --git a/frontend/appflowy_web_app/src/utils/font.ts b/frontend/appflowy_web_app/src/utils/font.ts index 765ebf5f00..645340d958 100644 --- a/frontend/appflowy_web_app/src/utils/font.ts +++ b/frontend/appflowy_web_app/src/utils/font.ts @@ -1,3 +1,16 @@ +const hasLoadedFonts: Set = new Set(); + export function getFontFamily(attribute: string) { - return attribute.split('_')[0]; + const fontFamily = attribute.split('_')[0]; + + if (hasLoadedFonts.has(fontFamily)) { + return fontFamily; + } + + window.WebFont?.load({ + google: { + families: [fontFamily], + }, + }); + return fontFamily; } diff --git a/frontend/appflowy_web_app/vite.config.ts b/frontend/appflowy_web_app/vite.config.ts index aeadc4e8fa..bc69405ca3 100644 --- a/frontend/appflowy_web_app/vite.config.ts +++ b/frontend/appflowy_web_app/vite.config.ts @@ -44,6 +44,13 @@ export default defineConfig({ istanbul({ cypress: true, requireEnv: false, + include: ['src/**/*'], + exclude: [ + '**/__tests__/**/*', + 'cypress/**/*', + 'node_modules/**/*', + 'src/application/services/tauri-services/**/*', + ], }), usePluginImport({ libraryName: '@mui/icons-material', @@ -130,6 +137,12 @@ export default defineConfig({ }, optimizeDeps: { - include: ['react', 'react-dom', '@mui/icons-material/ErrorOutline', '@mui/icons-material/CheckCircleOutline'], + include: [ + 'react', + 'react-dom', + '@mui/icons-material/ErrorOutline', + '@mui/icons-material/CheckCircleOutline', + '@mui/icons-material/FunctionsOutlined', + ], }, });