diff --git a/.changeset/config.json b/.changeset/config.json index 1892e1a308..6ce3b1a2ce 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -5,7 +5,7 @@ "fixed": [], "linked": [], "access": "restricted", - "baseBranch": "feat/cloud-sync", + "baseBranch": "feat/master", "updateInternalDependencies": "patch", "ignore": [] } diff --git a/.github/workflows/changlog.yml b/.github/workflows/changlog.yml index 6a4ced3e20..d91559cf50 100644 --- a/.github/workflows/changlog.yml +++ b/.github/workflows/changlog.yml @@ -1,8 +1,7 @@ name: Pathfinder changelog on: - push: - branches: [feat/cloud-sync, master] + workflow_dispatch: # Cancels all previous workflow runs for pull requests that have not completed. # See https://docs.github.com/en/actions/using-jobs/using-concurrency diff --git a/.github/workflows/temp_test.yml b/.github/workflows/publish.yml similarity index 52% rename from .github/workflows/temp_test.yml rename to .github/workflows/publish.yml index a1fa93bdf0..09ec455e77 100644 --- a/.github/workflows/temp_test.yml +++ b/.github/workflows/publish.yml @@ -1,10 +1,7 @@ -name: Pathfinder Check +name: Build Pathfinder Self-hosted on: - push: - branches: [feat/cloud-sync] - pull_request: - branches: [feat/cloud-sync] + workflow_dispatch: # Cancels all previous workflow runs for pull requests that have not completed. # See https://docs.github.com/en/actions/using-jobs/using-concurrency @@ -15,69 +12,12 @@ concurrency: cancel-in-progress: true jobs: - build: - name: Build on Pull Request - if: github.ref != 'refs/heads/master' + build-self-hosted: + name: Build Community + if: github.ref == 'refs/heads/master' runs-on: self-hosted environment: development - steps: - - uses: actions/checkout@v2 - - uses: pnpm/action-setup@v2 - with: - version: 'latest' - - - name: Use Node.js - uses: actions/setup-node@v2 - with: - node-version: 18.x - registry-url: https://npm.pkg.github.com - scope: '@toeverything' - cache: 'pnpm' - - - run: node scripts/module-resolve/ci.js - - - name: Restore cache - uses: actions/cache@v3 - with: - path: | - .next/cache - # Generate a new cache whenever packages or source files change. - key: ${{ runner.os }}-nextjs-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} - # If source files changed but packages didn't, rebuild from a prior cache. - restore-keys: | - ${{ runner.os }}-nextjs-${{ hashFiles('**/pnpm-lock.yaml') }}- - - - name: Install dependencies - run: pnpm install --no-frozen-lockfile - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_GITHUB_AUTH_TOKEN }} - - - name: Build - run: pnpm build - env: - NEXT_PUBLIC_FIREBASE_API_KEY: ${{ secrets.NEXT_PUBLIC_FIREBASE_API_KEY }} - NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN: ${{ secrets.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN }} - NEXT_PUBLIC_FIREBASE_PROJECT_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_PROJECT_ID }} - NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET: ${{ secrets.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET }} - NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID }} - NEXT_PUBLIC_FIREBASE_APP_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_APP_ID }} - NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID }} - - - name: Export - run: pnpm export - - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - path: ./packages/app/.next - - lint: - name: Lint and E2E Test - runs-on: ubuntu-latest - environment: development - needs: build - steps: - uses: actions/checkout@v2 - uses: pnpm/action-setup@v2 @@ -106,18 +46,8 @@ jobs: env: NODE_AUTH_TOKEN: ${{ secrets.NPM_GITHUB_AUTH_TOKEN }} - - name: Download artifact - uses: actions/download-artifact@v3 - with: - name: artifact - path: packages/app/.next/ - - - name: Lint & E2E Test - run: | - pnpm lint --max-warnings=0 - PLAYWRIGHT_BROWSERS_PATH=0 npx playwright install chromium - PLAYWRIGHT_BROWSERS_PATH=0 pnpm test - PLAYWRIGHT_BROWSERS_PATH=0 pnpm test:dc + - name: Build + run: pnpm build env: NEXT_PUBLIC_FIREBASE_API_KEY: ${{ secrets.NEXT_PUBLIC_FIREBASE_API_KEY }} NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN: ${{ secrets.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN }} @@ -126,3 +56,59 @@ jobs: NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID }} NEXT_PUBLIC_FIREBASE_APP_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_APP_ID }} NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID }} + + - name: Export + run: pnpm export + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + path: ./packages/app/out + + publish-self-hosted: + name: Push Community Image + if: github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + needs: build-self-hosted + + permissions: + contents: read + packages: write + + env: + REGISTRY: ghcr.io + IMAGE_NAME: 'toeverything/affine-static' + IMAGE_TAG_LATEST: abbey-wood + + steps: + - name: Check out the repo + uses: actions/checkout@v2 + + - name: Download artifact + uses: actions/download-artifact@v3 + with: + name: artifact + path: packages/app/out/ + + - name: Log in to Docker Hub + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v4 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: ${{ env.IMAGE_TAG_LATEST }} + + - name: Build Docker image + uses: docker/build-push-action@v3 + with: + context: . + push: true + file: ./.github/deployment/Dockerfile + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.vscode/settings.json b/.vscode/settings.json index a366193c02..166b0206c0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,5 +3,12 @@ "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, "editor.formatOnSaveMode": "file", - "cSpell.words": ["blocksuite", "datacenter", "livedemo", "pnpm", "testid"] + "cSpell.words": [ + "blocksuite", + "datacenter", + "livedemo", + "pnpm", + "selfhosted", + "testid" + ] } diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 4423f5ad0b..0000000000 --- a/jest.config.js +++ /dev/null @@ -1,19 +0,0 @@ -/** @type {import('ts-jest').JestConfigWithTsJest} */ -module.exports = { - projects: ['/packages/app'], - preset: 'ts-jest', - testEnvironment: 'node', - moduleNameMapper: { - '^(\\.{1,2}/.*)\\.js$': '$1', - }, - transform: { - // '^.+\\.[tj]sx?$' to process js/ts with `ts-jest` - // '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest` - '^.+\\.tsx?$': [ - 'ts-jest', - { - useESM: true, - }, - ], - }, -}; diff --git a/package.json b/package.json index 1abcf66a9b..0b5a3def03 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "license": "MPL-2.0", "scripts": { "dev": "pnpm --filter=!@affine/app build && pnpm --filter @affine/app dev", - "dev:ac": "pnpm --filter=!@affine/app build && pnpm --filter @affine/app dev:ac", + "dev:ac": "pnpm --filter=!@affine/app build && NODE_API_SERVER=ac pnpm --filter @affine/app dev", + "dev:local": "pnpm --filter=!@affine/app build && NODE_API_SERVER=local pnpm --filter @affine/app dev", "build": " pnpm --filter=!@affine/app build && pnpm --filter!=@affine/datacenter -r build", "export": "pnpm --filter @affine/app export", "start": "pnpm --filter @affine/app start", @@ -14,7 +15,8 @@ "test": "playwright test", "test:dc": "pnpm --filter @affine/datacenter test", "test:e2e:codegen": "npx playwright codegen http://localhost:8080", - "test:unit": "jest", + "test:unit": "vitest run", + "test:unit:watch": "vitest", "postinstall": "husky install", "notify": "node --experimental-modules scripts/notify.mjs", "check:ci": "pnpm lint & pnpm test" @@ -24,24 +26,23 @@ "*.{ts,tsx,js,jsx}": "npx eslint --cache --fix" }, "devDependencies": { - "@jest/globals": "^29.3.1", + "@changesets/cli": "^2.26.0", "@playwright/test": "^1.29.1", - "@typescript-eslint/eslint-plugin": "^5.47.0", - "@typescript-eslint/parser": "^5.47.0", "@types/eslint": "^8.4.10", "@types/node": "^18.11.17", + "@typescript-eslint/eslint-plugin": "^5.47.0", + "@typescript-eslint/parser": "^5.47.0", "eslint": "^8.30.0", "eslint-config-next": "12.3.1", "eslint-config-prettier": "^8.5.0", "eslint-plugin-prettier": "^4.2.1", + "fake-indexeddb": "4.0.1", "got": "^12.5.3", - "jest": "^29.3.1", - "prettier": "^2.7.1", - "ts-jest": "^29.0.3", - "typescript": "^4.9.3", - "lint-staged": "^13.1.0", "husky": "^8.0.2", - "@changesets/cli": "^2.26.0" + "lint-staged": "^13.1.0", + "prettier": "^2.7.1", + "typescript": "^4.9.3", + "vitest": "^0.26.3" }, "eslintConfig": { "root": true, @@ -65,7 +66,8 @@ "reportUnusedDisableDirectives": true, "ignorePatterns": [ "src/**/*.test.ts", - "package/**/dist/*" + "package/**/dist/*", + "package/**/sync.js" ] } } diff --git a/packages/app/next.config.js b/packages/app/next.config.js index 6d7225864c..df91a70108 100644 --- a/packages/app/next.config.js +++ b/packages/app/next.config.js @@ -9,6 +9,27 @@ const EDITOR_VERSION = enableDebugLocal ? 'local-version' : dependencies['@blocksuite/editor']; +const profileTarget = { + ac: '100.85.73.88:12001', + dev: '100.85.73.88:12001', + local: '127.0.0.1:3000', +}; + +const getRedirectConfig = profile => { + const target = profileTarget[profile || 'dev'] || profileTarget['dev']; + + return [ + [ + { source: '/api/:path*', destination: `http://${target}/api/:path*` }, + { + source: '/collaboration/:path*', + destination: `http://${target}/collaboration/:path*`, + }, + ], + target, + ]; +}; + /** @type {import('next').NextConfig} */ const nextConfig = { productionBrowserSourceMaps: true, @@ -36,30 +57,12 @@ const nextConfig = { images: { unoptimized: true, }, - // XXX not test yet rewrites: async () => { - if (process.env.NODE_API_SERVER === 'ac') { - let destinationAC = 'http://100.85.73.88:12001/api/:path*'; - printer.info('API request proxy to [AC Server] ' + destinationAC); - return [ - { - source: '/api/:path*', - destination: destinationAC, - }, - ]; - } else { - let destinationStandard = 'http://100.77.180.48:11001/api/:path*'; - printer.info( - 'API request proxy to [Standard Server] ' + destinationStandard - ); - - return [ - { - source: '/api/:path*', - destination: destinationStandard, - }, - ]; - } + const [profile, desc] = getRedirectConfig(process.env.NODE_API_SERVER); + printer.info( + `API request proxy to [${process.env.NODE_API_SERVER} Server]: ` + desc + ); + return profile; }, basePath: process.env.BASE_PATH, }; diff --git a/packages/app/package.json b/packages/app/package.json index ab9571a953..3ba74e2712 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -3,7 +3,6 @@ "version": "0.3.1", "scripts": { "dev": "next dev -p 8080", - "dev:ac": "NODE_API_SERVER=ac next dev -p 8080", "build": "next build", "export": "next export", "start": "next start", @@ -11,10 +10,10 @@ }, "dependencies": { "@affine/datacenter": "workspace:*", - "@blocksuite/blocks": "0.3.1", - "@blocksuite/editor": "0.3.1", + "@blocksuite/blocks": "=0.3.1-20230106060050-1aad55d", + "@blocksuite/editor": "=0.3.1-20230106060050-1aad55d", "@blocksuite/icons": "^2.0.2", - "@blocksuite/store": "0.3.1", + "@blocksuite/store": "=0.3.1-20230106060050-1aad55d", "@emotion/css": "^11.10.0", "@emotion/react": "^11.10.4", "@emotion/server": "^11.10.0", diff --git a/packages/app/scripts/__tests__/printer.spec.ts b/packages/app/scripts/__tests__/printer.spec.ts index beb14114e6..7f67223c1d 100644 --- a/packages/app/scripts/__tests__/printer.spec.ts +++ b/packages/app/scripts/__tests__/printer.spec.ts @@ -1,4 +1,4 @@ -import { describe, test, expect } from '@jest/globals'; +import { describe, test, expect } from 'vitest'; import { printer } from './../printer'; const chalk = require('chalk'); describe('printer', () => { diff --git a/packages/app/src/components/header/editor-header.tsx b/packages/app/src/components/header/editor-header.tsx index c1b52ec464..7f433de467 100644 --- a/packages/app/src/components/header/editor-header.tsx +++ b/packages/app/src/components/header/editor-header.tsx @@ -22,14 +22,14 @@ export const EditorHeader = () => { useEffect(() => { onPropsUpdated(editor => { - setTitle(editor.model?.title || 'Untitled'); + setTitle(editor.pageBlockModel?.title || 'Untitled'); }); }, [onPropsUpdated]); useEffect(() => { setTimeout(() => { // If first time in, need to wait for editor to be inserted into DOM - setTitle(editor?.model?.title || 'Untitled'); + setTitle(editor?.pageBlockModel?.title || 'Untitled'); }, 300); }, [editor]); diff --git a/packages/app/src/components/import/index.tsx b/packages/app/src/components/import/index.tsx index 7387cb5de6..96a383dad2 100644 --- a/packages/app/src/components/import/index.tsx +++ b/packages/app/src/components/import/index.tsx @@ -31,9 +31,10 @@ export const ImportModal = ({ open, onClose }: ImportModalProps) => { setTimeout(() => { const editor = document.querySelector('editor-container'); if (editor) { - const groupId = page.addBlock({ flavour: 'affine:group' }, pageId); + page.addBlock({ flavour: 'affine:surface' }, null); + const frameId = page.addBlock({ flavour: 'affine:frame' }, pageId); // TODO blocksuite should offer a method to import markdown from store - editor.clipboard.importMarkdown(template.source, `${groupId}`); + editor.clipboard.importMarkdown(template.source, `${frameId}`); page.resetHistory(); editor.requestUpdate(); } diff --git a/packages/app/src/components/login-modal/LoginOptionButton.tsx b/packages/app/src/components/login-modal/LoginOptionButton.tsx index e04d8fecc7..f8e3cdafce 100644 --- a/packages/app/src/components/login-modal/LoginOptionButton.tsx +++ b/packages/app/src/components/login-modal/LoginOptionButton.tsx @@ -9,14 +9,14 @@ export const GoogleLoginButton = () => { return ( { - getDataCenter() - .then(dc => dc.apis.signInWithGoogle?.()) - .then(() => { - triggerLoginModal(); - }) - .catch(error => { - console.log('sign google error', error); - }); + // getDataCenter() + // .then(dc => dc.apis.signInWithGoogle?.()) + // .then(() => { + // triggerLoginModal(); + // }) + // .catch(error => { + // console.log('sign google error', error); + // }); }} > diff --git a/packages/app/src/components/workspace-setting/general/leave/leave.tsx b/packages/app/src/components/workspace-setting/general/leave/leave.tsx index 4305400d08..a648da6a08 100644 --- a/packages/app/src/components/workspace-setting/general/leave/leave.tsx +++ b/packages/app/src/components/workspace-setting/general/leave/leave.tsx @@ -7,7 +7,7 @@ import { } from './style'; import { ModalCloseButton } from '@/ui/modal'; import { Button } from '@/ui/button'; -import { getDataCenter } from '@affine/datacenter'; +// import { getDataCenter } from '@affine/datacenter'; import { useAppState } from '@/providers/app-state-provider'; interface WorkspaceDeleteProps { @@ -25,10 +25,10 @@ export const WorkspaceLeave = ({ // const router = useRouter(); const { refreshWorkspacesMeta } = useAppState(); const handleLeave = async () => { - const dc = await getDataCenter(); - await dc.apis.leaveWorkspace({ id: workspaceId }); - // router.push(`/workspace/${nextWorkSpaceId}`); - refreshWorkspacesMeta(); + // const dc = await getDataCenter(); + // await dc.apis.leaveWorkspace({ id: workspaceId }); + // // router.push(`/workspace/${nextWorkSpaceId}`); + // refreshWorkspacesMeta(); onClose(); }; diff --git a/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent.tsx b/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent.tsx index db6d4d34f1..9bb22b343c 100644 --- a/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent.tsx +++ b/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent.tsx @@ -53,9 +53,9 @@ export const SelectorPopperContent = ({ if (type === WorkspaceType.Private) { return { id, member_count: 1, owner: user }; } else { - const dc = await getDataCenter(); - const data = await dc.apis.getWorkspaceDetail({ id }); - return { id, ...data } || { id, member_count: 0, owner: user }; + // const dc = await getDataCenter(); + // const data = await dc.apis.getWorkspaceDetail({ id }); + // return { id, ...data } || { id, member_count: 0, owner: user }; } } }) diff --git a/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/WorkspaceItem/CreateWorkspaceItem/workspace-create/workspace-create.tsx b/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/WorkspaceItem/CreateWorkspaceItem/workspace-create/workspace-create.tsx index b07fdeecac..23434c28d2 100644 --- a/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/WorkspaceItem/CreateWorkspaceItem/workspace-create/workspace-create.tsx +++ b/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/WorkspaceItem/CreateWorkspaceItem/workspace-create/workspace-create.tsx @@ -52,10 +52,10 @@ export const WorkspaceCreate = ({ open, onClose }: WorkspaceCreateProps) => { ctx.fillText(workspaceName[0], 50, 50); canvas.toBlob(blob => { if (blob) { - const blobId = getDataCenter().then(dc => - dc.apis.uploadBlob({ blob }) - ); - resolve(blobId); + // const blobId = getDataCenter().then(dc => + // dc.apis.uploadBlob({ blob }) + // ); + // resolve(blobId); } else { reject(); } @@ -71,23 +71,23 @@ export const WorkspaceCreate = ({ open, onClose }: WorkspaceCreateProps) => { setCreating(false); }); if (blobId) { - getDataCenter() - .then(dc => - dc.apis.createWorkspace({ name: workspaceName, avatar: blobId }) - ) - .then(async data => { - await refreshWorkspacesMeta(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - router.push(`/workspace/${data.id}`); - onClose(); - }) - .catch(err => { - console.log(err, 'err'); - }) - .finally(() => { - setCreating(false); - }); + // getDataCenter() + // .then(dc => + // dc.apis.createWorkspace({ name: workspaceName, avatar: blobId }) + // ) + // .then(async data => { + // await refreshWorkspacesMeta(); + // // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // // @ts-ignore + // router.push(`/workspace/${data.id}`); + // onClose(); + // }) + // .catch(err => { + // console.log(err, 'err'); + // }) + // .finally(() => { + // setCreating(false); + // }); } }; return ( diff --git a/packages/app/src/components/workspace-slider-bar/index.tsx b/packages/app/src/components/workspace-slider-bar/index.tsx index 8b96df83cf..5b2ca0f04e 100644 --- a/packages/app/src/components/workspace-slider-bar/index.tsx +++ b/packages/app/src/components/workspace-slider-bar/index.tsx @@ -4,14 +4,14 @@ import { StyledArrowButton, StyledLink, StyledListItem, - StyledListItemForWorkspace, + // StyledListItemForWorkspace, StyledNewPageButton, StyledSliderBar, StyledSliderBarWrapper, StyledSubListItem, } from './style'; import { Arrow } from './icons'; -import { WorkspaceSelector } from './WorkspaceSelector'; +// import { WorkspaceSelector } from './WorkspaceSelector'; import Collapse from '@mui/material/Collapse'; import { ArrowDownIcon, @@ -113,9 +113,9 @@ export const WorkSpaceSliderBar = () => { - + {/* - + */} { diff --git a/packages/app/src/hooks/use-props-updated.ts b/packages/app/src/hooks/use-props-updated.ts index d76ada43d2..1bbd8d2cd4 100644 --- a/packages/app/src/hooks/use-props-updated.ts +++ b/packages/app/src/hooks/use-props-updated.ts @@ -17,7 +17,7 @@ export const usePropsUpdated: UsePropsUpdated = () => { return; } setTimeout(() => { - editor.model?.propsUpdated.on(() => { + editor.pageBlockModel?.propsUpdated.on(() => { callbackQueue.current.forEach(callback => { callback(editor); }); @@ -26,7 +26,7 @@ export const usePropsUpdated: UsePropsUpdated = () => { return () => { callbackQueue.current = []; - editor?.model?.propsUpdated.dispose(); + editor?.pageBlockModel?.propsUpdated?.dispose(); }; }, [editor]); diff --git a/packages/app/src/pages/invite/[invite_code].tsx b/packages/app/src/pages/invite/[invite_code].tsx index c6ef0b6b4e..91488a7397 100644 --- a/packages/app/src/pages/invite/[invite_code].tsx +++ b/packages/app/src/pages/invite/[invite_code].tsx @@ -24,19 +24,19 @@ export default function DevPage() { // eslint-disable-next-line @typescript-eslint/no-explicit-any const [inviteData, setInviteData] = useState(null); useEffect(() => { - getDataCenter() - .then(dc => - dc.apis.acceptInviting({ - invitingCode: router.query.invite_code as string, - }) - ) - .then(data => { - setSuccessInvited(true); - setInviteData(data); - }) - .catch(err => { - console.log('err: ', err); - }); + // getDataCenter() + // .then(dc => + // dc.apis.acceptInviting({ + // invitingCode: router.query.invite_code as string, + // }) + // ) + // .then(data => { + // setSuccessInvited(true); + // setInviteData(data); + // }) + // .catch(err => { + // console.log('err: ', err); + // }); }, [router.query.invite_code]); return ( diff --git a/packages/app/src/pages/playground/templates.tsx b/packages/app/src/pages/playground/templates.tsx index 0517498878..a83d25e516 100644 --- a/packages/app/src/pages/playground/templates.tsx +++ b/packages/app/src/pages/playground/templates.tsx @@ -43,12 +43,16 @@ const All = () => { if (page) { currentWorkspace?.setPageMeta(page.id, { title }); if (page && page.root === null) { - setTimeout(() => { + setTimeout(async () => { const editor = document.querySelector('editor-container'); if (editor) { - const groupId = page.addBlock({ flavour: 'affine:group' }, pageId); + page.addBlock({ flavour: 'affine:surface' }, null); + const frameId = page.addBlock({ flavour: 'affine:frame' }, pageId); // TODO blocksuite should offer a method to import markdown from store - editor.clipboard.importMarkdown(template.source, `${groupId}`); + await editor.clipboard.importMarkdown( + template.source, + `${frameId}` + ); page.resetHistory(); editor.requestUpdate(); } diff --git a/packages/app/src/pages/testing-only.tsx b/packages/app/src/pages/testing-only.tsx new file mode 100644 index 0000000000..a050496bb9 --- /dev/null +++ b/packages/app/src/pages/testing-only.tsx @@ -0,0 +1,18 @@ +import { useEffect } from 'react'; +import { getDataCenter } from '@affine/datacenter'; +/** + * testing only when development + */ + +const Page = () => { + useEffect(() => { + getDataCenter().then(dc => { + // @ts-expect-error global variable + window.dc = dc; + }); + }, []); + + return
Testing only
; +}; + +export default Page; diff --git a/packages/app/src/pages/workspace/[workspaceId]/[pageId].tsx b/packages/app/src/pages/workspace/[workspaceId]/[pageId].tsx index 971e950f8b..05c2c17eba 100644 --- a/packages/app/src/pages/workspace/[workspaceId]/[pageId].tsx +++ b/packages/app/src/pages/workspace/[workspaceId]/[pageId].tsx @@ -1,10 +1,10 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { - useRef, - useEffect, - useState, - ReactElement, PropsWithChildren, + ReactElement, + useEffect, + useRef, + useState, } from 'react'; import { styled } from '@/styles'; import { EditorHeader } from '@/components/header'; @@ -16,9 +16,11 @@ import type { NextPageWithLayout } from '../..//_app'; import WorkspaceLayout from '@/components/workspace-layout'; import { useRouter } from 'next/router'; import { usePageHelper } from '@/hooks/use-page-helper'; + const StyledEditorContainer = styled('div')(() => { return { height: 'calc(100vh - 60px)', + padding: '0 32px', }; }); @@ -54,15 +56,20 @@ const Page: NextPageWithLayout = () => { flavour: 'affine:page', title, }); - const groupId = currentPage!.addBlock( - { flavour: 'affine:group' }, + currentPage!.addBlock({ flavour: 'affine:surface' }, null); + const frameId = currentPage!.addBlock( + { flavour: 'affine:frame' }, pageId ); - currentPage!.addBlock({ flavour: 'affine:group' }, pageId); + currentPage!.addBlock({ flavour: 'affine:frame' }, pageId); // If this is a first page in workspace, init an introduction markdown if (isFirstPage) { - editor.clipboard.importMarkdown(exampleMarkdown, `${groupId}`); - currentWorkspace!.setPageMeta(currentPage!.id, { title }); + editor.clipboard + .importMarkdown(exampleMarkdown, `${frameId}`) + .then(() => { + currentWorkspace!.setPageMeta(currentPage!.id, { title }); + currentPage!.resetHistory(); + }); } currentPage!.resetHistory(); } diff --git a/packages/app/src/templates/Welcome-to-AFFiNE-Alpha-v2.0.md b/packages/app/src/templates/Welcome-to-AFFiNE-Alpha-v2.0.md index e0fc661340..6495135903 100644 --- a/packages/app/src/templates/Welcome-to-AFFiNE-Alpha-v2.0.md +++ b/packages/app/src/templates/Welcome-to-AFFiNE-Alpha-v2.0.md @@ -10,6 +10,11 @@ Let us know what you think of this latest version. 2. More complete Markdown support and improved keyboard shortcuts; 3. New features such as dark mode; Switch between view styles using the ☀ and 🌙. 4. Clean and modern UI/UX design. +5. You can self-host locally with Docker. + +```basic +docker run -d -v [YOUR_PATH]:/app/data -p 3000:3000 ghcr.io/toeverything/affine-self-hosted:alpha-abbey-wood +``` **Looking for Markdown syntax or keyboard shortcuts?** @@ -23,15 +28,21 @@ Let us know what you think of this latest version. - Copy and paste **images** into your pages, resize them and add captions - Add horizontal line dividers to your text with `---` and `***` - Changes are saved **locally**, but we still recommend you export your data to avoid data loss -- Insert code blocks with syntax highlighting support using ``` +- Insert code blocks with syntax highlighting support using ````` ### Playground: [] Try a horizontal line: `---` -[] What about a code block? ``` +[] What about a code block? ````` -        console.log('Hello world'); +```javascript +console.log('Hello world'); +``` + +[] Can you resize this image? + +![](https://cdn.affine.pro/694fdbab78e0da3ed7922eba7d506dcf12f57308e1904dd694f53eb2.jpg) **How about page management?** diff --git a/packages/app/src/utils/__tests__/get-is-mobile.spec.ts b/packages/app/src/utils/__tests__/get-is-mobile.spec.ts index bef77b03ba..c5df2c6a63 100644 --- a/packages/app/src/utils/__tests__/get-is-mobile.spec.ts +++ b/packages/app/src/utils/__tests__/get-is-mobile.spec.ts @@ -1,4 +1,4 @@ -import { describe, test, expect } from '@jest/globals'; +import { describe, test, expect } from 'vitest'; import { isMobile } from '../get-is-mobile'; describe('get-is-mobile', () => { diff --git a/packages/data-center/package.json b/packages/data-center/package.json index 5d0ebc843f..fe639b306e 100644 --- a/packages/data-center/package.json +++ b/packages/data-center/package.json @@ -26,8 +26,8 @@ "typescript": "^4.8.4" }, "dependencies": { - "@blocksuite/blocks": "^0.3.1", - "@blocksuite/store": "^0.3.1", + "@blocksuite/blocks": "=0.3.1-20230106060050-1aad55d", + "@blocksuite/store": "=0.3.1-20230106060050-1aad55d", "debug": "^4.3.4", "encoding": "^0.1.13", "firebase": "^9.15.0", diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index 3e31e9f1d8..5714fe98fa 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -1,230 +1,315 @@ +import { Workspaces } from './workspaces'; +import type { WorkspacesChangeEvent } from './workspaces'; +import { Workspace } from '@blocksuite/store'; +import { BaseProvider } from './provider/base'; +import { LocalProvider } from './provider/local/local'; +import { AffineProvider } from './provider'; +import type { Workspace as WS, WorkspaceMeta } from './types'; import assert from 'assert'; +import { getLogger } from './logger'; import { BlockSchema } from '@blocksuite/blocks/models'; -import { Workspace, Signal } from '@blocksuite/store'; - -import { getLogger } from './index.js'; -import { getApis, Apis } from './apis/index.js'; -import { AffineProvider, BaseProvider } from './provider/index.js'; -import { LocalProvider } from './provider/index.js'; -import { getKVConfigure } from './store.js'; - -// load workspace's config -type LoadConfig = { - // use witch provider load data - providerId?: string; - // provider config - config?: Record; -}; - -export type DataCenterSignals = DataCenter['signals']; -type WorkspaceItem = { - // provider id - provider: string; - // data exists locally - locally: boolean; -}; -type WorkspaceLoadEvent = WorkspaceItem & { - workspace: string; -}; +import { applyUpdate, encodeStateAsUpdate } from 'yjs'; +/** + * @class DataCenter + * @classdesc DataCenter is a data center, it can manage different providers for business + */ export class DataCenter { - private readonly _apis: Apis; - private readonly _providers = new Map(); - private readonly _workspaces = new Map>(); - private readonly _config; - private readonly _logger; + private readonly _workspaces = new Workspaces(); + private readonly _logger = getLogger('dc'); + /** + * A mainProvider must exist as the only data trustworthy source. + */ + private _mainProvider?: BaseProvider; + providerMap: Map = new Map(); - readonly signals = { - listAdd: new Signal(), - listRemove: new Signal(), - }; + constructor(debug: boolean) { + this._logger.enabled = debug; + } static async init(debug: boolean): Promise { const dc = new DataCenter(debug); - dc.addProvider(AffineProvider); - dc.addProvider(LocalProvider); + // TODO: switch different provider + dc.registerProvider( + new LocalProvider({ + logger: dc._logger, + workspaces: dc._workspaces.createScope(), + }) + ); + dc.registerProvider( + new AffineProvider({ + logger: dc._logger, + workspaces: dc._workspaces.createScope(), + }) + ); return dc; } - private constructor(debug: boolean) { - this._apis = getApis(); - this._config = getKVConfigure('sys'); - this._logger = getLogger('dc'); - this._logger.enabled = debug; - - this.signals.listAdd.on(e => { - this._config.set(`list:${e.workspace}`, { - provider: e.provider, - locally: e.locally, - }); - }); - this.signals.listRemove.on(workspace => { - this._config.delete(`list:${workspace}`); - }); - } - - get apis(): Readonly { - return this._apis; - } - - private addProvider(provider: typeof BaseProvider) { - this._providers.set(provider.id, provider); - } - - private async _getProvider( - id: string, - providerId = 'local' - ): Promise { - const providerKey = `${id}:provider`; - if (this._providers.has(providerId)) { - await this._config.set(providerKey, providerId); - return providerId; - } else { - const providerValue = await this._config.get(providerKey); - if (providerValue) return providerValue; + /** + * Register provider. + * We will automatically set the first provider to default provider. + */ + registerProvider(provider: BaseProvider) { + if (!this._mainProvider) { + this._mainProvider = provider; } - throw Error(`Provider ${providerId} not found`); + + provider.init(); + this.providerMap.set(provider.id, provider); } - private async _getWorkspace( - id: string, - params: LoadConfig - ): Promise { - this._logger(`Init workspace ${id} with ${params.providerId}`); - - const providerId = await this._getProvider(id, params.providerId); - - // init workspace & register block schema - const workspace = new Workspace({ room: id }).register(BlockSchema); - - const Provider = this._providers.get(providerId); - assert(Provider); - - // initial configurator - const config = getKVConfigure(`workspace:${id}`); - // set workspace configs - const values = Object.entries(params.config || {}); - if (values.length) await config.setMany(values); - - // init data by provider - const provider = new Provider(); - await provider.init({ - apis: this._apis, - config, - debug: this._logger.enabled, - logger: this._logger.extend(`${Provider.id}:${id}`), - signals: this.signals, - workspace, - }); - await provider.initData(); - this._logger(`Workspace ${id} loaded`); - - return provider; + setMainProvider(providerId: string) { + this._mainProvider = this.providerMap.get(providerId); } - async auth(providerId: string, globalConfig?: Record) { - const Provider = this._providers.get(providerId); - if (Provider) { - // initial configurator - const config = getKVConfigure(`provider:${providerId}`); - // set workspace configs - const values = Object.entries(globalConfig || {}); - if (values.length) await config.setMany(values); + get providers() { + return Array.from(this.providerMap.values()); + } - const logger = this._logger.extend(`auth:${providerId}`); - logger.enabled = this._logger.enabled; - await Provider.auth(config, logger, this.signals); - } + public get workspaces() { + return this._workspaces.workspaces; + } + + public async refreshWorkspaces() { + return Promise.allSettled( + Object.values(this.providerMap).map(provider => provider.loadWorkspaces()) + ); } /** - * load workspace data to memory - * @param workspaceId workspace id - * @param config.providerId provider id - * @param config.config provider config - * @returns Workspace instance + * create new workspace , new workspace is a local workspace + * @param {string} name workspace name + * @returns {Promise} */ - async load( - workspaceId: string, - params: LoadConfig = {} - ): Promise { - if (workspaceId) { - if (!this._workspaces.has(workspaceId)) { - this._workspaces.set( - workspaceId, - this._getWorkspace(workspaceId, params) - ); - } - const workspace = this._workspaces.get(workspaceId); - assert(workspace); - return workspace.then(w => w.workspace); - } - return null; + public async createWorkspace(workspaceMeta: WorkspaceMeta) { + assert( + this._mainProvider, + 'There is no provider. You should add provider first.' + ); + + const workspace = await this._mainProvider.createWorkspace(workspaceMeta); + return workspace; } /** - * destroy workspace's instance in memory - * @param workspaceId workspace id + * delete workspace by id + * @param {string} workspaceId workspace id */ - async destroy(workspaceId: string) { - const provider = await this._workspaces.get(workspaceId); + public async deleteWorkspace(workspaceId: string) { + const workspaceInfo = this._workspaces.find(workspaceId); + assert(workspaceInfo, 'Workspace not found'); + const provider = this.providerMap.get(workspaceInfo.provider); + assert(provider, `Workspace exists, but we couldn't find its provider.`); + await provider.deleteWorkspace(workspaceId); + } + + /** + * get a new workspace only has room id + * @param {string} workspaceId workspace id + */ + private _getWorkspace(workspaceId: string) { + return new Workspace({ + room: workspaceId, + }).register(BlockSchema); + } + + /** + * login to all providers, it will default run all auth , + * maybe need a params to control which provider to auth + */ + public async login() { + this.providers.forEach(p => { + // TODO: may be add params of auth + p.auth(); + }); + } + + /** + * logout from all providers + */ + public async logout() { + this.providers.forEach(p => { + p.logout(); + }); + } + + /** + * load workspace instance by id + * @param {string} workspaceId workspace id + * @returns {Promise} + */ + public async loadWorkspace(workspaceId: string) { + const workspaceInfo = this._workspaces.find(workspaceId); + assert(workspaceInfo, 'Workspace not found'); + const currentProvider = this.providerMap.get(workspaceInfo.provider); + if (currentProvider) { + currentProvider.closeWorkspace(workspaceId); + } + const provider = this.providerMap.get(workspaceInfo.provider); + assert(provider, `provide '${workspaceInfo.provider}' is not registered`); + this._logger(`Loading ${workspaceInfo.provider} workspace: `, workspaceId); + + return await provider.warpWorkspace(this._getWorkspace(workspaceId)); + } + + /** + * get user info by provider id + * @param {string} providerId the provider name of workspace + * @returns {Promise} + */ + public async getUserInfo(providerId = 'affine') { + // XXX: maybe return all user info + const provider = this.providerMap.get(providerId); + assert(provider, `provide '${providerId}' is not registered`); + return provider.getUserInfo(); + } + + /** + * listen workspaces list change + * @param {Function} callback callback function + */ + public async onWorkspacesChange( + callback: (workspaces: WorkspacesChangeEvent) => void + ) { + this._workspaces.on('change', callback); + } + + /** + * change workspaces meta + * @param {WorkspaceMeta} workspaceMeta workspace meta + * @param {Workspace} workspace workspace instance + */ + public async resetWorkspaceMeta( + { name, avatar }: WorkspaceMeta, + workspace: Workspace + ) { + assert(workspace?.room, 'No workspace to set meta'); + const update: Partial = {}; + if (name) { + workspace.doc.meta.setName(name); + update.name = name; + } + if (avatar) { + workspace.doc.meta.setAvatar(avatar); + update.avatar = avatar; + } + // may run for change workspace meta + const workspaceInfo = this._workspaces.find(workspace.room); + assert(workspaceInfo, 'Workspace not found'); + const provider = this.providerMap.get(workspaceInfo.provider); + provider?.updateWorkspaceMeta(workspace.room, update); + } + + /** + * + * leave workspace by id + * @param id workspace id + */ + public async leaveWorkspace(workspaceId: string) { + const workspaceInfo = this._workspaces.find(workspaceId); + assert(workspaceInfo, 'Workspace not found'); + const provider = this.providerMap.get(workspaceInfo.provider); if (provider) { - this._workspaces.delete(workspaceId); - await provider.destroy(); + await provider.closeWorkspace(workspaceId); + await provider.leaveWorkspace(workspaceId); + } + } + + public async setWorkspacePublish(workspaceId: string, isPublish: boolean) { + const workspaceInfo = this._workspaces.find(workspaceId); + assert(workspaceInfo, 'Workspace not found'); + const provider = this.providerMap.get(workspaceInfo.provider); + if (provider) { + await provider.publish(workspaceId, isPublish); + } + } + + public async inviteMember(id: string, email: string) { + const workspaceInfo = this._workspaces.find(id); + assert(workspaceInfo, 'Workspace not found'); + const provider = this.providerMap.get(workspaceInfo.provider); + if (provider) { + await provider.invite(id, email); } } /** - * reload new workspace instance to memory to refresh config - * @param workspaceId workspace id - * @param config.providerId provider id - * @param config.config provider config - * @returns Workspace instance + * remove the new member to the workspace + * @param {number} permissionId permission id */ - async reload( - workspaceId: string, - config: LoadConfig = {} - ): Promise { - await this.destroy(workspaceId); - return this.load(workspaceId, config); - } - - /** - * get workspace list,return a map of workspace id and data state - * data state is also map, the key is the provider id, and the data exists locally when the value is true, otherwise it does not exist - */ - async list(): Promise>> { - const entries: [string, WorkspaceItem][] = await this._config.entries(); - return entries.reduce((acc, [k, i]) => { - if (k.startsWith('list:')) { - const key = k.slice(5); - acc[key] = acc[key] || {}; - acc[key][i.provider] = i.locally; - } - return acc; - }, {} as Record>); - } - - /** - * delete local workspace's data - * @param workspaceId workspace id - */ - async delete(workspaceId: string) { - await this._config.delete(`${workspaceId}:provider`); - const provider = await this._workspaces.get(workspaceId); + public async removeMember(workspaceId: string, permissionId: number) { + const workspaceInfo = this._workspaces.find(workspaceId); + assert(workspaceInfo, 'Workspace not found'); + const provider = this.providerMap.get(workspaceInfo.provider); if (provider) { - this._workspaces.delete(workspaceId); - // clear workspace data implement by provider + await provider.removeMember(permissionId); + } + } + + private async _transWorkspaceProvider( + workspace: Workspace, + providerId: string + ) { + assert(workspace.room, 'No workspace id'); + const workspaceInfo = this._workspaces.find(workspace.room); + assert(workspaceInfo, 'Workspace not found'); + if (workspaceInfo.provider === providerId) { + this._logger('Workspace provider is same'); + return; + } + const currentProvider = this.providerMap.get(workspaceInfo.provider); + assert(currentProvider, 'Provider not found'); + const newProvider = this.providerMap.get(providerId); + assert(newProvider, `provide '${providerId}' is not registered`); + this._logger(`create ${providerId} workspace: `, workspaceInfo.name); + const newWorkspace = await newProvider.createWorkspace({ + name: workspaceInfo.name, + avatar: workspaceInfo.avatar, + }); + assert(newWorkspace, 'Create workspace failed'); + this._logger( + `update workspace data from ${workspaceInfo.provider} to ${providerId}` + ); + applyUpdate(newWorkspace.doc, encodeStateAsUpdate(workspace.doc)); + assert(newWorkspace, 'Create workspace failed'); + await currentProvider.deleteWorkspace(workspace.room); + } + + /** + * Enable workspace cloud + * @param {string} id ID of workspace. + */ + public async enableWorkspaceCloud(workspace: Workspace) { + assert(workspace?.room, 'No workspace to enable cloud'); + return await this._transWorkspaceProvider(workspace, 'affine'); + } + + /** + * @deprecated + * clear all workspaces and data + */ + public async clear() { + for (const provider of this.providerMap.values()) { await provider.clear(); } } /** - * clear all local workspace's data + * Select a file to import the workspace + * @param {File} file file of workspace. */ - async clear() { - const workspaces = await this.list(); - await Promise.all(Object.keys(workspaces).map(id => this.delete(id))); + public async importWorkspace(file: File) { + file; + return; + } + + /** + * Generate a file ,and export it to local file system + * @param {string} id ID of workspace. + */ + public async exportWorkspace(id: string) { + id; + return; } } diff --git a/packages/data-center/src/index.ts b/packages/data-center/src/index.ts index 0ca8f1cfee..6fbb0e45db 100644 --- a/packages/data-center/src/index.ts +++ b/packages/data-center/src/index.ts @@ -1,5 +1,4 @@ -import debug from 'debug'; -import { DataCenter } from './datacenter.js'; +import { DataCenter } from './datacenter'; const _initializeDataCenter = () => { let _dataCenterInstance: Promise; @@ -26,11 +25,6 @@ const _initializeDataCenter = () => { export const getDataCenter = _initializeDataCenter(); -export function getLogger(namespace: string) { - const logger = debug(namespace); - logger.log = console.log.bind(console); - return logger; -} - -export type { AccessTokenMessage, Member, Workspace } from './apis'; -export { WorkspaceType } from './apis/index.js'; +export type { AccessTokenMessage } from './provider/affine/apis'; +export type { Workspace } from './types'; +export { getLogger } from './logger'; diff --git a/packages/data-center/src/logger.ts b/packages/data-center/src/logger.ts new file mode 100644 index 0000000000..83d68b932e --- /dev/null +++ b/packages/data-center/src/logger.ts @@ -0,0 +1,7 @@ +import debug from 'debug'; + +export function getLogger(namespace: string) { + const logger = debug(namespace); + logger.log = console.log.bind(console); + return logger; +} diff --git a/packages/data-center/src/provider/affine/affine.ts b/packages/data-center/src/provider/affine/affine.ts new file mode 100644 index 0000000000..b9f168a328 --- /dev/null +++ b/packages/data-center/src/provider/affine/affine.ts @@ -0,0 +1,280 @@ +import { + getWorkspaces, + getWorkspaceDetail, + WorkspaceDetail, + downloadWorkspace, + deleteWorkspace, + leaveWorkspace, + inviteMember, + removeMember, + createWorkspace, + updateWorkspace, +} from './apis/workspace'; +import { BaseProvider } from '../base'; +import type { ProviderConstructorParams } from '../base'; +import { User, Workspace as WS, WorkspaceMeta } from '../../types'; +import { Workspace } from '@blocksuite/store'; +import { BlockSchema } from '@blocksuite/blocks/models'; +import { applyUpdate } from 'yjs'; +import { token, Callback } from './apis'; +import { varStorage as storage } from 'lib0/storage'; +import assert from 'assert'; +import { getAuthorizer } from './apis/token'; +import { WebsocketProvider } from './sync'; +import { IndexedDBProvider } from '../indexeddb'; +import { getDefaultHeadImgBlob } from '../../utils'; + +export class AffineProvider extends BaseProvider { + public id = 'affine'; + private _workspacesCache: Map = new Map(); + private _onTokenRefresh?: Callback = undefined; + private readonly _authorizer = getAuthorizer(); + private _user: User | undefined = undefined; + private _wsMap: Map = new Map(); + private _idbMap: Map = new Map(); + + constructor(params: ProviderConstructorParams) { + super(params); + } + + override async init() { + this._onTokenRefresh = () => { + if (token.refresh) { + storage.setItem('token', token.refresh); + } + }; + + token.onChange(this._onTokenRefresh); + + // initial login token + if (token.isExpired) { + try { + const refreshToken = storage.getItem('token'); + await token.refreshToken(refreshToken); + + if (token.refresh) { + storage.set('token', token.refresh); + } + + assert(token.isLogin); + } catch (_) { + // this._logger('Authorization failed, fallback to local mode'); + } + } else { + storage.setItem('token', token.refresh); + } + } + + override async warpWorkspace(workspace: Workspace) { + const { doc, room } = workspace; + assert(room); + this._initWorkspaceDb(workspace); + const updates = await downloadWorkspace(room); + if (updates) { + await new Promise(resolve => { + doc.once('update', resolve); + applyUpdate(doc, new Uint8Array(updates)); + }); + } + const ws = new WebsocketProvider('/', room, doc); + this._wsMap.set(room, ws); + await new Promise((resolve, reject) => { + // TODO: synced will also be triggered on reconnection after losing sync + // There needs to be an event mechanism to emit the synchronization state to the upper layer + assert(ws); + ws.once('synced', () => resolve()); + ws.once('lost-connection', () => resolve()); + ws.once('connection-error', () => reject()); + }); + return workspace; + } + + override async loadWorkspaces() { + if (!token.isLogin) { + return []; + } + const workspacesList = await getWorkspaces(); + const workspaces: WS[] = workspacesList.map(w => { + return { + ...w, + memberCount: 0, + name: '', + provider: 'affine', + }; + }); + const workspaceInstances = workspaces.map(({ id }) => { + const workspace = + this._workspacesCache.get(id) || + new Workspace({ + room: id, + }).register(BlockSchema); + this._workspacesCache.set(id, workspace); + if (workspace) { + return new Promise(resolve => { + downloadWorkspace(id).then(data => { + applyUpdate(workspace.doc, new Uint8Array(data)); + resolve(workspace); + }); + }); + } else { + return Promise.resolve(null); + } + }); + + (await Promise.all(workspaceInstances)).forEach((workspace, i) => { + if (workspace) { + workspaces[i] = { + ...workspaces[i], + name: workspace.doc.meta.name, + avatar: workspace.doc.meta.avatar, + }; + } + }); + const getDetailList = workspacesList.map(w => { + const { id } = w; + return new Promise<{ id: string; detail: WorkspaceDetail | null }>( + resolve => { + getWorkspaceDetail({ id }).then(data => { + resolve({ id, detail: data || null }); + }); + } + ); + }); + const ownerList = await Promise.all(getDetailList); + (await Promise.all(ownerList)).forEach(detail => { + if (detail) { + const { id, detail: workspaceDetail } = detail; + if (workspaceDetail) { + const { owner, member_count } = workspaceDetail; + const currentWorkspace = workspaces.find(w => w.id === id); + if (currentWorkspace) { + currentWorkspace.owner = { + id: owner.id, + name: owner.name, + avatar: owner.avatar_url, + email: owner.email, + }; + currentWorkspace.memberCount = member_count; + } + } + } + }); + + workspaces.forEach(workspace => { + this._workspaces.add(workspace); + }); + + return workspaces; + } + + override async auth() { + const refreshToken = await storage.getItem('token'); + if (refreshToken) { + await token.refreshToken(refreshToken); + if (token.isLogin && !token.isExpired) { + // login success + return; + } + } + const user = await this._authorizer[0]?.(); + assert(user); + this._user = { + id: user.id, + name: user.name, + avatar: user.avatar_url, + email: user.email, + }; + } + + public override async getUserInfo(): Promise { + return this._user; + } + + public override async deleteWorkspace(id: string): Promise { + await this.closeWorkspace(id); + IndexedDBProvider.delete(id); + await deleteWorkspace({ id }); + this._workspaces.remove(id); + } + + public override async clear(): Promise { + for (const w of this._workspacesCache.values()) { + if (w.room) { + try { + await this.deleteWorkspace(w.room); + this._workspaces.remove(w.room); + } catch (e) { + this._logger('has a problem of delete workspace ', e); + } + } + } + this._workspacesCache.clear(); + } + + public override async closeWorkspace(id: string) { + const idb = this._idbMap.get(id); + idb?.destroy(); + const ws = this._wsMap.get(id); + ws?.disconnect(); + } + + public override async leaveWorkspace(id: string): Promise { + await leaveWorkspace({ id }); + } + + public override async invite(id: string, email: string): Promise { + return await inviteMember({ id, email }); + } + + public override async removeMember(permissionId: number): Promise { + return await removeMember({ permissionId }); + } + + private async _initWorkspaceDb(workspace: Workspace) { + assert(workspace.room); + let idb = this._idbMap.get(workspace.room); + idb?.destroy(); + idb = new IndexedDBProvider(workspace.room, workspace.doc); + this._idbMap.set(workspace.room, idb); + await idb.whenSynced; + this._logger('Local data loaded'); + return idb; + } + + public override async createWorkspace( + meta: WorkspaceMeta + ): Promise { + assert(meta.name, 'Workspace name is required'); + if (!meta.avatar) { + // set default avatar + const blob = await getDefaultHeadImgBlob(meta.name); + meta.avatar = (await this.setBlob(blob)) || ''; + } + const { id } = await createWorkspace(meta as Required); + this._logger('Creating affine workspace'); + const nw = new Workspace({ + room: id, + }).register(BlockSchema); + nw.meta.setName(meta.name); + nw.meta.setAvatar(meta.avatar); + this._initWorkspaceDb(nw); + + const workspaceInfo: WS = { + name: meta.name, + id, + isPublish: false, + avatar: '', + owner: undefined, + isLocal: true, + memberCount: 1, + provider: 'local', + }; + + this._workspaces.add(workspaceInfo); + return nw; + } + + public override async publish(id: string, isPublish: boolean): Promise { + await updateWorkspace({ id, public: isPublish }); + } +} diff --git a/packages/data-center/src/apis/index.ts b/packages/data-center/src/provider/affine/apis/index.ts similarity index 78% rename from packages/data-center/src/apis/index.ts rename to packages/data-center/src/provider/affine/apis/index.ts index de62a9fcb1..57c003d8fd 100644 --- a/packages/data-center/src/apis/index.ts +++ b/packages/data-center/src/provider/affine/apis/index.ts @@ -4,16 +4,24 @@ export type { Callback } from './token.js'; import { getAuthorizer } from './token.js'; import * as user from './user.js'; import * as workspace from './workspace.js'; +import * as business from './business.js'; export type Apis = typeof user & typeof workspace & { + business: typeof business; signInWithGoogle: ReturnType[0]; onAuthStateChanged: ReturnType[1]; }; export const getApis = (): Apis => { const [signInWithGoogle, onAuthStateChanged] = getAuthorizer(); - return { ...user, ...workspace, signInWithGoogle, onAuthStateChanged }; + return { + ...user, + ...workspace, + business, + signInWithGoogle, + onAuthStateChanged, + }; }; export type { AccessTokenMessage } from './token'; diff --git a/packages/data-center/src/apis/request.ts b/packages/data-center/src/provider/affine/apis/request.ts similarity index 100% rename from packages/data-center/src/apis/request.ts rename to packages/data-center/src/provider/affine/apis/request.ts diff --git a/packages/data-center/src/apis/token.ts b/packages/data-center/src/provider/affine/apis/token.ts similarity index 94% rename from packages/data-center/src/apis/token.ts rename to packages/data-center/src/provider/affine/apis/token.ts index c509da0f33..d7a2c34f87 100644 --- a/packages/data-center/src/apis/token.ts +++ b/packages/data-center/src/provider/affine/apis/token.ts @@ -2,8 +2,8 @@ import { initializeApp } from 'firebase/app'; import { getAuth, GoogleAuthProvider, signInWithPopup } from 'firebase/auth'; import type { User } from 'firebase/auth'; -import { getLogger } from '../index.js'; -import { bareClient } from './request.js'; +import { getLogger } from '../../../logger'; +import { bareClient } from './request'; export interface AccessTokenMessage { create_at: number; @@ -168,14 +168,15 @@ export const getAuthorizer = () => { const signInWithGoogle = async () => { const idToken = await getToken(); + let loginUser: AccessTokenMessage | null = null; if (idToken) { - await token.initToken(idToken); + loginUser = await token.initToken(idToken); } else { const user = await signInWithPopup(firebaseAuth, googleAuthProvider); const idToken = await user.user.getIdToken(); - await token.initToken(idToken); + loginUser = await token.initToken(idToken); } - return firebaseAuth.currentUser; + return loginUser; }; const onAuthStateChanged = (callback: (user: User | null) => void) => { diff --git a/packages/data-center/src/apis/user.ts b/packages/data-center/src/provider/affine/apis/user.ts similarity index 100% rename from packages/data-center/src/apis/user.ts rename to packages/data-center/src/provider/affine/apis/user.ts diff --git a/packages/data-center/src/apis/workspace.ts b/packages/data-center/src/provider/affine/apis/workspace.ts similarity index 99% rename from packages/data-center/src/apis/workspace.ts rename to packages/data-center/src/provider/affine/apis/workspace.ts index 396aa46f8e..0c145928d5 100644 --- a/packages/data-center/src/apis/workspace.ts +++ b/packages/data-center/src/provider/affine/apis/workspace.ts @@ -84,7 +84,7 @@ export interface CreateWorkspaceParams { export async function createWorkspace( params: CreateWorkspaceParams -): Promise { +): Promise<{ id: string }> { return client.post('api/workspace', { json: params }).json(); } diff --git a/packages/data-center/src/provider/affine/index.ts b/packages/data-center/src/provider/affine/index.ts index d2323f4ae1..8f9dc079c0 100644 --- a/packages/data-center/src/provider/affine/index.ts +++ b/packages/data-center/src/provider/affine/index.ts @@ -1,175 +1 @@ -import assert from 'assert'; -import { applyUpdate, Doc } from 'yjs'; - -import type { - ConfigStore, - DataCenterSignals, - InitialParams, - Logger, -} from '../index.js'; -import { token, Callback, getApis } from '../../apis/index.js'; -import { LocalProvider } from '../local/index.js'; - -import { WebsocketProvider } from './sync.js'; -import { IndexedDBProvider } from '../local/indexeddb.js'; - -export class AffineProvider extends LocalProvider { - static id = 'affine'; - private _onTokenRefresh?: Callback = undefined; - private _ws?: WebsocketProvider; - - constructor() { - super(); - } - - async init(params: InitialParams) { - super.init(params); - - this._onTokenRefresh = () => { - if (token.refresh) { - this._config.set('token', token.refresh); - } - }; - assert(this._onTokenRefresh); - - token.onChange(this._onTokenRefresh); - - // initial login token - if (token.isExpired) { - try { - const refreshToken = await this._config.get('token'); - await token.refreshToken(refreshToken); - - if (token.refresh) { - this._config.set('token', token.refresh); - } - - assert(token.isLogin); - } catch (_) { - this._logger('Authorization failed, fallback to local mode'); - } - } else { - this._config.set('token', token.refresh); - } - } - - async destroy() { - if (this._onTokenRefresh) { - token.offChange(this._onTokenRefresh); - } - this._ws?.disconnect(); - } - - async initData() { - const databases = await indexedDB.databases(); - await super.initData( - // set locally to true if exists a same name db - databases - .map(db => db.name) - .filter(v => v) - .includes(this._workspace.room) - ); - - const workspace = this._workspace; - const doc = workspace.doc; - - this._logger(`Login: ${token.isLogin}`); - - if (workspace.room && token.isLogin) { - try { - // init data from cloud - await AffineProvider._initCloudDoc( - workspace.room, - doc, - this._logger, - this._signals - ); - - // Wait for ws synchronization to complete, otherwise the data will be modified in reverse, which can be optimized later - this._ws = new WebsocketProvider('/', workspace.room, doc); - await new Promise((resolve, reject) => { - // TODO: synced will also be triggered on reconnection after losing sync - // There needs to be an event mechanism to emit the synchronization state to the upper layer - assert(this._ws); - this._ws.once('synced', () => resolve()); - this._ws.once('lost-connection', () => resolve()); - this._ws.once('connection-error', () => reject()); - }); - this._signals.listAdd.emit({ - workspace: workspace.room, - provider: this.id, - locally: true, - }); - } catch (e) { - this._logger('Failed to init cloud workspace', e); - } - } - - // if after update, the space:meta is empty - // then we need to get map with doc - // just a workaround for yjs - doc.getMap('space:meta'); - } - - private static async _initCloudDoc( - workspace: string, - doc: Doc, - logger: Logger, - signals: DataCenterSignals - ) { - const apis = getApis(); - logger(`Loading ${workspace}...`); - const updates = await apis.downloadWorkspace(workspace); - if (updates) { - await new Promise(resolve => { - doc.once('update', resolve); - applyUpdate(doc, new Uint8Array(updates)); - }); - logger(`Loaded: ${workspace}`); - - // only add to list as online workspace - signals.listAdd.emit({ - workspace, - provider: this.id, - // at this time we always download full workspace - // but after we support sub doc, we can only download metadata - locally: false, - }); - } - } - - static async auth( - config: Readonly>, - logger: Logger, - signals: DataCenterSignals - ) { - const refreshToken = await config.get('token'); - if (refreshToken) { - await token.refreshToken(refreshToken); - if (token.isLogin && !token.isExpired) { - logger('check login success'); - // login success - return; - } - } - - logger('start login'); - // login with google - const apis = getApis(); - assert(apis.signInWithGoogle); - const user = await apis.signInWithGoogle(); - assert(user); - logger(`login success: ${user.displayName}`); - - // TODO: refresh local workspace data - const workspaces = await apis.getWorkspaces(); - await Promise.all( - workspaces.map(async ({ id }) => { - const doc = new Doc(); - const idb = new IndexedDBProvider(id, doc); - await idb.whenSynced; - await this._initCloudDoc(id, doc, logger, signals); - }) - ); - } -} +export * from './affine'; diff --git a/packages/data-center/src/provider/base.ts b/packages/data-center/src/provider/base.ts index bd8d8a4e03..40572a136a 100644 --- a/packages/data-center/src/provider/base.ts +++ b/packages/data-center/src/provider/base.ts @@ -1,79 +1,158 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import type { Workspace } from '@blocksuite/store'; +import { BlobStorage, Workspace } from '@blocksuite/store'; +import { Logger, User, Workspace as WS, WorkspaceMeta } from '../types'; +import type { WorkspacesScope } from '../workspaces'; -import type { - Apis, - DataCenterSignals, - Logger, - InitialParams, - ConfigStore, -} from './index'; +const defaultLogger = () => { + return; +}; + +export interface ProviderConstructorParams { + logger?: Logger; + workspaces: WorkspacesScope; +} export class BaseProvider { - static id = 'base'; - protected _apis!: Readonly; - protected _config!: Readonly; + public readonly id: string = 'base'; + protected _workspaces!: WorkspacesScope; protected _logger!: Logger; - protected _signals!: DataCenterSignals; - protected _workspace!: Workspace; + protected _blobs!: BlobStorage; - constructor() { - // Nothing to do here + public constructor({ logger, workspaces }: ProviderConstructorParams) { + this._logger = (logger || defaultLogger) as Logger; + this._workspaces = workspaces; } - get id(): string { - return (this.constructor as any).id; + /** + * hook after provider registered + */ + public async init() { + return; } - async init(params: InitialParams) { - this._apis = params.apis; - this._config = params.config; - this._logger = params.logger; - this._signals = params.signals; - this._workspace = params.workspace; - this._logger.enabled = params.debug; + /** + * auth provider + */ + public async auth() { + return; } + /** + * logout provider + */ + public async logout() { + return; + } + + /** + * warp workspace with provider functions + * @param workspace + * @returns + */ + public async warpWorkspace(workspace: Workspace): Promise { + return workspace; + } + + /** + * load workspaces + **/ + public async loadWorkspaces(): Promise { + throw new Error(`provider: ${this.id} loadWorkSpace Not implemented`); + } + + /** + * get auth user info + * @returns + */ + public async getUserInfo(): Promise { + return; + } + + async getBlob(id: string): Promise { + return await this._blobs.get(id); + } + + async setBlob(blob: Blob): Promise { + return await this._blobs.set(blob); + } + + /** + * clear all local data in provider + */ async clear() { - await this.destroy(); - await this._config.clear(); + this._blobs.clear(); } - async destroy() { - // Nothing to do here + /** + * delete workspace include all data + * @param id workspace id + */ + public async deleteWorkspace(id: string): Promise { + id; + return; } - async initData() { - throw Error('Not implemented: initData'); + /** + * leave workspace by workspace id + * @param id workspace id + */ + public async leaveWorkspace(id: string): Promise { + id; + return; } - // should return a blob url - async getBlob(_id: string): Promise { - throw Error('Not implemented: getBlob'); + /** + * close db link and websocket connection and other resources + * @param id workspace id + */ + public async closeWorkspace(id: string) { + id; + return; } - // should return a blob unique id - async setBlob(_blob: Blob): Promise { - throw Error('Not implemented: setBlob'); + /** + * invite workspace member + * @param id workspace id + */ + public async invite(id: string, email: string): Promise { + id; + email; + return; } - get workspace() { - return this._workspace; + /** + * remove workspace member by permission id + * @param permissionId + */ + public async removeMember(permissionId: number): Promise { + permissionId; + return; } - static async auth( - _config: Readonly, - logger: Logger, - _signals: DataCenterSignals - ) { - logger("This provider doesn't require authentication"); + public async publish(id: string, isPublish: boolean): Promise { + id; + isPublish; + return; } - // get workspace list,return a map of workspace id and boolean - // if value is true, it exists locally, otherwise it does not exist locally - static async list( - _config: Readonly - ): Promise | undefined> { - throw Error('Not implemented: list'); + /** + * change workspace meta by workspace id , work for cached list in different provider + * @param id + * @param meta + * @returns + */ + public async updateWorkspaceMeta( + id: string, + meta: Partial + ): Promise { + id; + meta; + return; + } + + public async createWorkspace( + meta: WorkspaceMeta + ): Promise { + meta; + return; } } diff --git a/packages/data-center/src/provider/index.ts b/packages/data-center/src/provider/index.ts index 5718b7eef0..abfae4ce71 100644 --- a/packages/data-center/src/provider/index.ts +++ b/packages/data-center/src/provider/index.ts @@ -1,22 +1 @@ -import type { Workspace } from '@blocksuite/store'; - -import type { Apis } from '../apis'; -import type { DataCenterSignals } from '../datacenter'; -import type { getLogger } from '../index'; -import type { ConfigStore } from '../store'; - -export type Logger = ReturnType; - -export type InitialParams = { - apis: Apis; - config: Readonly; - debug: boolean; - logger: Logger; - signals: DataCenterSignals; - workspace: Workspace; -}; - -export type { Apis, ConfigStore, DataCenterSignals, Workspace }; -export type { BaseProvider } from './base.js'; -export { AffineProvider } from './affine/index.js'; -export { LocalProvider } from './local/index.js'; +export * from './affine/affine'; diff --git a/packages/data-center/src/provider/local/indexeddb.ts b/packages/data-center/src/provider/indexeddb.ts similarity index 98% rename from packages/data-center/src/provider/local/indexeddb.ts rename to packages/data-center/src/provider/indexeddb.ts index db7fab29d7..bece021b13 100644 --- a/packages/data-center/src/provider/local/indexeddb.ts +++ b/packages/data-center/src/provider/indexeddb.ts @@ -196,4 +196,8 @@ export class IndexedDBProvider extends Observable { return undefined; }); } + + static delete(name: string): Promise { + return idb.deleteDB(name); + } } diff --git a/packages/data-center/src/provider/local/index.ts b/packages/data-center/src/provider/local/index.ts index 24bf9584a7..0646cef75a 100644 --- a/packages/data-center/src/provider/local/index.ts +++ b/packages/data-center/src/provider/local/index.ts @@ -1,73 +1 @@ -import type { BlobStorage } from '@blocksuite/store'; -import assert from 'assert'; - -import type { ConfigStore, InitialParams } from '../index.js'; -import { BaseProvider } from '../base.js'; -import { IndexedDBProvider } from './indexeddb.js'; - -export class LocalProvider extends BaseProvider { - static id = 'local'; - private _blobs!: BlobStorage; - private _idb?: IndexedDBProvider = undefined; - - constructor() { - super(); - } - async init(params: InitialParams) { - super.init(params); - - const blobs = await this._workspace.blobs; - assert(blobs); - this._blobs = blobs; - } - - async initData(locally = true) { - assert(this._workspace.room); - this._logger('Loading local data'); - this._idb = new IndexedDBProvider( - this._workspace.room, - this._workspace.doc - ); - - await this._idb.whenSynced; - this._logger('Local data loaded'); - - this._signals.listAdd.emit({ - workspace: this._workspace.room, - provider: this.id, - locally, - }); - } - - async clear() { - assert(this._workspace.room); - await super.clear(); - await this._blobs.clear(); - await this._idb?.clearData(); - this._signals.listRemove.emit(this._workspace.room); - } - - async destroy(): Promise { - super.destroy(); - await this._idb?.destroy(); - } - - async getBlob(id: string): Promise { - return this._blobs.get(id); - } - - async setBlob(blob: Blob): Promise { - return this._blobs.set(blob); - } - - static async list( - config: Readonly> - ): Promise | undefined> { - const entries = await config.entries(); - return new Map( - entries - .filter(([key]) => key.startsWith('list:')) - .map(([key, value]) => [key.slice(5), value]) - ); - } -} +export * from './local'; diff --git a/packages/data-center/src/provider/local/local.spec.ts b/packages/data-center/src/provider/local/local.spec.ts new file mode 100644 index 0000000000..442263c070 --- /dev/null +++ b/packages/data-center/src/provider/local/local.spec.ts @@ -0,0 +1,49 @@ +import { describe, test, expect } from 'vitest'; +import { Workspaces } from '../../workspaces'; +import { LocalProvider } from './local'; +import 'fake-indexeddb/auto'; + +describe('local provider', () => { + const workspaces = new Workspaces(); + const provider = new LocalProvider({ + workspaces: workspaces.createScope(), + }); + + const workspaceName = 'workspace-test'; + let workspaceId: string | undefined; + + test('create workspace', async () => { + const w = await provider.createWorkspace({ + name: workspaceName, + avatar: 'avatar-url-test', + }); + workspaceId = w?.room; + + expect(workspaces.workspaces.length).toEqual(1); + expect(workspaces.workspaces[0].name).toEqual(workspaceName); + }); + + test('workspace list cache', async () => { + const workspaces1 = new Workspaces(); + const provider1 = new LocalProvider({ + workspaces: workspaces1.createScope(), + }); + await provider1.loadWorkspaces(); + expect(workspaces1.workspaces.length).toEqual(1); + expect(workspaces1.workspaces[0].name).toEqual(workspaceName); + expect(workspaces1.workspaces[0].id).toEqual(workspaceId); + }); + + test('update workspace', async () => { + await provider.updateWorkspaceMeta(workspaceId!, { + name: '1111', + }); + expect(workspaces.workspaces[0].name).toEqual('1111'); + }); + + test('delete workspace', async () => { + expect(workspaces.workspaces.length).toEqual(1); + await provider.deleteWorkspace(workspaces.workspaces[0].id); + expect(workspaces.workspaces.length).toEqual(0); + }); +}); diff --git a/packages/data-center/src/provider/local/local.ts b/packages/data-center/src/provider/local/local.ts new file mode 100644 index 0000000000..a01d516a35 --- /dev/null +++ b/packages/data-center/src/provider/local/local.ts @@ -0,0 +1,117 @@ +import { BaseProvider } from '../base'; +import type { ProviderConstructorParams } from '../base'; +import { varStorage as storage } from 'lib0/storage'; +import { Workspace as WS, WorkspaceMeta } from '../../types'; +import { Workspace, uuidv4 } from '@blocksuite/store'; +import { IndexedDBProvider } from '../indexeddb'; +import assert from 'assert'; +import { getDefaultHeadImgBlob } from '../../utils'; + +const WORKSPACE_KEY = 'workspaces'; + +export class LocalProvider extends BaseProvider { + public id = 'local'; + private _idbMap: Map = new Map(); + + constructor(params: ProviderConstructorParams) { + super(params); + this.loadWorkspaces(); + } + + private _storeWorkspaces(workspaces: WS[]) { + storage.setItem(WORKSPACE_KEY, JSON.stringify(workspaces)); + } + + private async _initWorkspaceDb(workspace: Workspace) { + assert(workspace.room); + let idb = this._idbMap.get(workspace.room); + idb?.destroy(); + idb = new IndexedDBProvider(workspace.room, workspace.doc); + this._idbMap.set(workspace.room, idb); + this._logger('Local data loaded'); + return idb; + } + + public override async warpWorkspace( + workspace: Workspace + ): Promise { + assert(workspace.room); + await this._initWorkspaceDb(workspace); + return workspace; + } + + override loadWorkspaces(): Promise { + const workspaceStr = storage.getItem(WORKSPACE_KEY); + let workspaces: WS[] = []; + if (workspaceStr) { + try { + workspaces = JSON.parse(workspaceStr) as WS[]; + workspaces.forEach(workspace => { + this._workspaces.add(workspace); + }); + } catch (error) { + this._logger(`Failed to parse workspaces from storage`); + } + } + return Promise.resolve(workspaces); + } + + public override async deleteWorkspace(id: string): Promise { + const workspace = this._workspaces.get(id); + if (workspace) { + IndexedDBProvider.delete(id); + this._workspaces.remove(id); + this._storeWorkspaces(this._workspaces.list()); + } else { + this._logger(`Failed to delete workspace ${id}`); + } + } + + public override async updateWorkspaceMeta( + id: string, + meta: Partial + ) { + this._workspaces.update(id, meta); + this._storeWorkspaces(this._workspaces.list()); + } + + public override async createWorkspace( + meta: WorkspaceMeta + ): Promise { + assert(meta.name, 'Workspace name is required'); + if (!meta.avatar) { + // set default avatar + const blob = await getDefaultHeadImgBlob(meta.name); + meta.avatar = (await this.setBlob(blob)) || ''; + } + this._logger('Creating affine workspace'); + + const workspaceInfo: WS = { + name: meta.name, + id: uuidv4(), + isPublish: false, + avatar: '', + owner: undefined, + isLocal: true, + memberCount: 1, + provider: 'local', + }; + + const workspace = new Workspace({ room: workspaceInfo.id }); + this._initWorkspaceDb(workspace); + workspace.meta.setName(meta.name); + workspace.meta.setAvatar(meta.avatar); + + this._workspaces.add(workspaceInfo); + this._storeWorkspaces(this._workspaces.list()); + + return workspace; + } + + public override async clear(): Promise { + const workspaces = await this.loadWorkspaces(); + workspaces.forEach(ws => IndexedDBProvider.delete(ws.id)); + this._storeWorkspaces([]); + this._workspaces.clear(); + } +} diff --git a/packages/data-center/src/provider/selfhosted/index.ts b/packages/data-center/src/provider/selfhosted/index.ts new file mode 100644 index 0000000000..48632480ef --- /dev/null +++ b/packages/data-center/src/provider/selfhosted/index.ts @@ -0,0 +1,63 @@ +import assert from 'assert'; + +import { LocalProvider } from '../local/index.js'; +import { WebsocketProvider } from './sync.js'; + +export class SelfHostedProvider extends LocalProvider { + static id = 'selfhosted'; + private _ws?: WebsocketProvider; + + constructor() { + super(); + } + + async destroy() { + this._ws?.disconnect(); + } + + async initData() { + const databases = await indexedDB.databases(); + await super.initData( + // set locally to true if exists a same name db + databases + .map(db => db.name) + .filter(v => v) + .includes(this._workspace.room) + ); + + const workspace = this._workspace; + const doc = workspace.doc; + + if (workspace.room) { + try { + // Wait for ws synchronization to complete, otherwise the data will be modified in reverse, which can be optimized later + this._ws = new WebsocketProvider(this.host, workspace.room, doc); + await new Promise((resolve, reject) => { + // TODO: synced will also be triggered on reconnection after losing sync + // There needs to be an event mechanism to emit the synchronization state to the upper layer + assert(this._ws); + this._ws.once('synced', () => resolve()); + this._ws.once('lost-connection', () => resolve()); + this._ws.once('connection-error', () => reject()); + }); + this._signals.listAdd.emit({ + workspace: workspace.room, + provider: this.id, + locally: true, + }); + } catch (e) { + this._logger('Failed to init cloud workspace', e); + } + } + + // if after update, the space:meta is empty + // then we need to get map with doc + // just a workaround for yjs + doc.getMap('space:meta'); + } + + private get host() { + const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:'; + return `${protocol}//${location.host}/collaboration/`; + } +} diff --git a/packages/data-center/src/provider/selfhosted/sync.js b/packages/data-center/src/provider/selfhosted/sync.js new file mode 100644 index 0000000000..54c8dec628 --- /dev/null +++ b/packages/data-center/src/provider/selfhosted/sync.js @@ -0,0 +1,508 @@ +/* eslint-disable no-undef */ +/** + * @module provider/websocket + */ + +/* eslint-env browser */ + +// import * as Y from 'yjs'; // eslint-disable-line +import * as bc from 'lib0/broadcastchannel'; +import * as time from 'lib0/time'; +import * as encoding from 'lib0/encoding'; +import * as decoding from 'lib0/decoding'; +import * as syncProtocol from 'y-protocols/sync'; +import * as authProtocol from 'y-protocols/auth'; +import * as awarenessProtocol from 'y-protocols/awareness'; +import { Observable } from 'lib0/observable'; +import * as math from 'lib0/math'; +import * as url from 'lib0/url'; + +export const messageSync = 0; +export const messageQueryAwareness = 3; +export const messageAwareness = 1; +export const messageAuth = 2; + +/** + * encoder, decoder, provider, emitSynced, messageType + * @type {Array} + */ +const messageHandlers = []; + +messageHandlers[messageSync] = ( + encoder, + decoder, + provider, + emitSynced, + _messageType +) => { + encoding.writeVarUint(encoder, messageSync); + const syncMessageType = syncProtocol.readSyncMessage( + decoder, + encoder, + provider.doc, + provider + ); + if ( + emitSynced && + syncMessageType === syncProtocol.messageYjsSyncStep2 && + !provider.synced + ) { + provider.synced = true; + } +}; + +messageHandlers[messageQueryAwareness] = ( + encoder, + _decoder, + provider, + _emitSynced, + _messageType +) => { + encoding.writeVarUint(encoder, messageAwareness); + encoding.writeVarUint8Array( + encoder, + awarenessProtocol.encodeAwarenessUpdate( + provider.awareness, + Array.from(provider.awareness.getStates().keys()) + ) + ); +}; + +messageHandlers[messageAwareness] = ( + _encoder, + decoder, + provider, + _emitSynced, + _messageType +) => { + awarenessProtocol.applyAwarenessUpdate( + provider.awareness, + decoding.readVarUint8Array(decoder), + provider + ); +}; + +messageHandlers[messageAuth] = ( + _encoder, + decoder, + provider, + _emitSynced, + _messageType +) => { + authProtocol.readAuthMessage(decoder, provider.doc, (_ydoc, reason) => + permissionDeniedHandler(provider, reason) + ); +}; + +// @todo - this should depend on awareness.outdatedTime +const messageReconnectTimeout = 30000; + +/** + * @param {WebsocketProvider} provider + * @param {string} reason + */ +const permissionDeniedHandler = (provider, reason) => + console.warn(`Permission denied to access ${provider.url}.\n${reason}`); + +/** + * @param {WebsocketProvider} provider + * @param {Uint8Array} buf + * @param {boolean} emitSynced + * @return {encoding.Encoder} + */ +const readMessage = (provider, buf, emitSynced) => { + const decoder = decoding.createDecoder(buf); + const encoder = encoding.createEncoder(); + const messageType = decoding.readVarUint(decoder); + const messageHandler = provider.messageHandlers[messageType]; + if (/** @type {any} */ (messageHandler)) { + messageHandler(encoder, decoder, provider, emitSynced, messageType); + } else { + console.error('Unable to compute message'); + } + return encoder; +}; + +/** + * @param {WebsocketProvider} provider + */ +const setupWS = provider => { + if (provider.shouldConnect && provider.ws === null) { + const websocket = new provider._WS(provider.url, 'AFFiNE'); + websocket.binaryType = 'arraybuffer'; + provider.ws = websocket; + provider.wsconnecting = true; + provider.wsconnected = false; + provider.synced = false; + + websocket.onmessage = event => { + provider.wsLastMessageReceived = time.getUnixTime(); + const encoder = readMessage(provider, new Uint8Array(event.data), true); + if (encoding.length(encoder) > 1) { + websocket.send(encoding.toUint8Array(encoder)); + } + }; + websocket.onerror = event => { + provider.emit('connection-error', [event, provider]); + }; + websocket.onclose = event => { + provider.emit('connection-close', [event, provider]); + provider.ws = null; + provider.wsconnecting = false; + if (provider.wsconnected) { + provider.wsconnected = false; + provider.synced = false; + // update awareness (all users except local left) + awarenessProtocol.removeAwarenessStates( + provider.awareness, + Array.from(provider.awareness.getStates().keys()).filter( + client => client !== provider.doc.clientID + ), + provider + ); + provider.emit('status', [ + { + status: 'disconnected', + }, + ]); + } else { + provider.wsUnsuccessfulReconnects++; + } + // Start with no reconnect timeout and increase timeout by + // using exponential backoff starting with 100ms + setTimeout( + setupWS, + math.min( + math.pow(2, provider.wsUnsuccessfulReconnects) * 100, + provider.maxBackoffTime + ), + provider + ); + }; + websocket.onopen = () => { + provider.wsLastMessageReceived = time.getUnixTime(); + provider.wsconnecting = false; + provider.wsconnected = true; + provider.wsUnsuccessfulReconnects = 0; + provider.emit('status', [ + { + status: 'connected', + }, + ]); + // always send sync step 1 when connected + const encoder = encoding.createEncoder(); + encoding.writeVarUint(encoder, messageSync); + syncProtocol.writeSyncStep1(encoder, provider.doc); + websocket.send(encoding.toUint8Array(encoder)); + // broadcast local awareness state + if (provider.awareness.getLocalState() !== null) { + const encoderAwarenessState = encoding.createEncoder(); + encoding.writeVarUint(encoderAwarenessState, messageAwareness); + encoding.writeVarUint8Array( + encoderAwarenessState, + awarenessProtocol.encodeAwarenessUpdate(provider.awareness, [ + provider.doc.clientID, + ]) + ); + websocket.send(encoding.toUint8Array(encoderAwarenessState)); + } + }; + + provider.emit('status', [ + { + status: 'connecting', + }, + ]); + } +}; + +/** + * @param {WebsocketProvider} provider + * @param {ArrayBuffer} buf + */ +const broadcastMessage = (provider, buf) => { + if (provider.wsconnected) { + /** @type {WebSocket} */ (provider.ws).send(buf); + } + if (provider.bcconnected) { + bc.publish(provider.bcChannel, buf, provider); + } +}; + +/** + * Websocket Provider for Yjs. Creates a websocket connection to sync the shared document. + * The document name is attached to the provided url. I.e. the following example + * creates a websocket connection to http://localhost:1234/my-document-name + * + * @example + * import * as Y from 'yjs' + * import { WebsocketProvider } from 'y-websocket' + * const doc = new Y.Doc() + * const provider = new WebsocketProvider('http://localhost:1234', 'my-document-name', doc) + * + * @extends {Observable} + */ +export class WebsocketProvider extends Observable { + /** + * @param {string} serverUrl + * @param {string} roomname + * @param {Y.Doc} doc + * @param {object} [opts] + * @param {boolean} [opts.connect] + * @param {awarenessProtocol.Awareness} [opts.awareness] + * @param {Object} [opts.params] + * @param {typeof WebSocket} [opts.WebSocketPolyfill] Optionall provide a WebSocket polyfill + * @param {number} [opts.resyncInterval] Request server state every `resyncInterval` milliseconds + * @param {number} [opts.maxBackoffTime] Maximum amount of time to wait before trying to reconnect (we try to reconnect using exponential backoff) + * @param {boolean} [opts.disableBc] Disable cross-tab BroadcastChannel communication + */ + constructor( + serverUrl, + roomname, + doc, + { + connect = true, + awareness = new awarenessProtocol.Awareness(doc), + params = {}, + WebSocketPolyfill = WebSocket, + resyncInterval = -1, + maxBackoffTime = 2500, + disableBc = false, + } = {} + ) { + super(); + // ensure that url is always ends with / + while (serverUrl[serverUrl.length - 1] === '/') { + serverUrl = serverUrl.slice(0, serverUrl.length - 1); + } + const encodedParams = url.encodeQueryParams(params); + this.maxBackoffTime = maxBackoffTime; + this.bcChannel = serverUrl + '/' + roomname; + this.url = + serverUrl + + '/' + + roomname + + (encodedParams.length === 0 ? '' : '?' + encodedParams); + this.roomname = roomname; + this.doc = doc; + this._WS = WebSocketPolyfill; + this.awareness = awareness; + this.wsconnected = false; + this.wsconnecting = false; + this.bcconnected = false; + this.disableBc = disableBc; + this.wsUnsuccessfulReconnects = 0; + this.messageHandlers = messageHandlers.slice(); + /** + * @type {boolean} + */ + this._synced = false; + /** + * @type {WebSocket?} + */ + this.ws = null; + this.wsLastMessageReceived = 0; + /** + * Whether to connect to other peers or not + * @type {boolean} + */ + this.shouldConnect = connect; + + /** + * @type {number} + */ + this._resyncInterval = 0; + if (resyncInterval > 0) { + this._resyncInterval = /** @type {any} */ ( + setInterval(() => { + if (this.ws && this.ws.readyState === WebSocket.OPEN) { + // resend sync step 1 + const encoder = encoding.createEncoder(); + encoding.writeVarUint(encoder, messageSync); + syncProtocol.writeSyncStep1(encoder, doc); + this.ws.send(encoding.toUint8Array(encoder)); + } + }, resyncInterval) + ); + } + + /** + * @param {ArrayBuffer} data + * @param {any} origin + */ + this._bcSubscriber = (data, origin) => { + if (origin !== this) { + const encoder = readMessage(this, new Uint8Array(data), false); + if (encoding.length(encoder) > 1) { + bc.publish(this.bcChannel, encoding.toUint8Array(encoder), this); + } + } + }; + /** + * Listens to Yjs updates and sends them to remote peers (ws and broadcastchannel) + * @param {Uint8Array} update + * @param {any} origin + */ + this._updateHandler = (update, origin) => { + if (origin !== this) { + const encoder = encoding.createEncoder(); + encoding.writeVarUint(encoder, messageSync); + syncProtocol.writeUpdate(encoder, update); + broadcastMessage(this, encoding.toUint8Array(encoder)); + } + }; + this.doc.on('update', this._updateHandler); + /** + * @param {any} changed + * @param {any} _origin + */ + this._awarenessUpdateHandler = ({ added, updated, removed }, _origin) => { + const changedClients = added.concat(updated).concat(removed); + const encoder = encoding.createEncoder(); + encoding.writeVarUint(encoder, messageAwareness); + encoding.writeVarUint8Array( + encoder, + awarenessProtocol.encodeAwarenessUpdate(awareness, changedClients) + ); + broadcastMessage(this, encoding.toUint8Array(encoder)); + }; + this._unloadHandler = () => { + awarenessProtocol.removeAwarenessStates( + this.awareness, + [doc.clientID], + 'window unload' + ); + }; + if (typeof window !== 'undefined') { + window.addEventListener('unload', this._unloadHandler); + } else if (typeof process !== 'undefined') { + process.on('exit', this._unloadHandler); + } + awareness.on('update', this._awarenessUpdateHandler); + this._checkInterval = /** @type {any} */ ( + setInterval(() => { + if ( + this.wsconnected && + messageReconnectTimeout < + time.getUnixTime() - this.wsLastMessageReceived + ) { + // no message received in a long time - not even your own awareness + // updates (which are updated every 15 seconds) + /** @type {WebSocket} */ (this.ws).close(); + } + }, messageReconnectTimeout / 10) + ); + if (connect) { + this.connect(); + } + } + + /** + * @type {boolean} + */ + get synced() { + return this._synced; + } + + set synced(state) { + if (this._synced !== state) { + this._synced = state; + this.emit('synced', [state]); + this.emit('sync', [state]); + } + } + + destroy() { + if (this._resyncInterval !== 0) { + clearInterval(this._resyncInterval); + } + clearInterval(this._checkInterval); + this.disconnect(); + if (typeof window !== 'undefined') { + window.removeEventListener('unload', this._unloadHandler); + } else if (typeof process !== 'undefined') { + process.off('exit', this._unloadHandler); + } + this.awareness.off('update', this._awarenessUpdateHandler); + this.doc.off('update', this._updateHandler); + super.destroy(); + } + + connectBc() { + if (this.disableBc) { + return; + } + if (!this.bcconnected) { + bc.subscribe(this.bcChannel, this._bcSubscriber); + this.bcconnected = true; + } + // send sync step1 to bc + // write sync step 1 + const encoderSync = encoding.createEncoder(); + encoding.writeVarUint(encoderSync, messageSync); + syncProtocol.writeSyncStep1(encoderSync, this.doc); + bc.publish(this.bcChannel, encoding.toUint8Array(encoderSync), this); + // broadcast local state + const encoderState = encoding.createEncoder(); + encoding.writeVarUint(encoderState, messageSync); + syncProtocol.writeSyncStep2(encoderState, this.doc); + bc.publish(this.bcChannel, encoding.toUint8Array(encoderState), this); + // write queryAwareness + const encoderAwarenessQuery = encoding.createEncoder(); + encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness); + bc.publish( + this.bcChannel, + encoding.toUint8Array(encoderAwarenessQuery), + this + ); + // broadcast local awareness state + const encoderAwarenessState = encoding.createEncoder(); + encoding.writeVarUint(encoderAwarenessState, messageAwareness); + encoding.writeVarUint8Array( + encoderAwarenessState, + awarenessProtocol.encodeAwarenessUpdate(this.awareness, [ + this.doc.clientID, + ]) + ); + bc.publish( + this.bcChannel, + encoding.toUint8Array(encoderAwarenessState), + this + ); + } + + disconnectBc() { + // broadcast message with local awareness state set to null (indicating disconnect) + const encoder = encoding.createEncoder(); + encoding.writeVarUint(encoder, messageAwareness); + encoding.writeVarUint8Array( + encoder, + awarenessProtocol.encodeAwarenessUpdate( + this.awareness, + [this.doc.clientID], + new Map() + ) + ); + broadcastMessage(this, encoding.toUint8Array(encoder)); + if (this.bcconnected) { + bc.unsubscribe(this.bcChannel, this._bcSubscriber); + this.bcconnected = false; + } + } + + disconnect() { + this.shouldConnect = false; + this.disconnectBc(); + if (this.ws !== null) { + this.ws.close(); + } + } + + connect() { + this.shouldConnect = true; + if (!this.wsconnected && this.ws === null) { + setupWS(this); + this.connectBc(); + } + } +} diff --git a/packages/data-center/src/types/index.ts b/packages/data-center/src/types/index.ts new file mode 100644 index 0000000000..61bc12be81 --- /dev/null +++ b/packages/data-center/src/types/index.ts @@ -0,0 +1,23 @@ +import { getLogger } from '../logger'; + +export type Workspace = { + name: string; + id: string; + isPublish?: boolean; + avatar?: string; + owner?: User; + isLocal?: boolean; + memberCount: number; + provider: string; +}; + +export type User = { + name: string; + id: string; + email: string; + avatar: string; +}; + +export type WorkspaceMeta = Pick; + +export type Logger = ReturnType; diff --git a/packages/data-center/src/utils/index.ts b/packages/data-center/src/utils/index.ts new file mode 100644 index 0000000000..d1040b0246 --- /dev/null +++ b/packages/data-center/src/utils/index.ts @@ -0,0 +1,38 @@ +const DefaultHeadImgColors = [ + ['#C6F2F3', '#0C6066'], + ['#FFF5AB', '#896406'], + ['#FFCCA7', '#8F4500'], + ['#FFCECE', '#AF1212'], + ['#E3DEFF', '#511AAB'], +]; + +export async function getDefaultHeadImgBlob( + workspaceName: string +): Promise { + const canvas = document.createElement('canvas'); + canvas.height = 100; + canvas.width = 100; + const ctx = canvas.getContext('2d'); + return new Promise((resolve, reject) => { + if (ctx) { + const randomNumber = Math.floor(Math.random() * 5); + const randomColor = DefaultHeadImgColors[randomNumber]; + ctx.fillStyle = randomColor[0]; + ctx.fillRect(0, 0, 100, 100); + ctx.font = "600 50px 'PingFang SC', 'Microsoft Yahei'"; + ctx.fillStyle = randomColor[1]; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText(workspaceName[0], 50, 50); + canvas.toBlob(blob => { + if (blob) { + resolve(blob); + } else { + reject(); + } + }, 'image/png'); + } else { + reject(); + } + }); +} diff --git a/packages/data-center/src/workspaces/index.ts b/packages/data-center/src/workspaces/index.ts new file mode 100644 index 0000000000..41419a6eb4 --- /dev/null +++ b/packages/data-center/src/workspaces/index.ts @@ -0,0 +1,2 @@ +export { Workspaces } from './workspaces'; +export type { WorkspacesScope, WorkspacesChangeEvent } from './workspaces'; diff --git a/packages/data-center/src/workspaces/workspaces.spec.ts b/packages/data-center/src/workspaces/workspaces.spec.ts new file mode 100644 index 0000000000..859cbabfb0 --- /dev/null +++ b/packages/data-center/src/workspaces/workspaces.spec.ts @@ -0,0 +1,50 @@ +import { describe, test, expect } from 'vitest'; +import { Workspaces } from './workspaces'; +import type { WorkspacesChangeEvent } from './workspaces'; + +describe('workspaces observable', () => { + const workspaces = new Workspaces(); + + const scope = workspaces.createScope(); + + test('add workspace', () => { + workspaces.once('change', (event: WorkspacesChangeEvent) => { + expect(event.added?.id).toEqual('123'); + }); + scope.add({ + id: '123', + name: 'test', + memberCount: 1, + provider: '', + }); + }); + + test('list workspace', () => { + const list = scope.list(); + expect(list.length).toEqual(1); + expect(list[0].id).toEqual('123'); + }); + + test('get workspace', () => { + expect(scope.get('123')?.id).toEqual('123'); + }); + + test('update workspace', () => { + workspaces.once('change', (event: WorkspacesChangeEvent) => { + expect(event.updated?.name).toEqual('demo'); + }); + scope.update('123', { name: 'demo' }); + }); + + test('get workspace form other scope', () => { + const scope1 = workspaces.createScope(); + expect(scope1.get('123')).toBeFalsy(); + }); + + test('delete workspace', () => { + workspaces.once('change', (event: WorkspacesChangeEvent) => { + expect(event.deleted?.id).toEqual('123'); + }); + scope.remove('123'); + }); +}); diff --git a/packages/data-center/src/workspaces/workspaces.ts b/packages/data-center/src/workspaces/workspaces.ts new file mode 100644 index 0000000000..90b1a5e9ea --- /dev/null +++ b/packages/data-center/src/workspaces/workspaces.ts @@ -0,0 +1,127 @@ +import { Observable } from 'lib0/observable'; +import type { Workspace, WorkspaceMeta } from '../types'; + +export interface WorkspacesScope { + get: (workspaceId: string) => Workspace | undefined; + list: () => Workspace[]; + add: (workspace: Workspace) => void; + remove: (workspaceId: string) => boolean; + clear: () => void; + update: (workspaceId: string, workspaceMeta: Partial) => void; +} + +export interface WorkspacesChangeEvent { + added?: Workspace; + deleted?: Workspace; + updated?: Workspace; +} + +export class Workspaces extends Observable<'change'> { + private _workspacesMap = new Map(); + + get workspaces(): Workspace[] { + return Array.from(this._workspacesMap.values()); + } + + find(workspaceId: string) { + return this._workspacesMap.get(workspaceId); + } + + createScope(): WorkspacesScope { + const scopedWorkspaceIds = new Set(); + + const get = (workspaceId: string) => { + if (!scopedWorkspaceIds.has(workspaceId)) { + return; + } + return this._workspacesMap.get(workspaceId); + }; + + const add = (workspace: Workspace) => { + if (this._workspacesMap.has(workspace.id)) { + throw new Error(`Duplicate workspace id.`); + } + this._workspacesMap.set(workspace.id, workspace); + scopedWorkspaceIds.add(workspace.id); + + this.emit('change', [ + { + added: workspace, + } as WorkspacesChangeEvent, + ]); + }; + + const remove = (workspaceId: string) => { + if (!scopedWorkspaceIds.has(workspaceId)) { + return true; + } + + const workspace = this._workspacesMap.get(workspaceId); + if (workspace) { + const ret = this._workspacesMap.delete(workspaceId); + // If deletion failed, return. + if (!ret) { + return ret; + } + + scopedWorkspaceIds.delete(workspaceId); + + this.emit('change', [ + { + deleted: workspace, + } as WorkspacesChangeEvent, + ]); + } + return true; + }; + + const clear = () => { + scopedWorkspaceIds.forEach(id => { + remove(id); + }); + }; + + const update = ( + workspaceId: string, + workspaceMeta: Partial + ) => { + if (!scopedWorkspaceIds.has(workspaceId)) { + return true; + } + + const workspace = this._workspacesMap.get(workspaceId); + if (!workspace) { + return true; + } + + this._workspacesMap.set(workspaceId, { ...workspace, ...workspaceMeta }); + + this.emit('change', [ + { + updated: this._workspacesMap.get(workspaceId), + } as WorkspacesChangeEvent, + ]); + }; + + // TODO: need to optimize + const list = () => { + const workspaces: Workspace[] = []; + scopedWorkspaceIds.forEach(id => { + const workspace = this._workspacesMap.get(id); + if (workspace) { + workspaces.push(workspace); + } + }); + return workspaces; + }; + + return { + get, + list, + add, + remove, + clear, + update, + }; + } +} diff --git a/packages/data-center/src/workspaces/workspaces.ts.bak b/packages/data-center/src/workspaces/workspaces.ts.bak new file mode 100644 index 0000000000..f813f6b16d --- /dev/null +++ b/packages/data-center/src/workspaces/workspaces.ts.bak @@ -0,0 +1,101 @@ +import { Workspace as WS } from '../types'; + +import { Observable } from 'lib0/observable'; +import { uuidv4 } from '@blocksuite/store'; +import { DataCenter } from '../datacenter'; + +export class Workspaces extends Observable { + private _workspaces: WS[]; + private readonly _dc: DataCenter; + + constructor(dc: DataCenter) { + super(); + this._workspaces = []; + this._dc = dc; + } + + public init() { + this._loadWorkspaces(); + } + + get workspaces() { + return this._workspaces; + } + + /** + * emit when workspaces changed + * @param {(workspace: WS[]) => void} cb + */ + onWorkspacesChange(cb: (workspace: WS[]) => void) { + this.on('change', cb); + } + + private async _loadWorkspaces() { + const providers = this._dc.providers; + let workspaces: WS[] = []; + providers.forEach(async p => { + const pWorkspaces = await p.loadWorkspaces(); + workspaces = [...workspaces, ...pWorkspaces]; + this._updateWorkspaces([...workspaces, ...pWorkspaces]); + }); + } + + /** + * focus load all workspaces list + */ + public async refreshWorkspaces() { + this._loadWorkspaces(); + } + + private _updateWorkspaces(workspaces: WS[]) { + this._workspaces = workspaces; + this.emit('change', this._workspaces); + } + + private _getDefaultWorkspace(name: string): WS { + return { + name, + id: uuidv4(), + isPublish: false, + avatar: '', + owner: undefined, + isLocal: true, + memberCount: 1, + provider: 'local', + }; + } + + /** add a local workspaces */ + public addLocalWorkspace(name: string) { + const workspace = this._getDefaultWorkspace(name); + this._updateWorkspaces([...this._workspaces, workspace]); + return workspace; + } + + /** delete a workspaces by id */ + public delete(id: string) { + const index = this._workspaces.findIndex(w => w.id === id); + if (index >= 0) { + this._workspaces.splice(index, 1); + this._updateWorkspaces(this._workspaces); + } + } + + /** get workspace info by id */ + public getWorkspace(id: string) { + return this._workspaces.find(w => w.id === id); + } + + /** check if workspace exists */ + public hasWorkspace(id: string) { + return this._workspaces.some(w => w.id === id); + } + + public updateWorkspaceInfo(id: string, info: Partial) { + const index = this._workspaces.findIndex(w => w.id === id); + if (index >= 0) { + this._workspaces[index] = { ...this._workspaces[index], ...info }; + this._updateWorkspaces(this._workspaces); + } + } +} diff --git a/packages/data-center/tests/cloud/auth.spec.ts b/packages/data-center/tests/cloud/auth.spec.ts deleted file mode 100644 index f896dc99e6..0000000000 --- a/packages/data-center/tests/cloud/auth.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { test, expect } from '@playwright/test'; - -import { getDataCenter } from '../utils.js'; - -import 'fake-indexeddb/auto'; - -test.describe('Auth', () => { - test('sign in', async () => {}); - - test('sign out', async () => {}); - - test('isLogin', async () => {}); - - test('getUserInfo', async () => {}); -}); diff --git a/packages/data-center/tests/cloud/collaborate.spec.ts b/packages/data-center/tests/cloud/collaborate.spec.ts deleted file mode 100644 index abe771109c..0000000000 --- a/packages/data-center/tests/cloud/collaborate.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { test, expect } from '@playwright/test'; - -import { getDataCenter } from '../utils.js'; - -import 'fake-indexeddb/auto'; - -test.describe('Collaborate', () => { - test('collaborate editor content', async () => {}); - - test('collaborate workspace name', async () => {}); - - test('collaborate workspace avatar', async () => {}); - - test('collaborate workspace list', async () => {}); -}); diff --git a/packages/data-center/tests/cloud/permission.spec.ts b/packages/data-center/tests/cloud/permission.spec.ts deleted file mode 100644 index 69f40ad8f3..0000000000 --- a/packages/data-center/tests/cloud/permission.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { test, expect } from '@playwright/test'; - -import { getDataCenter } from '../utils.js'; - -import 'fake-indexeddb/auto'; - -test.describe('Permission', () => { - test('get the public of workspace', async () => {}); - - test('make workspace public', async () => {}); - - test('make workspace private', async () => {}); - - test('un-login user open the public workspace ', async () => {}); - - test('un-login user open the private workspace ', async () => {}); -}); diff --git a/packages/data-center/tests/cloud/share.spec.ts b/packages/data-center/tests/cloud/share.spec.ts deleted file mode 100644 index 1698e29eab..0000000000 --- a/packages/data-center/tests/cloud/share.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { test, expect } from '@playwright/test'; - -import { getDataCenter } from '../utils.js'; - -import 'fake-indexeddb/auto'; - -test.describe('Share', () => { - test('add(invite) member by email', async () => {}); - - test('accept invite member link', async () => {}); - - test('members list', async () => {}); - - test('delete member', async () => {}); -}); diff --git a/packages/data-center/tests/cloud/sync.spec.ts b/packages/data-center/tests/cloud/sync.spec.ts deleted file mode 100644 index f81cde5758..0000000000 --- a/packages/data-center/tests/cloud/sync.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { test, expect } from '@playwright/test'; - -import { getDataCenter } from '../utils.js'; - -import 'fake-indexeddb/auto'; - -test.describe('Sync', () => { - test('get cloud the sync flag of workspace', async () => {}); - - test('enable [cloud sync feature]', async () => {}); - - test('close [cloud sync feature]', async () => {}); - - test('editor cloud storage', async () => {}); - - test('cloud sync is in-progress', async () => {}); - - test('cloud sync is completed', async () => {}); - - test('cloud sync is error', async () => {}); - - test('cloud storage is right', async () => {}); -}); diff --git a/packages/data-center/tests/local/attachment.spec.ts b/packages/data-center/tests/local/attachment.spec.ts deleted file mode 100644 index 3a017080b6..0000000000 --- a/packages/data-center/tests/local/attachment.spec.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { test, expect } from '@playwright/test'; - -import { getDataCenter } from '../utils.js'; - -import 'fake-indexeddb/auto'; - -test.describe('Attachment', () => { - test('upload blob', async () => {}); - - test('get blob', async () => {}); - - test('remove blob', async () => {}); -}); diff --git a/packages/data-center/tests/local/import-export.spec.ts b/packages/data-center/tests/local/import-export.spec.ts deleted file mode 100644 index 14d8a8a4f6..0000000000 --- a/packages/data-center/tests/local/import-export.spec.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { test, expect } from '@playwright/test'; - -import { getDataCenter } from '../utils.js'; - -import 'fake-indexeddb/auto'; - -test.describe('Import/Export Workspace', () => { - test('import workspace', async () => {}); - - test('export workspace', async () => {}); -}); diff --git a/packages/data-center/tests/local/init.spec.ts b/packages/data-center/tests/local/init.spec.ts deleted file mode 100644 index 26d48ed40c..0000000000 --- a/packages/data-center/tests/local/init.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { test, expect } from '@playwright/test'; - -import { getDataCenter } from '../utils.js'; - -import 'fake-indexeddb/auto'; - -test.describe('Init Data Center', () => { - test('init', async () => { - const dataCenter = await getDataCenter(); - expect(dataCenter).toBeTruthy(); - await dataCenter.clear(); - - const workspace = await dataCenter.load('test1'); - expect(workspace).toBeTruthy(); - }); - - test('init singleton', async () => { - // data center is singleton - const [dc1, dc2] = await Promise.all([getDataCenter(), getDataCenter()]); - expect(dc1).toEqual(dc2); - - // load same workspace will get same instance - const [ws1, ws2] = await Promise.all([ - dc1.load('test1'), - dc2.load('test1'), - ]); - expect(ws1).toEqual(ws2); - }); - - test('should init error with unknown provider', async () => { - const dc = await getDataCenter(); - await dc.clear(); - - // load workspace with unknown provider will throw error - test.fail(); - await dc.load('test2', { providerId: 'not exist provider' }); - }); - - test.skip('init affine provider', async () => { - const dataCenter = await getDataCenter(); - await dataCenter.clear(); - - // load workspace with affine provider - // TODO: set constant token for testing - const workspace = await dataCenter.load('6', { - providerId: 'affine', - config: { token: 'YOUR_TOKEN' }, - }); - expect(workspace).toBeTruthy(); - }); -}); diff --git a/packages/data-center/tests/local/search.spec.ts b/packages/data-center/tests/local/search.spec.ts deleted file mode 100644 index 98ac1ca59a..0000000000 --- a/packages/data-center/tests/local/search.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import assert from 'assert'; -import { test, expect } from '@playwright/test'; - -import { getDataCenter, waitOnce } from '../utils.js'; - -import 'fake-indexeddb/auto'; - -test.describe('Search', () => { - test('search result', async () => { - const dc = await getDataCenter(); - const workspace = await dc.load('test'); - - assert(workspace); - workspace.createPage('test'); - await waitOnce(workspace.signals.pageAdded); - const page = workspace.getPage('test'); - assert(page); - - const text = new page.Text(page, 'hello world'); - const blockId = page.addBlock({ flavour: 'affine:paragraph', text }); - - expect(workspace.search('hello')).toStrictEqual( - new Map([[blockId, 'test']]) - ); - }); -}); diff --git a/packages/data-center/tests/local/workspace.spec.ts b/packages/data-center/tests/local/workspace.spec.ts deleted file mode 100644 index 771bf2ff69..0000000000 --- a/packages/data-center/tests/local/workspace.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { test, expect } from '@playwright/test'; - -import { getDataCenter } from '../utils.js'; - -import 'fake-indexeddb/auto'; - -test.describe('Workspace', () => { - test('create', async () => {}); - - test('load', async () => {}); - - test('get workspace name', async () => {}); - test('set workspace name', async () => {}); - - test('get workspace avatar', async () => {}); - test('set workspace avatar', async () => {}); - - test('list', async () => { - const dataCenter = await getDataCenter(); - await dataCenter.clear(); - - await Promise.all([ - dataCenter.load('test3'), - dataCenter.load('test4'), - dataCenter.load('test5'), - dataCenter.load('test6'), - ]); - - expect(await dataCenter.list()).toStrictEqual({ - test3: { local: true }, - test4: { local: true }, - test5: { local: true }, - test6: { local: true }, - }); - - await dataCenter.reload('test3', { providerId: 'affine' }); - expect(await dataCenter.list()).toStrictEqual({ - test3: { affine: true }, - test4: { local: true }, - test5: { local: true }, - test6: { local: true }, - }); - }); - - test('destroy', async () => { - const dataCenter = await getDataCenter(); - await dataCenter.clear(); - - // return new workspace if origin workspace is destroyed - const ws1 = await dataCenter.load('test7'); - await dataCenter.destroy('test7'); - const ws2 = await dataCenter.load('test7'); - expect(ws1 !== ws2).toBeTruthy(); - - // return new workspace if workspace is reload - const ws3 = await dataCenter.load('test8'); - const ws4 = await dataCenter.reload('test8', { providerId: 'affine' }); - expect(ws3 !== ws4).toBeTruthy(); - }); - - test('remove', async () => { - const dataCenter = await getDataCenter(); - await dataCenter.clear(); - - // remove workspace will remove workspace data - await Promise.all([dataCenter.load('test9'), dataCenter.load('test10')]); - await dataCenter.delete('test9'); - expect(await dataCenter.list()).toStrictEqual({ test10: { local: true } }); - }); -}); diff --git a/packages/data-center/tests/utils.ts b/packages/data-center/tests/utils.ts deleted file mode 100644 index 1f4f128333..0000000000 --- a/packages/data-center/tests/utils.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Signal } from '@blocksuite/store'; - -export const getDataCenter = async () => { - const dataCenter = await import('../src/index.js'); - return await dataCenter.getDataCenter(false); -}; - -export const waitOnce = (signal: Signal) => - new Promise(resolve => signal.once(val => resolve(val))); diff --git a/packages/data-center/tsconfig.json b/packages/data-center/tsconfig.json index 063354ba92..835f0d0c88 100644 --- a/packages/data-center/tsconfig.json +++ b/packages/data-center/tsconfig.json @@ -21,5 +21,5 @@ "outDir": "./dist" }, "include": ["next-env.d.ts", "src/**/*.ts", "pages/**/*.tsx"], - "exclude": ["node_modules", "dist"] + "exclude": ["node_modules", "dist", "src/provider/affine/sync.js"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cbf408c5db..58d8ab75a3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,6 @@ importers: .: specifiers: '@changesets/cli': ^2.26.0 - '@jest/globals': ^29.3.1 '@playwright/test': ^1.29.1 '@types/eslint': ^8.4.10 '@types/node': ^18.11.17 @@ -15,16 +14,15 @@ importers: eslint-config-next: 12.3.1 eslint-config-prettier: ^8.5.0 eslint-plugin-prettier: ^4.2.1 + fake-indexeddb: 4.0.1 got: ^12.5.3 husky: ^8.0.2 - jest: ^29.3.1 lint-staged: ^13.1.0 prettier: ^2.7.1 - ts-jest: ^29.0.3 typescript: ^4.9.3 + vitest: ^0.26.3 devDependencies: '@changesets/cli': 2.26.0 - '@jest/globals': 29.3.1 '@playwright/test': 1.29.1 '@types/eslint': 8.4.10 '@types/node': 18.11.18 @@ -34,21 +32,21 @@ importers: eslint-config-next: 12.3.1_p4cjf2r47dnfiqufepc5hp43sq eslint-config-prettier: 8.5.0_eslint@8.31.0 eslint-plugin-prettier: 4.2.1_vkmhlldugy3fnk7p2bqhsfnaiq + fake-indexeddb: 4.0.1 got: 12.5.3 husky: 8.0.2 - jest: 29.3.1_@types+node@18.11.18 lint-staged: 13.1.0 prettier: 2.7.1 - ts-jest: 29.0.3_4f6uxrzmuwipl5rr3bcogf6k74 typescript: 4.9.3 + vitest: 0.26.3 packages/app: specifiers: '@affine/datacenter': workspace:* - '@blocksuite/blocks': 0.3.1 - '@blocksuite/editor': 0.3.1 + '@blocksuite/blocks': '=0.3.1-20230106060050-1aad55d' + '@blocksuite/editor': '=0.3.1-20230106060050-1aad55d' '@blocksuite/icons': ^2.0.2 - '@blocksuite/store': 0.3.1 + '@blocksuite/store': '=0.3.1-20230106060050-1aad55d' '@emotion/css': ^11.10.0 '@emotion/react': ^11.10.4 '@emotion/server': ^11.10.0 @@ -87,10 +85,10 @@ importers: yjs: ^13.5.44 dependencies: '@affine/datacenter': link:../data-center - '@blocksuite/blocks': 0.3.1_yjs@13.5.44 - '@blocksuite/editor': 0.3.1_yjs@13.5.44 + '@blocksuite/blocks': 0.3.1-20230106060050-1aad55d_yjs@13.5.44 + '@blocksuite/editor': 0.3.1-20230106060050-1aad55d_yjs@13.5.44 '@blocksuite/icons': 2.0.4_w5j4k42lgipnm43s3brx6h3c34 - '@blocksuite/store': 0.3.1_yjs@13.5.44 + '@blocksuite/store': 0.3.1-20230106060050-1aad55d_yjs@13.5.44 '@emotion/css': 11.10.0 '@emotion/react': 11.10.4_w5j4k42lgipnm43s3brx6h3c34 '@emotion/server': 11.10.0_@emotion+css@11.10.0 @@ -131,8 +129,8 @@ importers: packages/data-center: specifiers: - '@blocksuite/blocks': ^0.3.1 - '@blocksuite/store': ^0.3.1 + '@blocksuite/blocks': '=0.3.1-20230106060050-1aad55d' + '@blocksuite/store': '=0.3.1-20230106060050-1aad55d' '@playwright/test': ^1.29.1 '@types/debug': ^4.1.7 debug: ^4.3.4 @@ -148,8 +146,8 @@ importers: y-protocols: ^1.0.5 yjs: ^13.5.44 dependencies: - '@blocksuite/blocks': 0.3.1_yjs@13.5.44 - '@blocksuite/store': 0.3.1_yjs@13.5.44 + '@blocksuite/blocks': 0.3.1-20230106060050-1aad55d_yjs@13.5.44 + '@blocksuite/store': 0.3.1-20230106060050-1aad55d_yjs@13.5.44 debug: 4.3.4 encoding: 0.1.13 firebase: 9.15.0_encoding@0.1.13 @@ -755,15 +753,6 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true - /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.20.5: - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.5 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.20.5: resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: @@ -811,15 +800,6 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.20.5: - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.5 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.20.5: resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: @@ -838,16 +818,6 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: false - /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.20.5: - resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.5 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.20.5: resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: @@ -922,16 +892,6 @@ packages: '@babel/helper-plugin-utils': 7.19.0 dev: true - /@babel/plugin-syntax-typescript/7.20.0_@babel+core@7.20.5: - resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.5 - '@babel/helper-plugin-utils': 7.19.0 - dev: true - /@babel/plugin-transform-arrow-functions/7.20.7_@babel+core@7.20.5: resolution: {integrity: sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==} engines: {node: '>=6.9.0'} @@ -1466,15 +1426,6 @@ packages: - supports-color dev: true - /@babel/types/7.19.4: - resolution: {integrity: sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.19.4 - '@babel/helper-validator-identifier': 7.19.1 - to-fast-properties: 2.0.0 - dev: true - /@babel/types/7.20.5: resolution: {integrity: sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==} engines: {node: '>=6.9.0'} @@ -1492,14 +1443,11 @@ packages: to-fast-properties: 2.0.0 dev: true - /@bcoe/v8-coverage/0.2.3: - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - dev: true - - /@blocksuite/blocks/0.3.1_yjs@13.5.44: - resolution: {integrity: sha512-b0dGz2MG4yIgngJPRumaMY58wAsd2FEVSZl3tpCXlagK9I0HD165Bq70PxcaRHVjBSV1Gf29ZVHUF6BVTYogPw==} + /@blocksuite/blocks/0.3.1-20230106060050-1aad55d_yjs@13.5.44: + resolution: {integrity: sha512-qRNXmhjw+GAGsV1mI2XXPxYTlHfsFHv9ttTCNQ6IIcxvc5Hh6lWmdwVibxvlpYUkgEc1zv3/GxOEsR/ngpZXzQ==} dependencies: - '@blocksuite/store': 0.3.1_yjs@13.5.44 + '@blocksuite/phasor': 0.3.1_yjs@13.5.44 + '@blocksuite/store': 0.3.1-20230106060050-1aad55d_yjs@13.5.44 '@tldraw/intersect': 1.8.0 autosize: 5.0.2 highlight.js: 11.7.0 @@ -1515,11 +1463,11 @@ packages: - yjs dev: false - /@blocksuite/editor/0.3.1_yjs@13.5.44: - resolution: {integrity: sha512-ycKcyvPW6R8R2GZOFneGH1xNi5gJBx5WtWjW9YwcQslFzXVWMCCBips1Bud2uL4kkbWQoodyua6k2vsXxGAKLw==} + /@blocksuite/editor/0.3.1-20230106060050-1aad55d_yjs@13.5.44: + resolution: {integrity: sha512-wSlAF9XVxIkHFJ1qCzn7oQ/gwXybFYMrzRl35UTJV509D+DuWZefRZWvpdIDCOUJ24uQscr1HxwsON11ltfWgA==} dependencies: - '@blocksuite/blocks': 0.3.1_yjs@13.5.44 - '@blocksuite/store': 0.3.1_yjs@13.5.44 + '@blocksuite/blocks': 0.3.1-20230106060050-1aad55d_yjs@13.5.44 + '@blocksuite/store': 0.3.1-20230106060050-1aad55d_yjs@13.5.44 lit: 2.5.0 marked: 4.2.5 turndown: 7.1.1 @@ -1540,8 +1488,16 @@ packages: react: 18.2.0 dev: false - /@blocksuite/store/0.3.1_yjs@13.5.44: - resolution: {integrity: sha512-kynVTDfNCSChz2JI2rtGHxRIV2YrLzvAgVajcbfDVCuXKG0siBoEjLasG1a0kvevbvW/FabrNAj+xaIplklioA==} + /@blocksuite/phasor/0.3.1_yjs@13.5.44: + resolution: {integrity: sha512-aJmAQn2qoF6HxFZWgq7xa/pWVyzg3MmD6dynIHAKdfN7rBdKk3PNA+lRX919QkD2e270N/zgHEGFFQI1Nj5xrA==} + peerDependencies: + yjs: ^13 + dependencies: + yjs: 13.5.44 + dev: false + + /@blocksuite/store/0.3.1-20230106060050-1aad55d_yjs@13.5.44: + resolution: {integrity: sha512-dRy+YzlWMwiYq0Im9NogK/NTkV+NKK+lgejYq56m6nH2m16/G9AMODqP0oQy/XeYFevUpL9i9RdV0rHsJ2gc0Q==} peerDependencies: yjs: ^13 dependencies: @@ -1900,6 +1856,204 @@ packages: resolution: {integrity: sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==} dev: false + /@esbuild/android-arm/0.16.15: + resolution: {integrity: sha512-JsJtmadyWcR+DEtHLixM7bAQsfi1s0Xotv9kVOoXbCLyhKPOHvMEyh3kJBuTbCPSE4c2jQkQVmarwc9Mg9k3bA==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64/0.16.15: + resolution: {integrity: sha512-OdbkUv7468dSsgoFtHIwTaYAuI5lDEv/v+dlfGBUbVa2xSDIIuSOHXawynw5N9+5lygo/JdXa5/sgGjiEU18gQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64/0.16.15: + resolution: {integrity: sha512-dPUOBiNNWAm+/bxoA75o7R7qqqfcEzXaYlb5uJk2xGHmUMNKSAnDCtRYLgx9/wfE4sXyn8H948OrDyUAHhPOuA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64/0.16.15: + resolution: {integrity: sha512-AksarYV85Hxgwh5/zb6qGl4sYWxIXPQGBAZ+jUro1ZpINy3EWumK+/4DPOKUBPnsrOIvnNXy7Rq4mTeCsMQDNA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64/0.16.15: + resolution: {integrity: sha512-qqrKJxoohceZGGP+sZ5yXkzW9ZiyFZJ1gWSEfuYdOWzBSL18Uy3w7s/IvnDYHo++/cxwqM0ch3HQVReSZy7/4Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64/0.16.15: + resolution: {integrity: sha512-LBWaep6RvJm5KnsKkocdVEzuwnGMjz54fcRVZ9d3R7FSEWOtPBxMhuxeA1n98JVbCLMkTPFmKN6xSnfhnM9WXQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64/0.16.15: + resolution: {integrity: sha512-LE8mKC6JPR04kPLRP9A6k7ZmG0k2aWF4ru79Sde6UeWCo7yDby5f48uJNFQ2pZqzUUkLrHL8xNdIHerJeZjHXg==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm/0.16.15: + resolution: {integrity: sha512-+1sGlqtMJTOnJUXwLUGnDhPaGRKqxT0UONtYacS+EjdDOrSgpQ/1gUXlnze45Z/BogwYaswQM19Gu1YD1T19/w==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64/0.16.15: + resolution: {integrity: sha512-mRYpuQGbzY+XLczy3Sk7fMJ3DRKLGDIuvLKkkUkyecDGQMmil6K/xVKP9IpKO7JtNH477qAiMjjX7jfKae8t4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32/0.16.15: + resolution: {integrity: sha512-puXVFvY4m8EB6/fzu3LdgjiNnEZ3gZMSR7NmKoQe51l3hyQalvTjab3Dt7aX4qGf+8Pj7dsCOBNzNzkSlr/4Aw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64/0.16.15: + resolution: {integrity: sha512-ATMGb3eg8T6ZTGZFldlGeFEcevBiVq6SBHvRAO04HMfUjZWneZ/U+JJb3YzlNZxuscJ4Tmzq+JrYxlk7ro4dRg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el/0.16.15: + resolution: {integrity: sha512-3SEA4L82OnoSATW+Ve8rPgLaKjC8WMt8fnx7De9kvi/NcVbkj8W+J7qnu/tK2P9pUPQP7Au/0sjPEqZtFeyKQQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64/0.16.15: + resolution: {integrity: sha512-8PgbeX+N6vmqeySzyxO0NyDOltCEW13OS5jUHTvCHmCgf4kNXZtAWJ+zEfJxjRGYhVezQ1FdIm7WfN1R27uOyg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64/0.16.15: + resolution: {integrity: sha512-U+coqH+89vbPVoU30no1Fllrn6gvEeO5tfEArBhjYZ+dQ3Gv7ciQXYf5nrT1QdlIFwEjH4Is1U1iiaGWW+tGpQ==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x/0.16.15: + resolution: {integrity: sha512-M0nKLFMdyFGBoitxG42kq6Xap0CPeDC6gfF9lg7ZejzGF6kqYUGT+pQGl2QCQoxJBeat/LzTma1hG8C3dq2ocg==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64/0.16.15: + resolution: {integrity: sha512-t7/fOXBUKfigvhJLGKZ9TPHHgqNgpIpYaAbcXQk1X+fPeUG7x0tpAbXJ2wST9F/gJ02+CLETPMnhG7Tra2wqsQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64/0.16.15: + resolution: {integrity: sha512-0k0Nxi6DOJmTnLtKD/0rlyqOPpcqONXY53vpkoAsue8CfyhNPWtwzba1ICFNCfCY1dqL3Ho/xEzujJhmdXq1rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64/0.16.15: + resolution: {integrity: sha512-3SkckazfIbdSjsGpuIYT3d6n2Hx0tck3MS1yVsbahhWiLvdy4QozTpvlbjqO3GmvtvhxY4qdyhFOO2wiZKeTAQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64/0.16.15: + resolution: {integrity: sha512-8PNvBC+O8X5EnyIGqE8St2bOjjrXMR17NOLenIrzolvwWnJXvwPo0tE/ahOeiAJmTOS/eAcN8b4LAZcn17Uj7w==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64/0.16.15: + resolution: {integrity: sha512-YPaSgm/mm7kNcATB53OxVGVfn6rDNbImTn330ZlF3hKej1e9ktCaljGjn2vH08z2dlHEf3kdt57tNjE6zs8SzA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32/0.16.15: + resolution: {integrity: sha512-0movUXbSNrTeNf5ZXT0avklEvlJD0hNGZsrrXHfsp9z4tK5xC+apCqmUEZeE9mqrb84Z8XbgGr/MS9LqafTP2A==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64/0.16.15: + resolution: {integrity: sha512-27h5GCcbfomVAqAnMJWvR1LqEY0dFqIq4vTe5nY3becnZNu0SX8F0+gTk3JPvgWQHzaGc6VkPzlOiMkdSUunUA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@eslint/eslintrc/1.3.2: resolution: {integrity: sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2470,235 +2624,6 @@ packages: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true - /@istanbuljs/load-nyc-config/1.1.0: - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} - engines: {node: '>=8'} - dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.14.1 - resolve-from: 5.0.0 - dev: true - - /@istanbuljs/schema/0.1.3: - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} - dev: true - - /@jest/console/29.3.1: - resolution: {integrity: sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.3.1 - '@types/node': 18.11.18 - chalk: 4.1.2 - jest-message-util: 29.3.1 - jest-util: 29.3.1 - slash: 3.0.0 - dev: true - - /@jest/core/29.3.1: - resolution: {integrity: sha512-0ohVjjRex985w5MmO5L3u5GR1O30DexhBSpuwx2P+9ftyqHdJXnk7IUWiP80oHMvt7ubHCJHxV0a0vlKVuZirw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/console': 29.3.1 - '@jest/reporters': 29.3.1 - '@jest/test-result': 29.3.1 - '@jest/transform': 29.3.1 - '@jest/types': 29.3.1 - '@types/node': 18.11.18 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.7.0 - exit: 0.1.2 - graceful-fs: 4.2.10 - jest-changed-files: 29.2.0 - jest-config: 29.3.1_@types+node@18.11.18 - jest-haste-map: 29.3.1 - jest-message-util: 29.3.1 - jest-regex-util: 29.2.0 - jest-resolve: 29.3.1 - jest-resolve-dependencies: 29.3.1 - jest-runner: 29.3.1 - jest-runtime: 29.3.1 - jest-snapshot: 29.3.1 - jest-util: 29.3.1 - jest-validate: 29.3.1 - jest-watcher: 29.3.1 - micromatch: 4.0.5 - pretty-format: 29.3.1 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - supports-color - - ts-node - dev: true - - /@jest/environment/29.3.1: - resolution: {integrity: sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/fake-timers': 29.3.1 - '@jest/types': 29.3.1 - '@types/node': 18.11.18 - jest-mock: 29.3.1 - dev: true - - /@jest/expect-utils/29.3.1: - resolution: {integrity: sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.2.0 - dev: true - - /@jest/expect/29.3.1: - resolution: {integrity: sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - expect: 29.3.1 - jest-snapshot: 29.3.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@jest/fake-timers/29.3.1: - resolution: {integrity: sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.3.1 - '@sinonjs/fake-timers': 9.1.2 - '@types/node': 18.11.18 - jest-message-util: 29.3.1 - jest-mock: 29.3.1 - jest-util: 29.3.1 - dev: true - - /@jest/globals/29.3.1: - resolution: {integrity: sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.3.1 - '@jest/expect': 29.3.1 - '@jest/types': 29.3.1 - jest-mock: 29.3.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@jest/reporters/29.3.1: - resolution: {integrity: sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.3.1 - '@jest/test-result': 29.3.1 - '@jest/transform': 29.3.1 - '@jest/types': 29.3.1 - '@jridgewell/trace-mapping': 0.3.17 - '@types/node': 18.11.18 - chalk: 4.1.2 - collect-v8-coverage: 1.0.1 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.10 - istanbul-lib-coverage: 3.2.0 - istanbul-lib-instrument: 5.2.1 - istanbul-lib-report: 3.0.0 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.5 - jest-message-util: 29.3.1 - jest-util: 29.3.1 - jest-worker: 29.3.1 - slash: 3.0.0 - string-length: 4.0.2 - strip-ansi: 6.0.1 - v8-to-istanbul: 9.0.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@jest/schemas/29.0.0: - resolution: {integrity: sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@sinclair/typebox': 0.24.51 - dev: true - - /@jest/source-map/29.2.0: - resolution: {integrity: sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jridgewell/trace-mapping': 0.3.17 - callsites: 3.1.0 - graceful-fs: 4.2.10 - dev: true - - /@jest/test-result/29.3.1: - resolution: {integrity: sha512-qeLa6qc0ddB0kuOZyZIhfN5q0e2htngokyTWsGriedsDhItisW7SDYZ7ceOe57Ii03sL988/03wAcBh3TChMGw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.3.1 - '@jest/types': 29.3.1 - '@types/istanbul-lib-coverage': 2.0.4 - collect-v8-coverage: 1.0.1 - dev: true - - /@jest/test-sequencer/29.3.1: - resolution: {integrity: sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.3.1 - graceful-fs: 4.2.10 - jest-haste-map: 29.3.1 - slash: 3.0.0 - dev: true - - /@jest/transform/29.3.1: - resolution: {integrity: sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.20.5 - '@jest/types': 29.3.1 - '@jridgewell/trace-mapping': 0.3.17 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.10 - jest-haste-map: 29.3.1 - jest-regex-util: 29.2.0 - jest-util: 29.3.1 - micromatch: 4.0.5 - pirates: 4.0.5 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@jest/types/29.3.1: - resolution: {integrity: sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.0.0 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 18.11.18 - '@types/yargs': 17.0.15 - chalk: 4.1.2 - dev: true - /@jridgewell/gen-mapping/0.1.1: resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} engines: {node: '>=6.0.0'} @@ -3501,27 +3426,11 @@ packages: resolution: {integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==} dev: true - /@sinclair/typebox/0.24.51: - resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==} - dev: true - /@sindresorhus/is/5.3.0: resolution: {integrity: sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==} engines: {node: '>=14.16'} dev: true - /@sinonjs/commons/1.8.6: - resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==} - dependencies: - type-detect: 4.0.8 - dev: true - - /@sinonjs/fake-timers/9.1.2: - resolution: {integrity: sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==} - dependencies: - '@sinonjs/commons': 1.8.6 - dev: true - /@surma/rollup-plugin-off-main-thread/2.2.3: resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} dependencies: @@ -3559,33 +3468,14 @@ packages: resolution: {integrity: sha512-GiS5Df3CzXY/fPBFcM0CKFERZfI4Cg1X33VPZX+NLo7Fwm/h9zu/aU24N1mG75Q9LuMnwKm7woxKr8BiUXGYCg==} dev: false - /@types/babel__core/7.1.20: - resolution: {integrity: sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==} + /@types/chai-subset/1.3.3: + resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} dependencies: - '@babel/parser': 7.20.5 - '@babel/types': 7.20.5 - '@types/babel__generator': 7.6.4 - '@types/babel__template': 7.4.1 - '@types/babel__traverse': 7.18.3 + '@types/chai': 4.3.4 dev: true - /@types/babel__generator/7.6.4: - resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} - dependencies: - '@babel/types': 7.20.5 - dev: true - - /@types/babel__template/7.4.1: - resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} - dependencies: - '@babel/parser': 7.20.5 - '@babel/types': 7.20.5 - dev: true - - /@types/babel__traverse/7.18.3: - resolution: {integrity: sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==} - dependencies: - '@babel/types': 7.20.5 + /@types/chai/4.3.4: + resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==} dev: true /@types/debug/4.1.7: @@ -3616,12 +3506,6 @@ packages: '@types/node': 18.7.18 dev: true - /@types/graceful-fs/4.1.5: - resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} - dependencies: - '@types/node': 18.11.18 - dev: true - /@types/http-cache-semantics/4.0.1: resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==} dev: true @@ -3632,22 +3516,6 @@ packages: ci-info: 3.7.0 dev: true - /@types/istanbul-lib-coverage/2.0.4: - resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} - dev: true - - /@types/istanbul-lib-report/3.0.0: - resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} - dependencies: - '@types/istanbul-lib-coverage': 2.0.4 - dev: true - - /@types/istanbul-reports/3.0.1: - resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} - dependencies: - '@types/istanbul-lib-report': 3.0.0 - dev: true - /@types/json-schema/7.0.11: resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} dev: true @@ -3691,10 +3559,6 @@ packages: resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} dev: false - /@types/prettier/2.7.1: - resolution: {integrity: sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==} - dev: true - /@types/prop-types/15.7.5: resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} @@ -3754,10 +3618,6 @@ packages: resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} dev: true - /@types/stack-utils/2.0.1: - resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} - dev: true - /@types/trusted-types/2.0.2: resolution: {integrity: sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==} @@ -3765,16 +3625,6 @@ packages: resolution: {integrity: sha512-UYK244awtmcUYQfs7FR8710MJcefL2WvkyHMjA8yJzxd1mo0Gfn88sRZ1Bls7hiUhA2w7ne1gpJ9T5g3G0wOyA==} dev: true - /@types/yargs-parser/21.0.0: - resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} - dev: true - - /@types/yargs/17.0.15: - resolution: {integrity: sha512-ZHc4W2dnEQPfhn06TBEdWaiUHEZAocYaiVMfwOipY5jcJt/251wVrKCBWBetGZWO5CF8tdb7L3DmdxVlZ2BOIg==} - dependencies: - '@types/yargs-parser': 21.0.0 - dev: true - /@typescript-eslint/eslint-plugin/5.48.0_wxmz6cyorqfuzhhglktlvr5lve: resolution: {integrity: sha512-SVLafp0NXpoJY7ut6VFVUU9I+YeFsDzeQwtK0WZ+xbRN3mtxJ08je+6Oi2N89qDn087COdO0u3blKZNv9VetRQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3960,11 +3810,22 @@ packages: acorn: 8.8.0 dev: true + /acorn-walk/8.2.0: + resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + engines: {node: '>=0.4.0'} + dev: true + /acorn/8.8.0: resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==} engines: {node: '>=0.4.0'} dev: true + /acorn/8.8.1: + resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + /aggregate-error/3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} @@ -4032,24 +3893,11 @@ packages: dependencies: color-convert: 2.0.1 - /ansi-styles/5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - dev: true - /ansi-styles/6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} dev: true - /anymatch/3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - dev: true - /argparse/1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: @@ -4136,6 +3984,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /assertion-error/1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + /ast-types-flow/0.0.7: resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==} dev: true @@ -4167,24 +4019,6 @@ packages: resolution: {integrity: sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==} dev: true - /babel-jest/29.3.1_@babel+core@7.20.5: - resolution: {integrity: sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.8.0 - dependencies: - '@babel/core': 7.20.5 - '@jest/transform': 29.3.1 - '@types/babel__core': 7.1.20 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.2.0_@babel+core@7.20.5 - chalk: 4.1.2 - graceful-fs: 4.2.10 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - dev: true - /babel-loader/8.3.0: resolution: {integrity: sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==} engines: {node: '>= 8.9'} @@ -4198,29 +4032,6 @@ packages: schema-utils: 2.7.1 dev: true - /babel-plugin-istanbul/6.1.1: - resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} - engines: {node: '>=8'} - dependencies: - '@babel/helper-plugin-utils': 7.19.0 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.1 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /babel-plugin-jest-hoist/29.2.0: - resolution: {integrity: sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/template': 7.18.10 - '@babel/types': 7.20.5 - '@types/babel__core': 7.1.20 - '@types/babel__traverse': 7.18.3 - dev: true - /babel-plugin-macros/3.1.0: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} @@ -4266,37 +4077,6 @@ packages: - supports-color dev: true - /babel-preset-current-node-syntax/1.0.1_@babel+core@7.20.5: - resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.20.5 - '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.5 - '@babel/plugin-syntax-bigint': 7.8.3_@babel+core@7.20.5 - '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.20.5 - '@babel/plugin-syntax-import-meta': 7.10.4_@babel+core@7.20.5 - '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.5 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.5 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.5 - '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.5 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.5 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.5 - '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.5 - '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.20.5 - dev: true - - /babel-preset-jest/29.2.0_@babel+core@7.20.5: - resolution: {integrity: sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.20.5 - babel-plugin-jest-hoist: 29.2.0 - babel-preset-current-node-syntax: 1.0.1_@babel+core@7.20.5 - dev: true - /balanced-match/1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true @@ -4358,19 +4138,6 @@ packages: update-browserslist-db: 1.0.10_browserslist@4.21.4 dev: true - /bs-logger/0.2.6: - resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} - engines: {node: '>= 6'} - dependencies: - fast-json-stable-stringify: 2.1.0 - dev: true - - /bser/2.1.1: - resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - dependencies: - node-int64: 0.4.0 - dev: true - /buffer-from/0.1.2: resolution: {integrity: sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==} dev: false @@ -4433,14 +4200,22 @@ packages: engines: {node: '>=6'} dev: true - /camelcase/6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - dev: true - /caniuse-lite/1.0.30001419: resolution: {integrity: sha512-aFO1r+g6R7TW+PNQxKzjITwLOyDhVRLjW0LcwS/HCZGUUKTGNp9+IwLC4xyDSZBygVL/mxaFR3HIV6wEKQuSzw==} + /chai/4.3.7: + resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.2 + deep-eql: 4.1.3 + get-func-name: 2.0.0 + loupe: 2.3.6 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + /chalk/2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -4457,24 +4232,19 @@ packages: supports-color: 7.2.0 dev: true - /char-regex/1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} - dev: true - /chardet/0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} dev: true + /check-error/1.0.2: + resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} + dev: true + /ci-info/3.7.0: resolution: {integrity: sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==} engines: {node: '>=8'} dev: true - /cjs-module-lexer/1.2.2: - resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} - dev: true - /clean-stack/2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -4569,15 +4339,6 @@ packages: - '@types/react' dev: false - /co/4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} - engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - dev: true - - /collect-v8-coverage/1.0.1: - resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==} - dev: true - /color-convert/1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -4628,10 +4389,6 @@ packages: /convert-source-map/1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} - /convert-source-map/2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - dev: true - /core-js-compat/3.27.1: resolution: {integrity: sha512-Dg91JFeCDA17FKnneN7oCMz4BkQ4TcffkgHP4OWwp9yx3pi7ubqMDXXSacfNak1PQqjc95skyt+YBLHQJnkJwA==} dependencies: @@ -4777,8 +4534,11 @@ packages: mimic-response: 3.1.0 dev: true - /dedent/0.7.0: - resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} + /deep-eql/4.1.3: + resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.0.8 dev: true /deep-equal/1.1.1: @@ -4837,20 +4597,10 @@ packages: engines: {node: '>=8'} dev: true - /detect-newline/3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} - engines: {node: '>=8'} - dev: true - /detect-node-es/1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} dev: false - /diff-sequences/29.3.1: - resolution: {integrity: sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - /dir-glob/3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -4911,11 +4661,6 @@ packages: resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} dev: true - /emittery/0.13.1: - resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} - engines: {node: '>=12'} - dev: true - /emoji-regex/8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -4994,6 +4739,36 @@ packages: is-symbol: 1.0.4 dev: true + /esbuild/0.16.15: + resolution: {integrity: sha512-v+3ozjy9wyj8cOElzx3//Lsb4TCxPfZxRmdsfm0YaEkvZu7y6rKH7Zi1UpDx4JI7dSQui+U1Qxhfij9KBbHfrA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.16.15 + '@esbuild/android-arm64': 0.16.15 + '@esbuild/android-x64': 0.16.15 + '@esbuild/darwin-arm64': 0.16.15 + '@esbuild/darwin-x64': 0.16.15 + '@esbuild/freebsd-arm64': 0.16.15 + '@esbuild/freebsd-x64': 0.16.15 + '@esbuild/linux-arm': 0.16.15 + '@esbuild/linux-arm64': 0.16.15 + '@esbuild/linux-ia32': 0.16.15 + '@esbuild/linux-loong64': 0.16.15 + '@esbuild/linux-mips64el': 0.16.15 + '@esbuild/linux-ppc64': 0.16.15 + '@esbuild/linux-riscv64': 0.16.15 + '@esbuild/linux-s390x': 0.16.15 + '@esbuild/linux-x64': 0.16.15 + '@esbuild/netbsd-x64': 0.16.15 + '@esbuild/openbsd-x64': 0.16.15 + '@esbuild/sunos-x64': 0.16.15 + '@esbuild/win32-arm64': 0.16.15 + '@esbuild/win32-ia32': 0.16.15 + '@esbuild/win32-x64': 0.16.15 + dev: true + /escalade/3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -5002,11 +4777,6 @@ packages: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - /escape-string-regexp/2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - dev: true - /escape-string-regexp/4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -5586,21 +5356,6 @@ packages: resolution: {integrity: sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==} dev: false - /execa/5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - dev: true - /execa/6.1.0: resolution: {integrity: sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -5616,22 +5371,6 @@ packages: strip-final-newline: 3.0.0 dev: true - /exit/0.1.2: - resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} - engines: {node: '>= 0.8.0'} - dev: true - - /expect/29.3.1: - resolution: {integrity: sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/expect-utils': 29.3.1 - jest-get-type: 29.2.0 - jest-matcher-utils: 29.3.1 - jest-message-util: 29.3.1 - jest-util: 29.3.1 - dev: true - /extend/3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} dev: false @@ -5699,12 +5438,6 @@ packages: websocket-driver: 0.7.4 dev: false - /fb-watchman/2.0.2: - resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - dependencies: - bser: 2.1.1 - dev: true - /fetch-blob/3.2.0: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} @@ -5903,6 +5636,10 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + /get-func-name/2.0.0: + resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} + dev: true + /get-intrinsic/1.1.3: resolution: {integrity: sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==} dependencies: @@ -5919,11 +5656,6 @@ packages: resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} dev: true - /get-package-type/0.1.0: - resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} - engines: {node: '>=8.0.0'} - dev: true - /get-stream/6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} @@ -6098,10 +5830,6 @@ packages: resolution: {integrity: sha512-mshqjgTqx8ee0qryHvRgZaZDxTwxam/2yTQmQlqAWS3+twnq1jsY9Yng9zB7lWq6WRrjTbTOc7knNwccXQiAjQ==} dev: false - /html-escaper/2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - dev: true - /html-parse-stringify/3.0.1: resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} dependencies: @@ -6138,11 +5866,6 @@ packages: resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} dev: true - /human-signals/2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - dev: true - /human-signals/3.0.1: resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==} engines: {node: '>=12.20.0'} @@ -6204,15 +5927,6 @@ packages: parent-module: 1.0.1 resolve-from: 4.0.0 - /import-local/3.1.0: - resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} - engines: {node: '>=8'} - hasBin: true - dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 - dev: true - /imurmurhash/0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -6310,11 +6024,6 @@ packages: engines: {node: '>=12'} dev: true - /is-generator-fn/2.1.0: - resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} - engines: {node: '>=6'} - dev: true - /is-glob/4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -6453,52 +6162,6 @@ packages: resolution: {integrity: sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==} dev: false - /istanbul-lib-coverage/3.2.0: - resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} - engines: {node: '>=8'} - dev: true - - /istanbul-lib-instrument/5.2.1: - resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} - engines: {node: '>=8'} - dependencies: - '@babel/core': 7.20.5 - '@babel/parser': 7.20.5 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.0 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - dev: true - - /istanbul-lib-report/3.0.0: - resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} - engines: {node: '>=8'} - dependencies: - istanbul-lib-coverage: 3.2.0 - make-dir: 3.1.0 - supports-color: 7.2.0 - dev: true - - /istanbul-lib-source-maps/4.0.1: - resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} - engines: {node: '>=10'} - dependencies: - debug: 4.3.4 - istanbul-lib-coverage: 3.2.0 - source-map: 0.6.1 - transitivePeerDependencies: - - supports-color - dev: true - - /istanbul-reports/3.1.5: - resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==} - engines: {node: '>=8'} - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.0 - dev: true - /jake/10.8.5: resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==} engines: {node: '>=10'} @@ -6510,385 +6173,6 @@ packages: minimatch: 3.1.2 dev: true - /jest-changed-files/29.2.0: - resolution: {integrity: sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - execa: 5.1.1 - p-limit: 3.1.0 - dev: true - - /jest-circus/29.3.1: - resolution: {integrity: sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.3.1 - '@jest/expect': 29.3.1 - '@jest/test-result': 29.3.1 - '@jest/types': 29.3.1 - '@types/node': 18.11.18 - chalk: 4.1.2 - co: 4.6.0 - dedent: 0.7.0 - is-generator-fn: 2.1.0 - jest-each: 29.3.1 - jest-matcher-utils: 29.3.1 - jest-message-util: 29.3.1 - jest-runtime: 29.3.1 - jest-snapshot: 29.3.1 - jest-util: 29.3.1 - p-limit: 3.1.0 - pretty-format: 29.3.1 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-cli/29.3.1_@types+node@18.11.18: - resolution: {integrity: sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.3.1 - '@jest/test-result': 29.3.1 - '@jest/types': 29.3.1 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.10 - import-local: 3.1.0 - jest-config: 29.3.1_@types+node@18.11.18 - jest-util: 29.3.1 - jest-validate: 29.3.1 - prompts: 2.4.2 - yargs: 17.6.2 - transitivePeerDependencies: - - '@types/node' - - supports-color - - ts-node - dev: true - - /jest-config/29.3.1_@types+node@18.11.18: - resolution: {integrity: sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - dependencies: - '@babel/core': 7.20.5 - '@jest/test-sequencer': 29.3.1 - '@jest/types': 29.3.1 - '@types/node': 18.11.18 - babel-jest: 29.3.1_@babel+core@7.20.5 - chalk: 4.1.2 - ci-info: 3.7.0 - deepmerge: 4.2.2 - glob: 7.2.3 - graceful-fs: 4.2.10 - jest-circus: 29.3.1 - jest-environment-node: 29.3.1 - jest-get-type: 29.2.0 - jest-regex-util: 29.2.0 - jest-resolve: 29.3.1 - jest-runner: 29.3.1 - jest-util: 29.3.1 - jest-validate: 29.3.1 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 29.3.1 - slash: 3.0.0 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-diff/29.3.1: - resolution: {integrity: sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - diff-sequences: 29.3.1 - jest-get-type: 29.2.0 - pretty-format: 29.3.1 - dev: true - - /jest-docblock/29.2.0: - resolution: {integrity: sha512-bkxUsxTgWQGbXV5IENmfiIuqZhJcyvF7tU4zJ/7ioTutdz4ToB5Yx6JOFBpgI+TphRY4lhOyCWGNH/QFQh5T6A==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - detect-newline: 3.1.0 - dev: true - - /jest-each/29.3.1: - resolution: {integrity: sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.3.1 - chalk: 4.1.2 - jest-get-type: 29.2.0 - jest-util: 29.3.1 - pretty-format: 29.3.1 - dev: true - - /jest-environment-node/29.3.1: - resolution: {integrity: sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.3.1 - '@jest/fake-timers': 29.3.1 - '@jest/types': 29.3.1 - '@types/node': 18.11.18 - jest-mock: 29.3.1 - jest-util: 29.3.1 - dev: true - - /jest-get-type/29.2.0: - resolution: {integrity: sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - - /jest-haste-map/29.3.1: - resolution: {integrity: sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.3.1 - '@types/graceful-fs': 4.1.5 - '@types/node': 18.11.18 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.10 - jest-regex-util: 29.2.0 - jest-util: 29.3.1 - jest-worker: 29.3.1 - micromatch: 4.0.5 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.2 - dev: true - - /jest-leak-detector/29.3.1: - resolution: {integrity: sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.2.0 - pretty-format: 29.3.1 - dev: true - - /jest-matcher-utils/29.3.1: - resolution: {integrity: sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - jest-diff: 29.3.1 - jest-get-type: 29.2.0 - pretty-format: 29.3.1 - dev: true - - /jest-message-util/29.3.1: - resolution: {integrity: sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/code-frame': 7.18.6 - '@jest/types': 29.3.1 - '@types/stack-utils': 2.0.1 - chalk: 4.1.2 - graceful-fs: 4.2.10 - micromatch: 4.0.5 - pretty-format: 29.3.1 - slash: 3.0.0 - stack-utils: 2.0.6 - dev: true - - /jest-mock/29.3.1: - resolution: {integrity: sha512-H8/qFDtDVMFvFP4X8NuOT3XRDzOUTz+FeACjufHzsOIBAxivLqkB1PoLCaJx9iPPQ8dZThHPp/G3WRWyMgA3JA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.3.1 - '@types/node': 18.11.18 - jest-util: 29.3.1 - dev: true - - /jest-pnp-resolver/1.2.3_jest-resolve@29.3.1: - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - dependencies: - jest-resolve: 29.3.1 - dev: true - - /jest-regex-util/29.2.0: - resolution: {integrity: sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - - /jest-resolve-dependencies/29.3.1: - resolution: {integrity: sha512-Vk0cYq0byRw2WluNmNWGqPeRnZ3p3hHmjJMp2dyyZeYIfiBskwq4rpiuGFR6QGAdbj58WC7HN4hQHjf2mpvrLA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-regex-util: 29.2.0 - jest-snapshot: 29.3.1 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-resolve/29.3.1: - resolution: {integrity: sha512-amXJgH/Ng712w3Uz5gqzFBBjxV8WFLSmNjoreBGMqxgCz5cH7swmBZzgBaCIOsvb0NbpJ0vgaSFdJqMdT+rADw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.10 - jest-haste-map: 29.3.1 - jest-pnp-resolver: 1.2.3_jest-resolve@29.3.1 - jest-util: 29.3.1 - jest-validate: 29.3.1 - resolve: 1.22.1 - resolve.exports: 1.1.0 - slash: 3.0.0 - dev: true - - /jest-runner/29.3.1: - resolution: {integrity: sha512-oFvcwRNrKMtE6u9+AQPMATxFcTySyKfLhvso7Sdk/rNpbhg4g2GAGCopiInk1OP4q6gz3n6MajW4+fnHWlU3bA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.3.1 - '@jest/environment': 29.3.1 - '@jest/test-result': 29.3.1 - '@jest/transform': 29.3.1 - '@jest/types': 29.3.1 - '@types/node': 18.11.18 - chalk: 4.1.2 - emittery: 0.13.1 - graceful-fs: 4.2.10 - jest-docblock: 29.2.0 - jest-environment-node: 29.3.1 - jest-haste-map: 29.3.1 - jest-leak-detector: 29.3.1 - jest-message-util: 29.3.1 - jest-resolve: 29.3.1 - jest-runtime: 29.3.1 - jest-util: 29.3.1 - jest-watcher: 29.3.1 - jest-worker: 29.3.1 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-runtime/29.3.1: - resolution: {integrity: sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.3.1 - '@jest/fake-timers': 29.3.1 - '@jest/globals': 29.3.1 - '@jest/source-map': 29.2.0 - '@jest/test-result': 29.3.1 - '@jest/transform': 29.3.1 - '@jest/types': 29.3.1 - '@types/node': 18.11.18 - chalk: 4.1.2 - cjs-module-lexer: 1.2.2 - collect-v8-coverage: 1.0.1 - glob: 7.2.3 - graceful-fs: 4.2.10 - jest-haste-map: 29.3.1 - jest-message-util: 29.3.1 - jest-mock: 29.3.1 - jest-regex-util: 29.2.0 - jest-resolve: 29.3.1 - jest-snapshot: 29.3.1 - jest-util: 29.3.1 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-snapshot/29.3.1: - resolution: {integrity: sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.20.5 - '@babel/generator': 7.20.5 - '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.20.5 - '@babel/plugin-syntax-typescript': 7.20.0_@babel+core@7.20.5 - '@babel/traverse': 7.20.5 - '@babel/types': 7.19.4 - '@jest/expect-utils': 29.3.1 - '@jest/transform': 29.3.1 - '@jest/types': 29.3.1 - '@types/babel__traverse': 7.18.3 - '@types/prettier': 2.7.1 - babel-preset-current-node-syntax: 1.0.1_@babel+core@7.20.5 - chalk: 4.1.2 - expect: 29.3.1 - graceful-fs: 4.2.10 - jest-diff: 29.3.1 - jest-get-type: 29.2.0 - jest-haste-map: 29.3.1 - jest-matcher-utils: 29.3.1 - jest-message-util: 29.3.1 - jest-util: 29.3.1 - natural-compare: 1.4.0 - pretty-format: 29.3.1 - semver: 7.3.7 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-util/29.3.1: - resolution: {integrity: sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.3.1 - '@types/node': 18.11.18 - chalk: 4.1.2 - ci-info: 3.7.0 - graceful-fs: 4.2.10 - picomatch: 2.3.1 - dev: true - - /jest-validate/29.3.1: - resolution: {integrity: sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.3.1 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 29.2.0 - leven: 3.1.0 - pretty-format: 29.3.1 - dev: true - - /jest-watcher/29.3.1: - resolution: {integrity: sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.3.1 - '@jest/types': 29.3.1 - '@types/node': 18.11.18 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 29.3.1 - string-length: 4.0.2 - dev: true - /jest-worker/26.6.2: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} @@ -6907,36 +6191,6 @@ packages: supports-color: 8.1.1 dev: true - /jest-worker/29.3.1: - resolution: {integrity: sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@types/node': 18.11.18 - jest-util: 29.3.1 - merge-stream: 2.0.0 - supports-color: 8.1.1 - dev: true - - /jest/29.3.1_@types+node@18.11.18: - resolution: {integrity: sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.3.1 - '@jest/types': 29.3.1 - import-local: 3.1.0 - jest-cli: 29.3.1_@types+node@18.11.18 - transitivePeerDependencies: - - '@types/node' - - supports-color - - ts-node - dev: true - /js-sdsl/4.2.0: resolution: {integrity: sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==} dev: true @@ -7005,6 +6259,10 @@ packages: hasBin: true dev: true + /jsonc-parser/3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true + /jsonfile/4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: @@ -7043,11 +6301,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /kleur/3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} - dev: true - /kleur/4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} @@ -7214,6 +6467,11 @@ packages: json5: 2.2.1 dev: true + /local-pkg/0.4.2: + resolution: {integrity: sha512-mlERgSPrbxU3BP4qBqAvvwlgW4MTg78iwJdGGnv7kibKjWcJksrG3t6LB5lXI93wXRDvG4NpUgJFmTG4T6rdrg==} + engines: {node: '>=14'} + dev: true + /locate-path/5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -7236,10 +6494,6 @@ packages: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} dev: true - /lodash.memoize/4.1.2: - resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - dev: true - /lodash.merge/4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true @@ -7278,6 +6532,12 @@ packages: dependencies: js-tokens: 4.0.0 + /loupe/2.3.6: + resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} + dependencies: + get-func-name: 2.0.0 + dev: true + /lowercase-keys/3.0.0: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -7316,16 +6576,6 @@ packages: semver: 6.3.0 dev: true - /make-error/1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - dev: true - - /makeerror/1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - dependencies: - tmpl: 1.0.5 - dev: true - /map-obj/1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} @@ -7431,6 +6681,15 @@ packages: engines: {node: '>= 8.0.0'} dev: true + /mlly/1.0.0: + resolution: {integrity: sha512-QL108Hwt+u9bXdWgOI0dhzZfACovn5Aen4Xvc8Jasd9ouRH4NjnrXEiyP3nVvJo91zPlYjVRckta0Nt2zfoR6g==} + dependencies: + acorn: 8.8.1 + pathe: 1.0.0 + pkg-types: 1.0.1 + ufo: 1.0.1 + dev: true + /ms/2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} dev: true @@ -7603,10 +6862,6 @@ packages: formdata-polyfill: 4.0.10 dev: false - /node-int64/0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - dev: true - /node-releases/2.0.6: resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} dev: true @@ -7630,13 +6885,6 @@ packages: engines: {node: '>=14.16'} dev: true - /npm-run-path/4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - dependencies: - path-key: 3.1.1 - dev: true - /npm-run-path/5.1.0: resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -7860,6 +7108,18 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + /pathe/0.2.0: + resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==} + dev: true + + /pathe/1.0.0: + resolution: {integrity: sha512-nPdMG0Pd09HuSsr7QOKUXO2Jr9eqaDiZvDwdyIhNG5SHYujkQHYKDfGQkulBxvbDHz8oHLsTgKN86LSwYzSHAg==} + dev: true + + /pathval/1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + /perfect-freehand/1.2.0: resolution: {integrity: sha512-h/0ikF1M3phW7CwpZ5MMvKnfpHficWoOEyr//KVNTxV4F6deRK1eYMtHyBKEAKFK0aXIEUK9oBvlF6PNXMDsAw==} dev: false @@ -7900,11 +7160,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /pirates/4.0.5: - resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} - engines: {node: '>= 6'} - dev: true - /pkg-dir/4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} @@ -7912,6 +7167,14 @@ packages: find-up: 4.1.0 dev: true + /pkg-types/1.0.1: + resolution: {integrity: sha512-jHv9HB+Ho7dj6ItwppRDDl0iZRYBD0jsakHXtFgoLr+cHSF6xC+QL54sJmWxyGxOLYSHm0afhXhXcQDQqH9z8g==} + dependencies: + jsonc-parser: 3.2.0 + mlly: 1.0.0 + pathe: 1.0.0 + dev: true + /playwright-core/1.29.1: resolution: {integrity: sha512-20Ai3d+lMkWpI9YZYlxk8gxatfgax5STW8GaMozAHwigLiyiKQrdkt7gaoT9UQR8FIVDg6qVXs9IoZUQrDjIIg==} engines: {node: '>=14'} @@ -7926,6 +7189,15 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 + /postcss/8.4.21: + resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.4 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + /preferred-pm/3.0.3: resolution: {integrity: sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==} engines: {node: '>=10'} @@ -7957,27 +7229,10 @@ packages: engines: {node: '>=6'} dev: true - /pretty-format/29.3.1: - resolution: {integrity: sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.0.0 - ansi-styles: 5.2.0 - react-is: 18.2.0 - dev: true - /process-nextick-args/2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} dev: false - /prompts/2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - dev: true - /prop-types/15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} dependencies: @@ -8119,6 +7374,7 @@ packages: /react-is/18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: false /react-remove-scroll-bar/2.3.4_w5j4k42lgipnm43s3brx6h3c34: resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==} @@ -8343,13 +7599,6 @@ packages: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} dev: true - /resolve-cwd/3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} - dependencies: - resolve-from: 5.0.0 - dev: true - /resolve-from/4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -8359,11 +7608,6 @@ packages: engines: {node: '>=8'} dev: true - /resolve.exports/1.1.0: - resolution: {integrity: sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==} - engines: {node: '>=10'} - dev: true - /resolve/1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} dependencies: @@ -8437,6 +7681,14 @@ packages: fsevents: 2.3.2 dev: true + /rollup/3.9.1: + resolution: {integrity: sha512-GswCYHXftN8ZKGVgQhTFUJB/NBXxrRGgO2NCy6E8s1rwEJ4Q9/VttNqcYfEvx4dTo4j58YqdC3OVztPzlKSX8w==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + /run-parallel/1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: @@ -8570,10 +7822,6 @@ packages: - supports-color dev: false - /sisteransi/1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - dev: true - /slash/3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -8626,13 +7874,6 @@ packages: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} - /source-map-support/0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - dev: true - /source-map-support/0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} dependencies: @@ -8695,13 +7936,6 @@ packages: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: true - /stack-utils/2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} - dependencies: - escape-string-regexp: 2.0.0 - dev: true - /stream-transform/2.1.3: resolution: {integrity: sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ==} dependencies: @@ -8713,14 +7947,6 @@ packages: engines: {node: '>=0.6.19'} dev: true - /string-length/4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} - engines: {node: '>=10'} - dependencies: - char-regex: 1.0.2 - strip-ansi: 6.0.1 - dev: true - /string-width/4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -8810,21 +8036,11 @@ packages: engines: {node: '>=4'} dev: true - /strip-bom/4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - dev: true - /strip-comments/2.0.1: resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} engines: {node: '>=10'} dev: true - /strip-final-newline/2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - dev: true - /strip-final-newline/3.0.0: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} @@ -8842,6 +8058,12 @@ packages: engines: {node: '>=8'} dev: true + /strip-literal/1.0.0: + resolution: {integrity: sha512-5o4LsH1lzBzO9UFH63AJ2ad2/S2AVx6NtjOcaz+VTT2h1RiRvbipW72z8M/lxEhcPHDBQwpDrnTF7sXy/7OwCQ==} + dependencies: + acorn: 8.8.1 + dev: true + /styled-jsx/5.0.7_react@18.2.0: resolution: {integrity: sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==} engines: {node: '>= 12.0.0'} @@ -8965,15 +8187,6 @@ packages: source-map-support: 0.5.21 dev: true - /test-exclude/6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 7.2.3 - minimatch: 3.1.2 - dev: true - /text-table/0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true @@ -8988,6 +8201,20 @@ packages: xtend: 2.1.2 dev: false + /tinybench/2.3.1: + resolution: {integrity: sha512-hGYWYBMPr7p4g5IarQE7XhlyWveh1EKhy4wUBS1LrHXCKYgvz+4/jCqgmJqZxxldesn05vccrtME2RLLZNW7iA==} + dev: true + + /tinypool/0.3.0: + resolution: {integrity: sha512-NX5KeqHOBZU6Bc0xj9Vr5Szbb1j8tUHIeD18s41aDJaPeC5QTdEhK0SpdpUrZlj2nv5cctNcSjaKNanXlfcVEQ==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy/1.0.2: + resolution: {integrity: sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==} + engines: {node: '>=14.0.0'} + dev: true + /tmp/0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -8995,10 +8222,6 @@ packages: os-tmpdir: 1.0.2 dev: true - /tmpl/1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - dev: true - /to-fast-properties/2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} @@ -9032,39 +8255,6 @@ packages: engines: {node: '>=8'} dev: true - /ts-jest/29.0.3_4f6uxrzmuwipl5rr3bcogf6k74: - resolution: {integrity: sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - '@babel/core': '>=7.0.0-beta.0 <8' - '@jest/types': ^29.0.0 - babel-jest: ^29.0.0 - esbuild: '*' - jest: ^29.0.0 - typescript: '>=4.3' - peerDependenciesMeta: - '@babel/core': - optional: true - '@jest/types': - optional: true - babel-jest: - optional: true - esbuild: - optional: true - dependencies: - bs-logger: 0.2.6 - fast-json-stable-stringify: 2.1.0 - jest: 29.3.1_@types+node@18.11.18 - jest-util: 29.3.1 - json5: 2.2.1 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.3.7 - typescript: 4.9.3 - yargs-parser: 21.1.1 - dev: true - /tsconfig-paths/3.14.1: resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==} dependencies: @@ -9195,6 +8385,10 @@ packages: engines: {node: '>=0.1.14'} dev: true + /ufo/1.0.1: + resolution: {integrity: sha512-boAm74ubXHY7KJQZLlXrtMz52qFvpsbOxDcZOnw/Wf+LS4Mmyu7JxmzD4tDLtUQtmZECypJ0FrCz4QIe6dvKRA==} + dev: true + /unbox-primitive/1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: @@ -9319,15 +8513,6 @@ packages: resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} dev: true - /v8-to-istanbul/9.0.1: - resolution: {integrity: sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==} - engines: {node: '>=10.12.0'} - dependencies: - '@jridgewell/trace-mapping': 0.3.17 - '@types/istanbul-lib-coverage': 2.0.4 - convert-source-map: 1.9.0 - dev: true - /validate-npm-package-license/3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: @@ -9335,17 +8520,112 @@ packages: spdx-expression-parse: 3.0.1 dev: true + /vite-node/0.26.3_@types+node@18.11.18: + resolution: {integrity: sha512-Te2bq0Bfvq6XiO718I+1EinMjpNYKws6SNHKOmVbILAQimKoZKDd+IZLlkaYcBXPpK3HFe2U80k8Zw+m3w/a2w==} + engines: {node: '>=v14.16.0'} + hasBin: true + dependencies: + debug: 4.3.4 + mlly: 1.0.0 + pathe: 0.2.0 + source-map: 0.6.1 + source-map-support: 0.5.21 + vite: 4.0.4_@types+node@18.11.18 + transitivePeerDependencies: + - '@types/node' + - less + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vite/4.0.4_@types+node@18.11.18: + resolution: {integrity: sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 18.11.18 + esbuild: 0.16.15 + postcss: 8.4.21 + resolve: 1.22.1 + rollup: 3.9.1 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /vitest/0.26.3: + resolution: {integrity: sha512-FmHxU9aUCxTi23keF3vxb/Qp0lYXaaJ+jRLGOUmMS3qVTOJvgGE+f1VArupA6pEhaG2Ans4X+zV9dqM5WISMbg==} + engines: {node: '>=v14.16.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/chai': 4.3.4 + '@types/chai-subset': 1.3.3 + '@types/node': 18.11.18 + acorn: 8.8.1 + acorn-walk: 8.2.0 + chai: 4.3.7 + debug: 4.3.4 + local-pkg: 0.4.2 + source-map: 0.6.1 + strip-literal: 1.0.0 + tinybench: 2.3.1 + tinypool: 0.3.0 + tinyspy: 1.0.2 + vite: 4.0.4_@types+node@18.11.18 + vite-node: 0.26.3_@types+node@18.11.18 + transitivePeerDependencies: + - less + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /void-elements/3.1.0: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} dev: false - /walker/1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - dependencies: - makeerror: 1.0.12 - dev: true - /wcwidth/1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: @@ -9639,14 +8919,6 @@ packages: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true - /write-file-atomic/4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - dev: true - /ws/7.5.9: resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==} engines: {node: '>=8.3.0'} diff --git a/tests/quick-search.spec.ts b/tests/quick-search.spec.ts index 242e09bc51..1fd1b1efb9 100644 --- a/tests/quick-search.spec.ts +++ b/tests/quick-search.spec.ts @@ -7,10 +7,13 @@ loadPage(); const openQuickSearchByShortcut = async (page: Page) => await withCtrlOrMeta(page, () => page.keyboard.press('k', { delay: 50 })); -async function assertTitleTexts(page: Page, texts: string[]) { - const actual = await page - .locator('.affine-default-page-block-title') - .allTextContents(); +async function assertTitleTexts(page: Page, texts: string) { + const actual = await page.evaluate(() => { + const titleElement = ( + document.querySelector('.affine-default-page-block-title') + ); + return titleElement.value; + }); expect(actual).toEqual(texts); } async function assertResultList(page: Page, texts: string[]) { @@ -55,7 +58,7 @@ test.describe('Add new page in quick search', () => { const addNewPage = page.locator('[data-testid=quickSearch-addNewPage]'); await addNewPage.click(); await page.waitForTimeout(200); - await assertTitleTexts(page, ['']); + await assertTitleTexts(page, ''); }); test('Create a new page with keyword', async ({ page }) => { @@ -65,7 +68,7 @@ test.describe('Add new page in quick search', () => { const addNewPage = page.locator('[data-testid=quickSearch-addNewPage]'); await addNewPage.click(); await page.waitForTimeout(200); - await assertTitleTexts(page, ['test123456']); + await assertTitleTexts(page, 'test123456'); }); }); @@ -81,6 +84,6 @@ test.describe('Search and select', () => { await page.keyboard.insertText('test123456'); await assertResultList(page, ['test123456']); await page.keyboard.press('Enter', { delay: 50 }); - await assertTitleTexts(page, ['test123456']); + await assertTitleTexts(page, 'test123456'); }); }); diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000000..4c6feaa4dd --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['packages/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + }, +});