From 69721f2a616b160f0830bf8b8585712a3b832d6e Mon Sep 17 00:00:00 2001 From: Himself65 Date: Thu, 23 Mar 2023 11:17:38 -0500 Subject: [PATCH] refactor: init package `@affine/workspace` (#1661) --- apps/web/package.json | 1 + apps/web/src/atoms/index.ts | 5 +- apps/web/src/atoms/public-workspace/index.ts | 2 +- .../components/__debug__/client/Editor.tsx | 2 +- .../__tests__/WorkSpaceSliderBar.spec.tsx | 5 +- .../affine/workspace-setting-detail/index.tsx | 20 +- .../panel/collaboration/index.tsx | 10 +- .../panel/general/delete/index.tsx | 4 +- .../panel/general/index.tsx | 4 +- .../panel/publish/index.tsx | 10 +- .../panel/sync/index.tsx | 4 +- .../header/header-right-items/SyncUser.tsx | 8 +- .../components/pure/workspace-card/index.tsx | 23 +- apps/web/src/hooks/__tests__/index.spec.tsx | 9 +- .../src/hooks/use-create-first-workspace.ts | 8 +- ...-router-with-current-workspace-and-page.ts | 6 +- apps/web/src/hooks/use-transform-workspace.ts | 7 +- apps/web/src/hooks/use-workspaces.ts | 6 +- apps/web/src/pages/_debug/broadcast.dev.tsx | 2 +- apps/web/src/pages/_debug/login.dev.tsx | 78 +++++ apps/web/src/pages/preview/[previewId].tsx | 2 +- .../workspace/[workspaceId]/[pageId].tsx | 6 +- .../src/pages/workspace/[workspaceId]/all.tsx | 6 +- .../pages/workspace/[workspaceId]/setting.tsx | 28 +- apps/web/src/plugins/affine/fetcher.ts | 6 +- apps/web/src/plugins/affine/index.tsx | 17 +- apps/web/src/plugins/index.tsx | 75 +---- apps/web/src/plugins/local/index.tsx | 15 +- apps/web/src/shared/apis.ts | 2 +- apps/web/src/shared/index.ts | 31 +- apps/web/src/utils/index.ts | 26 -- package.json | 1 + packages/env/package.json | 5 + packages/env/src/constant.ts | 66 ++++ packages/workspace/package.json | 22 ++ .../src/affine/__tests__/atom.spec.ts | 20 ++ .../src/affine/__tests__/login.spec.ts | 42 +++ packages/workspace/src/affine/atom.ts | 4 + packages/workspace/src/affine/login.ts | 175 ++++++++++ packages/workspace/src/type.ts | 74 ++++ packages/workspace/src/utils.ts | 24 ++ packages/workspace/tsconfig.json | 4 + tsconfig.json | 6 +- yarn.lock | 317 +++++++++++++++++- 44 files changed, 952 insertions(+), 236 deletions(-) create mode 100644 apps/web/src/pages/_debug/login.dev.tsx create mode 100644 packages/workspace/package.json create mode 100644 packages/workspace/src/affine/__tests__/atom.spec.ts create mode 100644 packages/workspace/src/affine/__tests__/login.spec.ts create mode 100644 packages/workspace/src/affine/atom.ts create mode 100644 packages/workspace/src/affine/login.ts create mode 100644 packages/workspace/src/type.ts create mode 100644 packages/workspace/src/utils.ts create mode 100644 packages/workspace/tsconfig.json diff --git a/apps/web/package.json b/apps/web/package.json index d2c1cc622..58ceb588e 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -15,6 +15,7 @@ "@affine/env": "workspace:*", "@affine/i18n": "workspace:*", "@affine/templates": "workspace:*", + "@affine/workspace": "workspace:*", "@blocksuite/blocks": "0.5.0-20230323085636-3110abb", "@blocksuite/editor": "0.5.0-20230323085636-3110abb", "@blocksuite/icons": "2.0.23", diff --git a/apps/web/src/atoms/index.ts b/apps/web/src/atoms/index.ts index 77b5dedd5..558e7bced 100644 --- a/apps/web/src/atoms/index.ts +++ b/apps/web/src/atoms/index.ts @@ -1,3 +1,4 @@ +import type { WorkspaceFlavour } from '@affine/workspace/type'; import type { EditorContainer } from '@blocksuite/editor'; import type { Page } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store'; @@ -6,7 +7,7 @@ import { atomWithStorage } from 'jotai/utils'; import { unstable_batchedUpdates } from 'react-dom'; import { WorkspacePlugins } from '../plugins'; -import type { RemWorkspace, RemWorkspaceFlavour } from '../shared'; +import type { RemWorkspace } from '../shared'; // workspace necessary atoms export const currentWorkspaceIdAtom = atom(null); export const currentPageIdAtom = atom(null); @@ -37,7 +38,7 @@ export const jotaiStore = createStore(); type JotaiWorkspace = { id: string; - flavour: RemWorkspaceFlavour; + flavour: WorkspaceFlavour; }; export const jotaiWorkspacesAtom = atomWithStorage( diff --git a/apps/web/src/atoms/public-workspace/index.ts b/apps/web/src/atoms/public-workspace/index.ts index b7032955f..be780d6f4 100644 --- a/apps/web/src/atoms/public-workspace/index.ts +++ b/apps/web/src/atoms/public-workspace/index.ts @@ -1,8 +1,8 @@ +import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils'; import { atom } from 'jotai/index'; import { BlockSuiteWorkspace } from '../../shared'; import { apis } from '../../shared/apis'; -import { createEmptyBlockSuiteWorkspace } from '../../utils'; export const publicWorkspaceIdAtom = atom(null); export const publicBlockSuiteAtom = atom>( diff --git a/apps/web/src/components/__debug__/client/Editor.tsx b/apps/web/src/components/__debug__/client/Editor.tsx index 4aaf6f3d6..7486db72d 100644 --- a/apps/web/src/components/__debug__/client/Editor.tsx +++ b/apps/web/src/components/__debug__/client/Editor.tsx @@ -1,11 +1,11 @@ 'use client'; +import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils'; import type { EditorContainer } from '@blocksuite/editor'; import type { Page } from '@blocksuite/store'; import { Generator } from '@blocksuite/store'; import type React from 'react'; import { useCallback, useRef } from 'react'; -import { createEmptyBlockSuiteWorkspace } from '../../../utils'; import { BlockSuiteEditor } from '../../blocksuite/block-suite-editor'; const blockSuiteWorkspace = createEmptyBlockSuiteWorkspace( diff --git a/apps/web/src/components/__tests__/WorkSpaceSliderBar.spec.tsx b/apps/web/src/components/__tests__/WorkSpaceSliderBar.spec.tsx index 7ae7c001c..ace40f5d3 100644 --- a/apps/web/src/components/__tests__/WorkSpaceSliderBar.spec.tsx +++ b/apps/web/src/components/__tests__/WorkSpaceSliderBar.spec.tsx @@ -3,6 +3,7 @@ */ import 'fake-indexeddb/auto'; +import { WorkspaceFlavour } from '@affine/workspace/type'; import { assertExists } from '@blocksuite/store'; import { render, renderHook } from '@testing-library/react'; import { createStore, getDefaultStore, Provider } from 'jotai'; @@ -20,7 +21,7 @@ import { import { useBlockSuiteWorkspaceHelper } from '../../hooks/use-blocksuite-workspace-helper'; import { useWorkspacesHelper } from '../../hooks/use-workspaces'; import { ThemeProvider } from '../../providers/ThemeProvider'; -import { pathGenerator, RemWorkspaceFlavour } from '../../shared'; +import { pathGenerator } from '../../shared'; import { WorkSpaceSliderBar } from '../pure/workspace-slider-bar'; vi.mock('../blocksuite/header/editor-mode-switch/CustomLottie', () => ({ @@ -92,7 +93,7 @@ describe('WorkSpaceSliderBar', () => { currentWorkspaceHook.result.current[1](id); const currentWorkspace = await store.get(currentWorkspaceAtom); expect(currentWorkspace).toBeDefined(); - expect(currentWorkspace?.flavour).toBe(RemWorkspaceFlavour.LOCAL); + expect(currentWorkspace?.flavour).toBe(WorkspaceFlavour.LOCAL); expect(currentWorkspace?.id).toBe(id); const app = render(); const card = await app.findByTestId('current-workspace'); diff --git a/apps/web/src/components/affine/workspace-setting-detail/index.tsx b/apps/web/src/components/affine/workspace-setting-detail/index.tsx index 6430aede7..03d0983b3 100644 --- a/apps/web/src/components/affine/workspace-setting-detail/index.tsx +++ b/apps/web/src/components/affine/workspace-setting-detail/index.tsx @@ -1,4 +1,6 @@ import { useTranslation } from '@affine/i18n'; +import type { SettingPanel, WorkspaceRegistry } from '@affine/workspace/type'; +import { settingPanel, WorkspaceFlavour } from '@affine/workspace/type'; import type { MouseEvent } from 'react'; import type React from 'react'; import { Suspense, useCallback, useEffect, useMemo, useRef } from 'react'; @@ -6,12 +8,7 @@ import { preload } from 'swr'; import { useIsWorkspaceOwner } from '../../../hooks/affine/use-is-workspace-owner'; import { fetcher, QueryKey } from '../../../plugins/affine/fetcher'; -import type { - AffineOfficialWorkspace, - FlavourToWorkspace, - SettingPanel, -} from '../../../shared'; -import { RemWorkspaceFlavour, settingPanel } from '../../../shared'; +import type { AffineOfficialWorkspace } from '../../../shared'; import { CollaborationPanel } from './panel/collaboration'; import { ExportPanel } from './panel/export'; import { GeneralPanel } from './panel/general'; @@ -31,12 +28,12 @@ export type WorkspaceSettingDetailProps = { onChangeTab: (tab: SettingPanel) => void; onDeleteWorkspace: () => void; onTransferWorkspace: < - From extends RemWorkspaceFlavour, - To extends RemWorkspaceFlavour + From extends WorkspaceFlavour, + To extends WorkspaceFlavour >( from: From, to: To, - workspace: FlavourToWorkspace[From] + workspace: WorkspaceRegistry[From] ) => void; }; @@ -49,8 +46,7 @@ const panelMap = { }, [settingPanel.Sync]: { name: 'Sync', - enable: (flavour: RemWorkspaceFlavour) => - flavour === RemWorkspaceFlavour.AFFINE, + enable: (flavour: WorkspaceFlavour) => flavour === WorkspaceFlavour.AFFINE, ui: SyncPanel, }, [settingPanel.Collaboration]: { @@ -68,7 +64,7 @@ const panelMap = { } satisfies { [Key in SettingPanel]: { name: string; - enable?: (flavour: RemWorkspaceFlavour) => boolean; + enable?: (flavour: WorkspaceFlavour) => boolean; ui: React.FC; }; }; diff --git a/apps/web/src/components/affine/workspace-setting-detail/panel/collaboration/index.tsx b/apps/web/src/components/affine/workspace-setting-detail/panel/collaboration/index.tsx index 391936480..ea01204be 100644 --- a/apps/web/src/components/affine/workspace-setting-detail/panel/collaboration/index.tsx +++ b/apps/web/src/components/affine/workspace-setting-detail/panel/collaboration/index.tsx @@ -8,6 +8,7 @@ import { } from '@affine/component'; import { PermissionType } from '@affine/datacenter'; import { useTranslation } from '@affine/i18n'; +import { WorkspaceFlavour } from '@affine/workspace/type'; import { DeleteTemporarilyIcon, EmailIcon, @@ -18,7 +19,6 @@ import { useCallback, useState } from 'react'; import { useMembers } from '../../../../../hooks/affine/use-members'; import type { AffineWorkspace, LocalWorkspace } from '../../../../../shared'; -import { RemWorkspaceFlavour } from '../../../../../shared'; import { Unreachable } from '../../../affine-error-eoundary'; import { TransformWorkspaceToAffineModal } from '../../../transform-workspace-to-affine-modal'; import type { PanelProps } from '../../index'; @@ -194,8 +194,8 @@ const LocalCollaborationPanel: React.FC< }} onConform={() => { onTransferWorkspace( - RemWorkspaceFlavour.LOCAL, - RemWorkspaceFlavour.AFFINE, + WorkspaceFlavour.LOCAL, + WorkspaceFlavour.AFFINE, workspace ); setOpen(false); @@ -207,13 +207,13 @@ const LocalCollaborationPanel: React.FC< export const CollaborationPanel: React.FC = props => { switch (props.workspace.flavour) { - case RemWorkspaceFlavour.AFFINE: { + case WorkspaceFlavour.AFFINE: { const workspace = props.workspace as AffineWorkspace; return ( ); } - case RemWorkspaceFlavour.LOCAL: { + case WorkspaceFlavour.LOCAL: { const workspace = props.workspace as LocalWorkspace; return ; } diff --git a/apps/web/src/components/affine/workspace-setting-detail/panel/general/delete/index.tsx b/apps/web/src/components/affine/workspace-setting-detail/panel/general/delete/index.tsx index 78a335a33..fe2d4925e 100644 --- a/apps/web/src/components/affine/workspace-setting-detail/panel/general/delete/index.tsx +++ b/apps/web/src/components/affine/workspace-setting-detail/panel/general/delete/index.tsx @@ -1,10 +1,10 @@ import { Button, Input, Modal, ModalCloseButton } from '@affine/component'; import { Trans, useTranslation } from '@affine/i18n'; +import { WorkspaceFlavour } from '@affine/workspace/type'; import { useCallback, useState } from 'react'; import { useBlockSuiteWorkspaceName } from '../../../../../../hooks/use-blocksuite-workspace-name'; import type { AffineOfficialWorkspace } from '../../../../../../shared'; -import { RemWorkspaceFlavour } from '../../../../../../shared'; import { StyledButtonContent, StyledInputContent, @@ -43,7 +43,7 @@ export const WorkspaceDeleteModal = ({ {t('Delete Workspace')}? - {workspace.flavour === RemWorkspaceFlavour.LOCAL ? ( + {workspace.flavour === WorkspaceFlavour.LOCAL ? ( Deleting ( diff --git a/apps/web/src/components/affine/workspace-setting-detail/panel/general/index.tsx b/apps/web/src/components/affine/workspace-setting-detail/panel/general/index.tsx index 02a6e6998..ed2be56ac 100644 --- a/apps/web/src/components/affine/workspace-setting-detail/panel/general/index.tsx +++ b/apps/web/src/components/affine/workspace-setting-detail/panel/general/index.tsx @@ -1,12 +1,12 @@ import { Button, FlexWrapper, MuiFade } from '@affine/component'; import { useTranslation } from '@affine/i18n'; +import { WorkspaceFlavour } from '@affine/workspace/type'; import type React from 'react'; import { useState } from 'react'; import { useIsWorkspaceOwner } from '../../../../../hooks/affine/use-is-workspace-owner'; import { useBlockSuiteWorkspaceAvatarUrl } from '../../../../../hooks/use-blocksuite-workspace-avatar-url'; import { useBlockSuiteWorkspaceName } from '../../../../../hooks/use-blocksuite-workspace-name'; -import { RemWorkspaceFlavour } from '../../../../../shared'; import { Upload } from '../../../../pure/file-upload'; import { CloudWorkspaceIcon, @@ -161,7 +161,7 @@ export const GeneralPanel: React.FC = ({ {t('Workspace Type')} {isOwner ? ( - workspace.flavour === RemWorkspaceFlavour.LOCAL ? ( + workspace.flavour === WorkspaceFlavour.LOCAL ? ( {t('Local Workspace')} diff --git a/apps/web/src/components/affine/workspace-setting-detail/panel/publish/index.tsx b/apps/web/src/components/affine/workspace-setting-detail/panel/publish/index.tsx index dde88a333..1bd49b889 100644 --- a/apps/web/src/components/affine/workspace-setting-detail/panel/publish/index.tsx +++ b/apps/web/src/components/affine/workspace-setting-detail/panel/publish/index.tsx @@ -7,6 +7,7 @@ import { Wrapper, } from '@affine/component'; import { useTranslation } from '@affine/i18n'; +import { WorkspaceFlavour } from '@affine/workspace/type'; import { Box } from '@mui/material'; import type React from 'react'; import { useCallback, useEffect, useState } from 'react'; @@ -17,7 +18,6 @@ import type { AffineWorkspace, LocalWorkspace, } from '../../../../../shared'; -import { RemWorkspaceFlavour } from '../../../../../shared'; import { Unreachable } from '../../../affine-error-eoundary'; import { EnableAffineCloudModal } from '../../../enable-affine-cloud-modal'; import type { WorkspaceSettingDetailProps } from '../../index'; @@ -133,8 +133,8 @@ const PublishPanelLocal: React.FC = ({ }} onConfirm={() => { onTransferWorkspace( - RemWorkspaceFlavour.LOCAL, - RemWorkspaceFlavour.AFFINE, + WorkspaceFlavour.LOCAL, + WorkspaceFlavour.AFFINE, workspace ); setOpen(false); @@ -145,9 +145,9 @@ const PublishPanelLocal: React.FC = ({ }; export const PublishPanel: React.FC = props => { - if (props.workspace.flavour === RemWorkspaceFlavour.AFFINE) { + if (props.workspace.flavour === WorkspaceFlavour.AFFINE) { return ; - } else if (props.workspace.flavour === RemWorkspaceFlavour.LOCAL) { + } else if (props.workspace.flavour === WorkspaceFlavour.LOCAL) { return ; } throw new Unreachable(); diff --git a/apps/web/src/components/affine/workspace-setting-detail/panel/sync/index.tsx b/apps/web/src/components/affine/workspace-setting-detail/panel/sync/index.tsx index 69a616466..12d6d1641 100644 --- a/apps/web/src/components/affine/workspace-setting-detail/panel/sync/index.tsx +++ b/apps/web/src/components/affine/workspace-setting-detail/panel/sync/index.tsx @@ -1,11 +1,11 @@ import { Content, FlexWrapper, styled } from '@affine/component'; import { Trans, useTranslation } from '@affine/i18n'; +import { WorkspaceFlavour } from '@affine/workspace/type'; import type React from 'react'; import { useCurrentUser } from '../../../../../hooks/current/use-current-user'; import { useBlockSuiteWorkspaceAvatarUrl } from '../../../../../hooks/use-blocksuite-workspace-avatar-url'; import { useBlockSuiteWorkspaceName } from '../../../../../hooks/use-blocksuite-workspace-name'; -import { RemWorkspaceFlavour } from '../../../../../shared'; import { WorkspaceAvatar } from '../../../../pure/footer'; import type { PanelProps } from '../../index'; @@ -17,7 +17,7 @@ export const StyledWorkspaceName = styled('span')(({ theme }) => { }); export const SyncPanel: React.FC = ({ workspace }) => { - if (workspace.flavour !== RemWorkspaceFlavour.AFFINE) { + if (workspace.flavour !== WorkspaceFlavour.AFFINE) { throw new TypeError('SyncPanel can only be used with Affine workspace'); } const [name] = useBlockSuiteWorkspaceName(workspace.blockSuiteWorkspace); diff --git a/apps/web/src/components/blocksuite/header/header-right-items/SyncUser.tsx b/apps/web/src/components/blocksuite/header/header-right-items/SyncUser.tsx index bacb6f63c..dd0d2a574 100644 --- a/apps/web/src/components/blocksuite/header/header-right-items/SyncUser.tsx +++ b/apps/web/src/components/blocksuite/header/header-right-items/SyncUser.tsx @@ -1,5 +1,6 @@ import { displayFlex, IconButton, styled, Tooltip } from '@affine/component'; import { useTranslation } from '@affine/i18n'; +import { WorkspaceFlavour } from '@affine/workspace/type'; import { CloudWorkspaceIcon, LocalWorkspaceIcon, @@ -15,7 +16,6 @@ import type { AffineOfficialWorkspace, LocalWorkspace, } from '../../../../shared'; -import { RemWorkspaceFlavour } from '../../../../shared'; import { apis } from '../../../../shared/apis'; import { TransformWorkspaceToAffineModal } from '../../../affine/transform-workspace-to-affine-modal'; @@ -117,10 +117,10 @@ export const SyncUser = () => { router.reload(); return; } - assertEquals(workspace.flavour, RemWorkspaceFlavour.LOCAL); + assertEquals(workspace.flavour, WorkspaceFlavour.LOCAL); const id = await transformWorkspace( - RemWorkspaceFlavour.LOCAL, - RemWorkspaceFlavour.AFFINE, + WorkspaceFlavour.LOCAL, + WorkspaceFlavour.AFFINE, workspace as LocalWorkspace ); // fixme(himself65): refactor this diff --git a/apps/web/src/components/pure/workspace-card/index.tsx b/apps/web/src/components/pure/workspace-card/index.tsx index 15b295de5..e2843e2cb 100644 --- a/apps/web/src/components/pure/workspace-card/index.tsx +++ b/apps/web/src/components/pure/workspace-card/index.tsx @@ -1,12 +1,12 @@ import { PermissionType } from '@affine/datacenter'; import { useTranslation } from '@affine/i18n'; +import { WorkspaceFlavour } from '@affine/workspace/type'; import { SettingsIcon } from '@blocksuite/icons'; import type React from 'react'; import { useCallback } from 'react'; import { useBlockSuiteWorkspaceName } from '../../../hooks/use-blocksuite-workspace-name'; import type { RemWorkspace } from '../../../shared'; -import { RemWorkspaceFlavour } from '../../../shared'; import { CloudWorkspaceIcon, JoinedWorkspaceIcon, @@ -28,13 +28,13 @@ export type WorkspaceTypeProps = { const WorkspaceType: React.FC = ({ workspace }) => { const { t } = useTranslation(); let isOwner = true; - if (workspace.flavour === RemWorkspaceFlavour.AFFINE) { + if (workspace.flavour === WorkspaceFlavour.AFFINE) { isOwner = workspace.permission === PermissionType.Owner; - } else if (workspace.flavour === RemWorkspaceFlavour.LOCAL) { + } else if (workspace.flavour === WorkspaceFlavour.LOCAL) { isOwner = true; } - if (workspace.flavour === RemWorkspaceFlavour.LOCAL) { + if (workspace.flavour === WorkspaceFlavour.LOCAL) { return (

@@ -85,19 +85,18 @@ export const WorkspaceCard: React.FC = ({ {name} - {workspace.flavour === RemWorkspaceFlavour.LOCAL && ( + {workspace.flavour === WorkspaceFlavour.LOCAL && (

{t('Available Offline')}

)} - {workspace.flavour === RemWorkspaceFlavour.AFFINE && - workspace.public && ( -

- - {t('Published to Web')} -

- )} + {workspace.flavour === WorkspaceFlavour.AFFINE && workspace.public && ( +

+ + {t('Published to Web')} +

+ )} { expect(result2.current.length).toEqual(1); const firstWorkspace = result2.current[0]; expect(firstWorkspace.flavour).toBe('local'); - assert(firstWorkspace.flavour === RemWorkspaceFlavour.LOCAL); + assert(firstWorkspace.flavour === WorkspaceFlavour.LOCAL); expect(firstWorkspace.blockSuiteWorkspace.meta.name).toBe('test'); }); }); @@ -266,12 +267,12 @@ describe('useRecentlyViewed', () => { store.set(jotaiWorkspacesAtom, [ { id: workspaceId, - flavour: RemWorkspaceFlavour.LOCAL, + flavour: WorkspaceFlavour.LOCAL, }, ]); LocalPlugin.CRUD.get = vi.fn().mockResolvedValue({ id: workspaceId, - flavour: RemWorkspaceFlavour.LOCAL, + flavour: WorkspaceFlavour.LOCAL, blockSuiteWorkspace, providers: [], } satisfies LocalWorkspace); diff --git a/apps/web/src/hooks/use-create-first-workspace.ts b/apps/web/src/hooks/use-create-first-workspace.ts index 366bae6a9..bcd2f14ef 100644 --- a/apps/web/src/hooks/use-create-first-workspace.ts +++ b/apps/web/src/hooks/use-create-first-workspace.ts @@ -1,12 +1,12 @@ import { DEFAULT_WORKSPACE_NAME } from '@affine/env'; +import { WorkspaceFlavour } from '@affine/workspace/type'; +import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils'; import { assertEquals, assertExists, nanoid } from '@blocksuite/store'; -import { useAtom } from 'jotai/index'; +import { useAtom } from 'jotai'; import { useEffect } from 'react'; import { jotaiWorkspacesAtom } from '../atoms'; import { LocalPlugin } from '../plugins/local'; -import { RemWorkspaceFlavour } from '../shared'; -import { createEmptyBlockSuiteWorkspace } from '../utils'; export function useCreateFirstWorkspace() { const [jotaiWorkspaces, set] = useAtom(jotaiWorkspacesAtom); @@ -29,7 +29,7 @@ export function useCreateFirstWorkspace() { set([ { id: workspace.id, - flavour: RemWorkspaceFlavour.LOCAL, + flavour: WorkspaceFlavour.LOCAL, }, ]); } diff --git a/apps/web/src/hooks/use-sync-router-with-current-workspace-and-page.ts b/apps/web/src/hooks/use-sync-router-with-current-workspace-and-page.ts index cfed36f66..05cb11486 100644 --- a/apps/web/src/hooks/use-sync-router-with-current-workspace-and-page.ts +++ b/apps/web/src/hooks/use-sync-router-with-current-workspace-and-page.ts @@ -1,9 +1,9 @@ +import { WorkspaceFlavour } from '@affine/workspace/type'; import type { NextRouter } from 'next/router'; import { useEffect } from 'react'; import { currentPageIdAtom, jotaiStore } from '../atoms'; import type { RemWorkspace } from '../shared'; -import { RemWorkspaceFlavour } from '../shared'; import { useCurrentPageId } from './current/use-current-page-id'; import { useCurrentWorkspace } from './current/use-current-workspace'; import { useWorkspaces } from './use-workspaces'; @@ -13,7 +13,7 @@ export function findSuitablePageId( targetId: string ): string | null { switch (workspace.flavour) { - case RemWorkspaceFlavour.AFFINE: { + case WorkspaceFlavour.AFFINE: { return ( workspace.blockSuiteWorkspace.meta.pageMetas.find( page => page.id === targetId @@ -22,7 +22,7 @@ export function findSuitablePageId( null ); } - case RemWorkspaceFlavour.LOCAL: { + case WorkspaceFlavour.LOCAL: { return ( workspace.blockSuiteWorkspace.meta.pageMetas.find( page => page.id === targetId diff --git a/apps/web/src/hooks/use-transform-workspace.ts b/apps/web/src/hooks/use-transform-workspace.ts index 37574e40e..10fc9cf46 100644 --- a/apps/web/src/hooks/use-transform-workspace.ts +++ b/apps/web/src/hooks/use-transform-workspace.ts @@ -1,9 +1,10 @@ +import type { WorkspaceFlavour } from '@affine/workspace/type'; +import type { WorkspaceRegistry } from '@affine/workspace/type'; import { useSetAtom } from 'jotai'; import { useCallback } from 'react'; import { jotaiWorkspacesAtom } from '../atoms'; import { WorkspacePlugins } from '../plugins'; -import type { FlavourToWorkspace, RemWorkspaceFlavour } from '../shared'; /** * Transform workspace from one flavour to another @@ -13,10 +14,10 @@ import type { FlavourToWorkspace, RemWorkspaceFlavour } from '../shared'; export function useTransformWorkspace() { const set = useSetAtom(jotaiWorkspacesAtom); return useCallback( - async ( + async ( from: From, to: To, - workspace: FlavourToWorkspace[From] + workspace: WorkspaceRegistry[From] ): Promise => { await WorkspacePlugins[from].CRUD.delete(workspace as any); const newId = await WorkspacePlugins[to].CRUD.create( diff --git a/apps/web/src/hooks/use-workspaces.ts b/apps/web/src/hooks/use-workspaces.ts index 455d8be2d..86f26ae88 100644 --- a/apps/web/src/hooks/use-workspaces.ts +++ b/apps/web/src/hooks/use-workspaces.ts @@ -1,3 +1,5 @@ +import { WorkspaceFlavour } from '@affine/workspace/type'; +import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils'; import { nanoid } from '@blocksuite/store'; import { useAtomValue, useSetAtom } from 'jotai'; import { useCallback, useEffect } from 'react'; @@ -6,8 +8,6 @@ import { jotaiWorkspacesAtom, workspacesAtom } from '../atoms'; import { WorkspacePlugins } from '../plugins'; import { LocalPlugin } from '../plugins/local'; import type { LocalWorkspace, RemWorkspace } from '../shared'; -import { RemWorkspaceFlavour } from '../shared'; -import { createEmptyBlockSuiteWorkspace } from '../utils'; export function useWorkspaces(): RemWorkspace[] { return useAtomValue(workspacesAtom); @@ -43,7 +43,7 @@ export function useWorkspacesHelper() { ...workspaces, { id, - flavour: RemWorkspaceFlavour.LOCAL, + flavour: WorkspaceFlavour.LOCAL, }, ]); return id; diff --git a/apps/web/src/pages/_debug/broadcast.dev.tsx b/apps/web/src/pages/_debug/broadcast.dev.tsx index d56086361..6c80f0b9b 100644 --- a/apps/web/src/pages/_debug/broadcast.dev.tsx +++ b/apps/web/src/pages/_debug/broadcast.dev.tsx @@ -1,5 +1,6 @@ import { Button, toast } from '@affine/component'; import { DebugLogger } from '@affine/debug'; +import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils'; import { nanoid } from '@blocksuite/store'; import { Typography } from '@mui/material'; import type React from 'react'; @@ -9,7 +10,6 @@ import { createBroadCastChannelProvider } from '../../blocksuite/providers'; import PageList from '../../components/blocksuite/block-suite-page-list/page-list'; import { StyledPage, StyledWrapper } from '../../layouts/styles'; import type { BroadCastChannelProvider } from '../../shared'; -import { createEmptyBlockSuiteWorkspace } from '../../utils'; const logger = new DebugLogger('broadcast'); diff --git a/apps/web/src/pages/_debug/login.dev.tsx b/apps/web/src/pages/_debug/login.dev.tsx new file mode 100644 index 000000000..467744a6a --- /dev/null +++ b/apps/web/src/pages/_debug/login.dev.tsx @@ -0,0 +1,78 @@ +import { Button, toast } from '@affine/component'; +import { currentAffineUserAtom } from '@affine/workspace/affine/atom'; +import { + clearLoginStorage, + createAffineAuth, + getLoginStorage, + isExpired, + parseIdToken, + setLoginStorage, + SignMethod, +} from '@affine/workspace/affine/login'; +import { useAtom } from 'jotai'; +import type { NextPage } from 'next'; +import { useMemo } from 'react'; + +import { StyledPage, StyledWrapper } from '../../layouts/styles'; + +const LoginDevPage: NextPage = () => { + const [user, setUser] = useAtom(currentAffineUserAtom); + const auth = useMemo(() => createAffineAuth(), []); + return ( + + +

LoginDevPage

+ + + + {user && JSON.stringify(user)} +
+
+ ); +}; + +export default LoginDevPage; diff --git a/apps/web/src/pages/preview/[previewId].tsx b/apps/web/src/pages/preview/[previewId].tsx index a93433310..b433f014e 100644 --- a/apps/web/src/pages/preview/[previewId].tsx +++ b/apps/web/src/pages/preview/[previewId].tsx @@ -1,3 +1,4 @@ +import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils'; import { ContentParser } from '@blocksuite/blocks/content-parser'; import type { GetStaticPaths, @@ -16,7 +17,6 @@ import { StyledWrapper, } from '../../layouts/styles'; import type { BlockSuiteWorkspace } from '../../shared'; -import { createEmptyBlockSuiteWorkspace } from '../../utils'; export type PreviewPageProps = { text: string; diff --git a/apps/web/src/pages/workspace/[workspaceId]/[pageId].tsx b/apps/web/src/pages/workspace/[workspaceId]/[pageId].tsx index 43c0f75a5..7112f06ab 100644 --- a/apps/web/src/pages/workspace/[workspaceId]/[pageId].tsx +++ b/apps/web/src/pages/workspace/[workspaceId]/[pageId].tsx @@ -1,3 +1,4 @@ +import { WorkspaceFlavour } from '@affine/workspace/type'; import { useRouter } from 'next/router'; import type React from 'react'; import { useEffect } from 'react'; @@ -11,7 +12,6 @@ import { useSyncRouterWithCurrentWorkspaceAndPage } from '../../../hooks/use-syn import { WorkspaceLayout } from '../../../layouts'; import { WorkspacePlugins } from '../../../plugins'; import type { BlockSuiteWorkspace, NextPageWithLayout } from '../../../shared'; -import { RemWorkspaceFlavour } from '../../../shared'; function enableFullFlags(blockSuiteWorkspace: BlockSuiteWorkspace) { blockSuiteWorkspace.awarenessStore.setFlag('enable_set_remote_flag', false); @@ -38,12 +38,12 @@ const WorkspaceDetail: React.FC = () => { if (!pageId) { return ; } - if (currentWorkspace.flavour === RemWorkspaceFlavour.AFFINE) { + if (currentWorkspace.flavour === WorkspaceFlavour.AFFINE) { const PageDetail = WorkspacePlugins[currentWorkspace.flavour].UI.PageDetail; return ( ); - } else if (currentWorkspace.flavour === RemWorkspaceFlavour.LOCAL) { + } else if (currentWorkspace.flavour === WorkspaceFlavour.LOCAL) { const PageDetail = WorkspacePlugins[currentWorkspace.flavour].UI.PageDetail; return ( diff --git a/apps/web/src/pages/workspace/[workspaceId]/all.tsx b/apps/web/src/pages/workspace/[workspaceId]/all.tsx index cd1b54ca6..678c5785a 100644 --- a/apps/web/src/pages/workspace/[workspaceId]/all.tsx +++ b/apps/web/src/pages/workspace/[workspaceId]/all.tsx @@ -1,4 +1,5 @@ import { useTranslation } from '@affine/i18n'; +import { WorkspaceFlavour } from '@affine/workspace/type'; import { FolderIcon } from '@blocksuite/icons'; import { assertEquals, assertExists, nanoid } from '@blocksuite/store'; import Head from 'next/head'; @@ -20,7 +21,6 @@ import type { LocalIndexedDBProvider, NextPageWithLayout, } from '../../../shared'; -import { RemWorkspaceFlavour } from '../../../shared'; const AllPage: NextPageWithLayout = () => { const router = useRouter(); @@ -78,7 +78,7 @@ const AllPage: NextPageWithLayout = () => { if (currentWorkspace === null) { return ; } - if (currentWorkspace.flavour === RemWorkspaceFlavour.AFFINE) { + if (currentWorkspace.flavour === WorkspaceFlavour.AFFINE) { const PageList = WorkspacePlugins[currentWorkspace.flavour].UI.PageList; return ( <> @@ -92,7 +92,7 @@ const AllPage: NextPageWithLayout = () => { /> ); - } else if (currentWorkspace.flavour === RemWorkspaceFlavour.LOCAL) { + } else if (currentWorkspace.flavour === WorkspaceFlavour.LOCAL) { const PageList = WorkspacePlugins[currentWorkspace.flavour].UI.PageList; return ( <> diff --git a/apps/web/src/pages/workspace/[workspaceId]/setting.tsx b/apps/web/src/pages/workspace/[workspaceId]/setting.tsx index 858d6b299..ad7297beb 100644 --- a/apps/web/src/pages/workspace/[workspaceId]/setting.tsx +++ b/apps/web/src/pages/workspace/[workspaceId]/setting.tsx @@ -1,4 +1,10 @@ import { useTranslation } from '@affine/i18n'; +import type { SettingPanel, WorkspaceRegistry } from '@affine/workspace/type'; +import { + settingPanel, + settingPanelValues, + WorkspaceFlavour, +} from '@affine/workspace/type'; import { SettingsIcon } from '@blocksuite/icons'; import { assertExists } from '@blocksuite/store'; import { useAtom } from 'jotai'; @@ -16,16 +22,7 @@ import { useTransformWorkspace } from '../../../hooks/use-transform-workspace'; import { useWorkspacesHelper } from '../../../hooks/use-workspaces'; import { WorkspaceLayout } from '../../../layouts'; import { WorkspacePlugins } from '../../../plugins'; -import type { - FlavourToWorkspace, - NextPageWithLayout, - SettingPanel, -} from '../../../shared'; -import { - RemWorkspaceFlavour, - settingPanel, - settingPanelValues, -} from '../../../shared'; +import type { NextPageWithLayout } from '../../../shared'; import { apis } from '../../../shared/apis'; const settingPanelAtom = atomWithStorage( @@ -105,13 +102,12 @@ const SettingPage: NextPageWithLayout = () => { }, [currentWorkspace, helper]); const transformWorkspace = useTransformWorkspace(); const onTransformWorkspace = useCallback( - async ( + async ( from: From, to: To, - workspace: FlavourToWorkspace[From] + workspace: WorkspaceRegistry[From] ): Promise => { - const needRefresh = - to === RemWorkspaceFlavour.AFFINE && !apis.auth.isLogin; + const needRefresh = to === WorkspaceFlavour.AFFINE && !apis.auth.isLogin; if (needRefresh) { await apis.signInWithGoogle(); } @@ -135,7 +131,7 @@ const SettingPage: NextPageWithLayout = () => { return ; } else if (settingPanelValues.indexOf(currentTab as SettingPanel) === -1) { return ; - } else if (currentWorkspace.flavour === RemWorkspaceFlavour.AFFINE) { + } else if (currentWorkspace.flavour === WorkspaceFlavour.AFFINE) { const Setting = WorkspacePlugins[currentWorkspace.flavour].UI.SettingsDetail; return ( @@ -155,7 +151,7 @@ const SettingPage: NextPageWithLayout = () => { /> ); - } else if (currentWorkspace.flavour === RemWorkspaceFlavour.LOCAL) { + } else if (currentWorkspace.flavour === WorkspaceFlavour.LOCAL) { const Setting = WorkspacePlugins[currentWorkspace.flavour].UI.SettingsDetail; return ( diff --git a/apps/web/src/plugins/affine/fetcher.ts b/apps/web/src/plugins/affine/fetcher.ts index 3ff0f4c1b..d452f8db1 100644 --- a/apps/web/src/plugins/affine/fetcher.ts +++ b/apps/web/src/plugins/affine/fetcher.ts @@ -1,12 +1,12 @@ +import { WorkspaceFlavour } from '@affine/workspace/type'; +import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils'; import { assertExists } from '@blocksuite/store'; import { jotaiStore, workspacesAtom } from '../../atoms'; import { createAffineProviders } from '../../blocksuite'; import { Unreachable } from '../../components/affine/affine-error-eoundary'; import type { AffineWorkspace } from '../../shared'; -import { RemWorkspaceFlavour } from '../../shared'; import { apis } from '../../shared/apis'; -import { createEmptyBlockSuiteWorkspace } from '../../utils'; type Query = (typeof QueryKey)[keyof typeof QueryKey]; @@ -73,7 +73,7 @@ export const fetcher = async ( ); const remWorkspace: AffineWorkspace = { ...workspace, - flavour: RemWorkspaceFlavour.AFFINE, + flavour: WorkspaceFlavour.AFFINE, blockSuiteWorkspace, providers: [...createAffineProviders(blockSuiteWorkspace)], }; diff --git a/apps/web/src/plugins/affine/index.tsx b/apps/web/src/plugins/affine/index.tsx index 1bec4ca9b..d85818094 100644 --- a/apps/web/src/plugins/affine/index.tsx +++ b/apps/web/src/plugins/affine/index.tsx @@ -1,3 +1,5 @@ +import { LoadPriority, WorkspaceFlavour } from '@affine/workspace/type'; +import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils'; import { createJSONStorage } from 'jotai/utils'; import React from 'react'; import { mutate } from 'swr'; @@ -9,13 +11,8 @@ import { WorkspaceSettingDetail } from '../../components/affine/workspace-settin import { BlockSuitePageList } from '../../components/blocksuite/block-suite-page-list'; import { PageDetailEditor } from '../../components/page-detail-editor'; import type { AffineWorkspace } from '../../shared'; -import { - BlockSuiteWorkspace, - LoadPriority, - RemWorkspaceFlavour, -} from '../../shared'; +import { BlockSuiteWorkspace } from '../../shared'; import { apis, clientAuth } from '../../shared/apis'; -import { createEmptyBlockSuiteWorkspace } from '../../utils'; import { initPage } from '../../utils/blocksuite'; import type { WorkspacePlugin } from '..'; import { QueryKey } from './fetcher'; @@ -46,7 +43,7 @@ const getPersistenceAllWorkspace = () => { ); const affineWorkspace: AffineWorkspace = { ...item, - flavour: RemWorkspaceFlavour.AFFINE, + flavour: WorkspaceFlavour.AFFINE, blockSuiteWorkspace, providers: [...createAffineProviders(blockSuiteWorkspace)], }; @@ -57,8 +54,8 @@ const getPersistenceAllWorkspace = () => { return allWorkspaces; }; -export const AffinePlugin: WorkspacePlugin = { - flavour: RemWorkspaceFlavour.AFFINE, +export const AffinePlugin: WorkspacePlugin = { + flavour: WorkspaceFlavour.AFFINE, loadPriority: LoadPriority.HIGH, cleanup: () => { storage.removeItem(kAffineLocal); @@ -167,7 +164,7 @@ export const AffinePlugin: WorkspacePlugin = { const affineWorkspace: AffineWorkspace = { ...workspace, - flavour: RemWorkspaceFlavour.AFFINE, + flavour: WorkspaceFlavour.AFFINE, blockSuiteWorkspace, providers: [...createAffineProviders(blockSuiteWorkspace)], }; diff --git a/apps/web/src/plugins/index.tsx b/apps/web/src/plugins/index.tsx index 22d391feb..bac7e3ee2 100644 --- a/apps/web/src/plugins/index.tsx +++ b/apps/web/src/plugins/index.tsx @@ -1,74 +1,35 @@ -import type React from 'react'; - import type { - BlockSuiteWorkspace, - FlavourToWorkspace, LoadPriority, - SettingPanel, -} from '../shared'; -import { RemWorkspaceFlavour } from '../shared'; + WorkspaceCRUD, + WorkspaceUISchema, +} from '@affine/workspace/type'; +import { WorkspaceFlavour } from '@affine/workspace/type'; + +import type { AffineWorkspace, LocalWorkspace } from '../shared'; import { AffinePlugin } from './affine'; import { LocalPlugin } from './local'; -type UIBaseProps = { - currentWorkspace: FlavourToWorkspace[Flavour]; -}; +declare module '@affine/workspace/type' { + interface WorkspaceRegistry { + [WorkspaceFlavour.AFFINE]: AffineWorkspace; + [WorkspaceFlavour.LOCAL]: LocalWorkspace; + } +} -type SettingProps = - UIBaseProps & { - currentTab: SettingPanel; - onChangeTab: (tab: SettingPanel) => void; - onDeleteWorkspace: () => void; - onTransformWorkspace: < - From extends RemWorkspaceFlavour, - To extends RemWorkspaceFlavour - >( - from: From, - to: To, - workspace: FlavourToWorkspace[From] - ) => void; - }; - -type PageDetailProps = - UIBaseProps & { - currentPageId: string; - }; - -type PageListProps = { - blockSuiteWorkspace: BlockSuiteWorkspace; - onOpenPage: (pageId: string, newTab?: boolean) => void; -}; - -type SideBarMenuProps = - UIBaseProps & { - setSideBarOpen: (open: boolean) => void; - }; - -export interface WorkspacePlugin { +export interface WorkspacePlugin { flavour: Flavour; // Plugin will be loaded according to the priority loadPriority: LoadPriority; // fixme: this is a hack cleanup?: () => void; // Fetch necessary data for the first render - CRUD: { - create: (blockSuiteWorkspace: BlockSuiteWorkspace) => Promise; - delete: (workspace: FlavourToWorkspace[Flavour]) => Promise; - get: (workspaceId: string) => Promise; - // not supported yet - // update: (workspace: FlavourToWorkspace[Flavour]) => Promise; - list: () => Promise; - }; - UI: { - PageDetail: React.FC>; - PageList: React.FC>; - SettingsDetail: React.FC>; - }; + CRUD: WorkspaceCRUD; + UI: WorkspaceUISchema; } export const WorkspacePlugins = { - [RemWorkspaceFlavour.AFFINE]: AffinePlugin, - [RemWorkspaceFlavour.LOCAL]: LocalPlugin, + [WorkspaceFlavour.AFFINE]: AffinePlugin, + [WorkspaceFlavour.LOCAL]: LocalPlugin, } satisfies { - [Key in RemWorkspaceFlavour]: WorkspacePlugin; + [Key in WorkspaceFlavour]: WorkspacePlugin; }; diff --git a/apps/web/src/plugins/local/index.tsx b/apps/web/src/plugins/local/index.tsx index 31978b06f..ee97a8c28 100644 --- a/apps/web/src/plugins/local/index.tsx +++ b/apps/web/src/plugins/local/index.tsx @@ -1,4 +1,6 @@ import { DEFAULT_WORKSPACE_NAME } from '@affine/env'; +import { LoadPriority, WorkspaceFlavour } from '@affine/workspace/type'; +import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils'; import { nanoid } from '@blocksuite/store'; import { createJSONStorage } from 'jotai/utils'; import React from 'react'; @@ -11,12 +13,7 @@ import { WorkspaceSettingDetail } from '../../components/affine/workspace-settin import { BlockSuitePageList } from '../../components/blocksuite/block-suite-page-list'; import { PageDetailEditor } from '../../components/page-detail-editor'; import type { LocalWorkspace } from '../../shared'; -import { - BlockSuiteWorkspace, - LoadPriority, - RemWorkspaceFlavour, -} from '../../shared'; -import { createEmptyBlockSuiteWorkspace } from '../../utils'; +import { BlockSuiteWorkspace } from '../../shared'; import { initPage } from '../../utils/blocksuite'; import type { WorkspacePlugin } from '..'; @@ -25,8 +22,8 @@ const getStorage = () => createJSONStorage(() => localStorage); export const kStoreKey = 'affine-local-workspace'; const schema = z.array(z.string()); -export const LocalPlugin: WorkspacePlugin = { - flavour: RemWorkspaceFlavour.LOCAL, +export const LocalPlugin: WorkspacePlugin = { + flavour: WorkspaceFlavour.LOCAL, loadPriority: LoadPriority.LOW, CRUD: { get: async workspaceId => { @@ -44,7 +41,7 @@ export const LocalPlugin: WorkspacePlugin = { ); const workspace: LocalWorkspace = { id, - flavour: RemWorkspaceFlavour.LOCAL, + flavour: WorkspaceFlavour.LOCAL, blockSuiteWorkspace: blockSuiteWorkspace, providers: [...createLocalProviders(blockSuiteWorkspace)], }; diff --git a/apps/web/src/shared/apis.ts b/apps/web/src/shared/apis.ts index 296b07161..a55f02b1e 100644 --- a/apps/web/src/shared/apis.ts +++ b/apps/web/src/shared/apis.ts @@ -12,7 +12,7 @@ let prefixUrl = '/'; if (typeof window === 'undefined') { // SSR const serverAPI = config.serverAPI; - if (isValidIPAddress(serverAPI)) { + if (isValidIPAddress(serverAPI.split(':')[0])) { // This is for Server side rendering support prefixUrl = new URL('http://' + config.serverAPI + '/').origin; } else { diff --git a/apps/web/src/shared/index.ts b/apps/web/src/shared/index.ts index e0de6c12e..cf73b8cdd 100644 --- a/apps/web/src/shared/index.ts +++ b/apps/web/src/shared/index.ts @@ -1,4 +1,5 @@ import type { Workspace as RemoteWorkspace } from '@affine/datacenter'; +import type { WorkspaceFlavour } from '@affine/workspace/type'; import { Workspace as BlockSuiteWorkspace } from '@blocksuite/store'; import type { NextPage } from 'next'; import type { ReactElement, ReactNode } from 'react'; @@ -11,25 +12,15 @@ declare global { } } -export const enum RemWorkspaceFlavour { - AFFINE = 'affine', - LOCAL = 'local', -} - -export interface FlavourToWorkspace { - [RemWorkspaceFlavour.AFFINE]: AffineWorkspace; - [RemWorkspaceFlavour.LOCAL]: LocalWorkspace; -} - export interface AffineWorkspace extends RemoteWorkspace { - flavour: RemWorkspaceFlavour.AFFINE; + flavour: WorkspaceFlavour.AFFINE; // empty blockSuiteWorkspace: BlockSuiteWorkspace; providers: Provider[]; } export interface LocalWorkspace { - flavour: RemWorkspaceFlavour.LOCAL; + flavour: WorkspaceFlavour.LOCAL; id: string; blockSuiteWorkspace: BlockSuiteWorkspace; providers: Provider[]; @@ -89,16 +80,6 @@ export const enum WorkspaceSubPath { TRASH = 'trash', } -export const settingPanel = { - General: 'general', - Collaboration: 'collaboration', - Publish: 'publish', - Export: 'export', - Sync: 'sync', -} as const; -export const settingPanelValues = [...Object.values(settingPanel)] as const; -export type SettingPanel = (typeof settingPanel)[keyof typeof settingPanel]; - export const WorkspaceSubPathName = { [WorkspaceSubPath.ALL]: 'All Pages', [WorkspaceSubPath.FAVORITE]: 'Favorites', @@ -125,9 +106,3 @@ export const publicPathGenerator = { } satisfies { [Path in WorkspaceSubPath]: (workspaceId: string) => string; }; - -export const enum LoadPriority { - HIGH = 1, - MEDIUM = 2, - LOW = 3, -} diff --git a/apps/web/src/utils/index.ts b/apps/web/src/utils/index.ts index 75f60b939..431342fbf 100644 --- a/apps/web/src/utils/index.ts +++ b/apps/web/src/utils/index.ts @@ -1,8 +1,3 @@ -import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models'; -import type { BlobOptionsGetter, Generator } from '@blocksuite/store'; - -import { BlockSuiteWorkspace } from '../shared'; - export function stringToColour(str: string) { str = str || 'affine'; let colour = '#'; @@ -23,24 +18,3 @@ export function stringToColour(str: string) { return colour; } - -const hashMap = new Map(); -export const createEmptyBlockSuiteWorkspace = ( - id: string, - blobOptionsGetter?: BlobOptionsGetter, - idGenerator?: Generator -): BlockSuiteWorkspace => { - if (hashMap.has(id)) { - return hashMap.get(id) as BlockSuiteWorkspace; - } - const workspace = new BlockSuiteWorkspace({ - id, - isSSR: typeof window === 'undefined', - blobOptionsGetter, - idGenerator, - }) - .register(AffineSchemas) - .register(__unstableSchemas); - hashMap.set(id, workspace); - return workspace; -}; diff --git a/package.json b/package.json index cff3595d7..1fc8d6964 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "happy-dom": "^8.9.0", "husky": "^8.0.3", "lint-staged": "^13.2.0", + "msw": "^1.2.0", "nanoid": "^4.0.1", "nyc": "^15.1.0", "prettier": "^2.8.5", diff --git a/packages/env/package.json b/packages/env/package.json index 7893d3057..a34d3140b 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -2,12 +2,17 @@ "name": "@affine/env", "private": true, "main": "./src/index.ts", + "module": "./src/index.ts", "devDependencies": { "next": "=13.2.3", "react": "^18.2.0", "react-dom": "^18.2.0", "zod": "^3.21.4" }, + "exports": { + ".": "./src/index.ts", + "./constant": "./src/constant.ts" + }, "dependencies": { "@blocksuite/global": "0.5.0-20230323085636-3110abb", "lit": "^2.6.1" diff --git a/packages/env/src/constant.ts b/packages/env/src/constant.ts index 9f2777f3b..fed34a016 100644 --- a/packages/env/src/constant.ts +++ b/packages/env/src/constant.ts @@ -1,2 +1,68 @@ export const DEFAULT_WORKSPACE_NAME = 'Demo Workspace'; export const UNTITLED_WORKSPACE_NAME = 'Untitled'; + +export const enum MessageCode { + loginError, + noPermission, + loadListFailed, + getDetailFailed, + createWorkspaceFailed, + getMembersFailed, + updateWorkspaceFailed, + deleteWorkspaceFailed, + inviteMemberFailed, + removeMemberFailed, + acceptInvitingFailed, + getBlobFailed, + leaveWorkspaceFailed, + downloadWorkspaceFailed, + refreshTokenError, +} + +export const Messages = { + [MessageCode.loginError]: { + message: 'Login failed', + }, + [MessageCode.noPermission]: { + message: 'No permission', + }, + [MessageCode.loadListFailed]: { + message: 'Load list failed', + }, + [MessageCode.getDetailFailed]: { + message: 'Get detail failed', + }, + [MessageCode.createWorkspaceFailed]: { + message: 'Create workspace failed', + }, + [MessageCode.getMembersFailed]: { + message: 'Get members failed', + }, + [MessageCode.updateWorkspaceFailed]: { + message: 'Update workspace failed', + }, + [MessageCode.deleteWorkspaceFailed]: { + message: 'Delete workspace failed', + }, + [MessageCode.inviteMemberFailed]: { + message: 'Invite member failed', + }, + [MessageCode.removeMemberFailed]: { + message: 'Remove member failed', + }, + [MessageCode.acceptInvitingFailed]: { + message: 'Accept inviting failed', + }, + [MessageCode.getBlobFailed]: { + message: 'Get blob failed', + }, + [MessageCode.leaveWorkspaceFailed]: { + message: 'Leave workspace failed', + }, + [MessageCode.downloadWorkspaceFailed]: { + message: 'Download workspace failed', + }, + [MessageCode.refreshTokenError]: { + message: 'Refresh token failed', + }, +} as const; diff --git a/packages/workspace/package.json b/packages/workspace/package.json new file mode 100644 index 000000000..f917d7341 --- /dev/null +++ b/packages/workspace/package.json @@ -0,0 +1,22 @@ +{ + "name": "@affine/workspace", + "private": true, + "exports": { + "./utils": "./src/utils.ts", + "./type": "./src/type.ts", + "./affine/*": "./src/affine/*.ts" + }, + "dependencies": { + "@affine/component": "workspace:*", + "@affine/debug": "workspace:*", + "@affine/env": "workspace:*", + "@blocksuite/blocks": "0.5.0-20230323085636-3110abb", + "@blocksuite/store": "0.5.0-20230323085636-3110abb", + "firebase": "^9.18.0", + "jotai": "^2.0.3", + "js-base64": "^3.7.5", + "ky": "^0.33.3", + "react": "^18.2.0", + "react-dom": "^18.2.0" + } +} diff --git a/packages/workspace/src/affine/__tests__/atom.spec.ts b/packages/workspace/src/affine/__tests__/atom.spec.ts new file mode 100644 index 000000000..9a5823c26 --- /dev/null +++ b/packages/workspace/src/affine/__tests__/atom.spec.ts @@ -0,0 +1,20 @@ +import { getDefaultStore } from 'jotai'; +import { describe, expect, test } from 'vitest'; + +import { currentAffineUserAtom } from '../atom'; + +describe('atom', () => { + test('currentAffineUserAtom', () => { + const store = getDefaultStore(); + const mock = { + created_at: 0, + exp: 0, + email: '', + id: '', + name: '', + avatar_url: '', + }; + store.set(currentAffineUserAtom, mock); + expect(store.get(currentAffineUserAtom)).toEqual(mock); + }); +}); diff --git a/packages/workspace/src/affine/__tests__/login.spec.ts b/packages/workspace/src/affine/__tests__/login.spec.ts new file mode 100644 index 000000000..8065b77da --- /dev/null +++ b/packages/workspace/src/affine/__tests__/login.spec.ts @@ -0,0 +1,42 @@ +/** + * @vitest-environment happy-dom + */ +import type { AccessTokenMessage } from '@affine/workspace/affine/login'; +import { + getLoginStorage, + isExpired, + setLoginStorage, + STORAGE_KEY, +} from '@affine/workspace/affine/login'; +import { describe, expect, test } from 'vitest'; + +describe('storage', () => { + test('should work', () => { + setLoginStorage({ + token: '1', + refresh: '2', + }); + const data = localStorage.getItem(STORAGE_KEY); + expect(data).toBe('{"token":"1","refresh":"2"}'); + const login = getLoginStorage(); + expect(login).toEqual({ + token: '1', + refresh: '2', + }); + }); +}); + +describe('utils', () => { + test('isExpired', async () => { + const now = Math.floor(Date.now() / 1000); + expect(isExpired({ exp: now + 1 } as AccessTokenMessage)).toBeFalsy(); + const promise = new Promise(resolve => { + setTimeout(() => { + expect(isExpired({ exp: now + 1 } as AccessTokenMessage)).toBeTruthy(); + resolve(); + }, 2000); + }); + expect(isExpired({ exp: now - 1 } as AccessTokenMessage)).toBeTruthy(); + await promise; + }); +}); diff --git a/packages/workspace/src/affine/atom.ts b/packages/workspace/src/affine/atom.ts new file mode 100644 index 000000000..87dcb1bfc --- /dev/null +++ b/packages/workspace/src/affine/atom.ts @@ -0,0 +1,4 @@ +import type { AccessTokenMessage } from '@affine/workspace/affine/login'; +import { atom } from 'jotai'; + +export const currentAffineUserAtom = atom(null); diff --git a/packages/workspace/src/affine/login.ts b/packages/workspace/src/affine/login.ts new file mode 100644 index 000000000..0a557b98d --- /dev/null +++ b/packages/workspace/src/affine/login.ts @@ -0,0 +1,175 @@ +import { DebugLogger } from '@affine/debug'; +import { initializeApp } from 'firebase/app'; +import type { AuthProvider } from 'firebase/auth'; +import { + type Auth as FirebaseAuth, + connectAuthEmulator, + getAuth as getFirebaseAuth, + GithubAuthProvider, + GoogleAuthProvider, + signInWithPopup, +} from 'firebase/auth'; +import { decode } from 'js-base64'; +// Connect emulators based on env vars +const envConnectEmulators = process.env.REACT_APP_FIREBASE_EMULATORS === 'true'; + +export type AccessTokenMessage = { + created_at: number; + exp: number; + email: string; + id: string; + name: string; + avatar_url: string; +}; + +export type LoginParams = { + type: 'Google' | 'Refresh'; + token: string; +}; + +export type LoginResponse = { + // access token, expires in a very short time + token: string; + // Refresh token + refresh: string; +}; + +const logger = new DebugLogger('token'); + +export const STORAGE_KEY = 'affine-login-v2'; + +export function parseIdToken(token: string): AccessTokenMessage { + return JSON.parse(decode(token.split('.')[1])); +} + +export const isExpired = (token: AccessTokenMessage): boolean => { + const now = Math.floor(Date.now() / 1000); + return token.exp < now; +}; + +export const setLoginStorage = (login: LoginResponse) => { + localStorage.setItem( + STORAGE_KEY, + JSON.stringify({ + token: login.token, + refresh: login.refresh, + }) + ); +}; + +export const clearLoginStorage = () => { + localStorage.removeItem(STORAGE_KEY); +}; + +export const getLoginStorage = (): LoginResponse | null => { + const login = localStorage.getItem(STORAGE_KEY); + if (login) { + try { + return JSON.parse(login); + } catch (error) { + logger.error('Failed to parse login', error); + } + } + return null; +}; + +export const enum SignMethod { + Google = 'Google', + GitHub = 'GitHub', + // Twitter = 'Twitter', +} + +declare global { + // eslint-disable-next-line no-var + var firebaseAuthEmulatorStarted: boolean | undefined; +} + +export function createAffineAuth() { + let _firebaseAuth: FirebaseAuth | null = null; + const getAuth = (): FirebaseAuth | null => { + try { + if (!_firebaseAuth) { + const app = initializeApp({ + apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, + authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, + projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, + storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, + messagingSenderId: + process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, + appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, + measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID, + }); + _firebaseAuth = getFirebaseAuth(app); + } + if (envConnectEmulators && !globalThis.firebaseAuthEmulatorStarted) { + connectAuthEmulator(_firebaseAuth, 'http://localhost:9099', { + disableWarnings: true, + }); + globalThis.firebaseAuthEmulatorStarted = true; + } + return _firebaseAuth; + } catch (error) { + logger.error('Failed to initialize firebase', error); + return null; + } + }; + + return { + generateToken: async ( + method: SignMethod + ): Promise => { + const auth = getAuth(); + if (!auth) { + throw new Error('Failed to initialize firebase'); + } + let provider: AuthProvider; + switch (method) { + case SignMethod.Google: + provider = new GoogleAuthProvider(); + break; + case SignMethod.GitHub: + provider = new GithubAuthProvider(); + break; + default: + throw new Error('Unsupported sign method'); + } + try { + const response = await signInWithPopup(auth, provider); + const idToken = await response.user.getIdToken(); + logger.debug(idToken); + return fetch('/api/user/token', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + type: 'Google', + token: idToken, + }), + }).then(r => r.json()) as Promise; + } catch (error) { + if (error instanceof Error && 'code' in error) { + if (error.code === 'auth/popup-closed-by-user') { + return null; + } + } + logger.error('Failed to sign in', error); + } + return null; + }, + refreshToken: async ( + loginResponse: LoginResponse + ): Promise => { + return fetch('/api/user/token', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + type: 'Refresh', + token: loginResponse.refresh, + }), + }).then(r => r.json()) as Promise; + }, + } as const; +} diff --git a/packages/workspace/src/type.ts b/packages/workspace/src/type.ts new file mode 100644 index 000000000..afbad1530 --- /dev/null +++ b/packages/workspace/src/type.ts @@ -0,0 +1,74 @@ +import type { Workspace as BlockSuiteWorkspace } from '@blocksuite/store'; +import type { FC } from 'react'; + +export const enum LoadPriority { + HIGH = 1, + MEDIUM = 2, + LOW = 3, +} + +export const enum WorkspaceFlavour { + AFFINE = 'affine', + LOCAL = 'local', +} +export const settingPanel = { + General: 'general', + Collaboration: 'collaboration', + Publish: 'publish', + Export: 'export', + Sync: 'sync', +} as const; +export const settingPanelValues = [...Object.values(settingPanel)] as const; +export type SettingPanel = (typeof settingPanel)[keyof typeof settingPanel]; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface WorkspaceRegistry {} + +export interface WorkspaceCRUD { + create: (blockSuiteWorkspace: BlockSuiteWorkspace) => Promise; + delete: (workspace: WorkspaceRegistry[Flavour]) => Promise; + get: (workspaceId: string) => Promise; + // not supported yet + // update: (workspace: FlavourToWorkspace[Flavour]) => Promise; + list: () => Promise; +} + +type UIBaseProps = { + currentWorkspace: WorkspaceRegistry[Flavour]; +}; + +type SettingProps = + UIBaseProps & { + currentTab: SettingPanel; + onChangeTab: (tab: SettingPanel) => void; + onDeleteWorkspace: () => void; + onTransformWorkspace: < + From extends keyof WorkspaceRegistry, + To extends keyof WorkspaceRegistry + >( + from: From, + to: To, + workspace: WorkspaceRegistry[From] + ) => void; + }; + +type PageDetailProps = + UIBaseProps & { + currentPageId: string; + }; + +type PageListProps = { + blockSuiteWorkspace: BlockSuiteWorkspace; + onOpenPage: (pageId: string, newTab?: boolean) => void; +}; + +type SideBarMenuProps = + UIBaseProps & { + setSideBarOpen: (open: boolean) => void; + }; + +export interface WorkspaceUISchema { + PageDetail: FC>; + PageList: FC>; + SettingsDetail: FC>; +} diff --git a/packages/workspace/src/utils.ts b/packages/workspace/src/utils.ts new file mode 100644 index 000000000..b9360cd61 --- /dev/null +++ b/packages/workspace/src/utils.ts @@ -0,0 +1,24 @@ +import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models'; +import type { BlobOptionsGetter, Generator } from '@blocksuite/store'; +import { Workspace } from '@blocksuite/store'; + +const hashMap = new Map(); +export const createEmptyBlockSuiteWorkspace = ( + id: string, + blobOptionsGetter?: BlobOptionsGetter, + idGenerator?: Generator +): Workspace => { + if (hashMap.has(id)) { + return hashMap.get(id) as Workspace; + } + const workspace = new Workspace({ + id, + isSSR: typeof window === 'undefined', + blobOptionsGetter, + idGenerator, + }) + .register(AffineSchemas) + .register(__unstableSchemas); + hashMap.set(id, workspace); + return workspace; +}; diff --git a/packages/workspace/tsconfig.json b/packages/workspace/tsconfig.json new file mode 100644 index 000000000..7513033f0 --- /dev/null +++ b/packages/workspace/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["./src"] +} diff --git a/tsconfig.json b/tsconfig.json index 50e573fb0..7e21aab38 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,7 +26,9 @@ "@affine/i18n": ["./packages/i18n/src"], "@affine/debug": ["./packages/debug"], "@affine/env": ["./packages/env"], - "@affine/utils": ["./packages/utils"] + "@affine/env/*": ["./packages/env/src/*"], + "@affine/utils": ["./packages/utils"], + "@affine/workspace/*": ["./packages/workspace/src/*"] } }, "references": [ @@ -55,7 +57,7 @@ "path": "./packages/debug" }, { - "path": "./packages/cli" + "path": "./packages/workspace" } ], "files": [], diff --git a/yarn.lock b/yarn.lock index 9c2ead466..bcf37828a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,6 +22,7 @@ __metadata: "@affine/env": "workspace:*" "@affine/i18n": "workspace:*" "@affine/templates": "workspace:*" + "@affine/workspace": "workspace:*" "@blocksuite/blocks": 0.5.0-20230323085636-3110abb "@blocksuite/editor": 0.5.0-20230323085636-3110abb "@blocksuite/icons": 2.0.23 @@ -251,6 +252,24 @@ __metadata: languageName: unknown linkType: soft +"@affine/workspace@workspace:*, @affine/workspace@workspace:packages/workspace": + version: 0.0.0-use.local + resolution: "@affine/workspace@workspace:packages/workspace" + dependencies: + "@affine/component": "workspace:*" + "@affine/debug": "workspace:*" + "@affine/env": "workspace:*" + "@blocksuite/blocks": 0.5.0-20230323085636-3110abb + "@blocksuite/store": 0.5.0-20230323085636-3110abb + firebase: ^9.18.0 + jotai: ^2.0.3 + js-base64: ^3.7.5 + ky: ^0.33.3 + react: ^18.2.0 + react-dom: ^18.2.0 + languageName: unknown + linkType: soft + "@ampproject/remapping@npm:^2.2.0": version: 2.2.0 resolution: "@ampproject/remapping@npm:2.2.0" @@ -3856,6 +3875,32 @@ __metadata: languageName: node linkType: hard +"@mswjs/cookies@npm:^0.2.2": + version: 0.2.2 + resolution: "@mswjs/cookies@npm:0.2.2" + dependencies: + "@types/set-cookie-parser": ^2.4.0 + set-cookie-parser: ^2.4.6 + checksum: 23b1ef56d57efcc1b44600076f531a1fb703855af342a31e01bad4adaf0dab51f6d3b5595a95a7988c3f612ba075835f9a06c52833205284d101eb9a51dd72b0 + languageName: node + linkType: hard + +"@mswjs/interceptors@npm:^0.17.5": + version: 0.17.9 + resolution: "@mswjs/interceptors@npm:0.17.9" + dependencies: + "@open-draft/until": ^1.0.3 + "@types/debug": ^4.1.7 + "@xmldom/xmldom": ^0.8.3 + debug: ^4.3.3 + headers-polyfill: ^3.1.0 + outvariant: ^1.2.1 + strict-event-emitter: ^0.2.4 + web-encoding: ^1.1.5 + checksum: 4df726cbee93d8baa54ead1ecb11e98124468659f51eb659ef8ead4aca7d6375198baf412ea17d4810fa5f1ee4fa53994702cb3b0b4f6f427a2f0fb890020192 + languageName: node + linkType: hard + "@mui/base@npm:5.0.0-alpha.121": version: 5.0.0-alpha.121 resolution: "@mui/base@npm:5.0.0-alpha.121" @@ -4193,6 +4238,13 @@ __metadata: languageName: node linkType: hard +"@open-draft/until@npm:^1.0.3": + version: 1.0.3 + resolution: "@open-draft/until@npm:1.0.3" + checksum: 323e92ebef0150ed0f8caedc7d219b68cdc50784fa4eba0377eef93533d3f46514eb2400ced83dda8c51bddc3d2c7b8e9cf95e5ec85ab7f62dfc015d174f62f2 + languageName: node + linkType: hard + "@perfsee/bundle-analyzer@npm:1.4.0": version: 1.4.0 resolution: "@perfsee/bundle-analyzer@npm:1.4.0" @@ -6475,6 +6527,13 @@ __metadata: languageName: node linkType: hard +"@types/cookie@npm:^0.4.1": + version: 0.4.1 + resolution: "@types/cookie@npm:0.4.1" + checksum: 3275534ed69a76c68eb1a77d547d75f99fedc80befb75a3d1d03662fb08d697e6f8b1274e12af1a74c6896071b11510631ba891f64d30c78528d0ec45a9c1a18 + languageName: node + linkType: hard + "@types/debug@npm:^4.1.7": version: 4.1.7 resolution: "@types/debug@npm:4.1.7" @@ -6680,6 +6739,13 @@ __metadata: languageName: node linkType: hard +"@types/js-levenshtein@npm:^1.1.1": + version: 1.1.1 + resolution: "@types/js-levenshtein@npm:1.1.1" + checksum: 1d1ff1ee2ad551909e47f3ce19fcf85b64dc5146d3b531c8d26fc775492d36e380b32cf5ef68ff301e812c3b00282f37aac579ebb44498b94baff0ace7509769 + languageName: node + linkType: hard + "@types/json-schema@npm:*, @types/json-schema@npm:^7.0.11, @types/json-schema@npm:^7.0.6, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": version: 7.0.11 resolution: "@types/json-schema@npm:7.0.11" @@ -6949,6 +7015,15 @@ __metadata: languageName: node linkType: hard +"@types/set-cookie-parser@npm:^2.4.0": + version: 2.4.2 + resolution: "@types/set-cookie-parser@npm:2.4.2" + dependencies: + "@types/node": "*" + checksum: c31bf04eb9620829dc3c91bced74ac934ad039d20d20893fb5acac0f08769cbd4eef3bf7502a0289c7be59c3e9cfa456147b4e88bff47dd1b9efb4995ba5d5a3 + languageName: node + linkType: hard + "@types/stack-utils@npm:^2.0.0": version: 2.0.1 resolution: "@types/stack-utils@npm:2.0.1" @@ -7395,6 +7470,13 @@ __metadata: languageName: node linkType: hard +"@xmldom/xmldom@npm:^0.8.3": + version: 0.8.6 + resolution: "@xmldom/xmldom@npm:0.8.6" + checksum: f17ac6d99a971a6aeb831fcfc5cfa86f367664e45815046548814b2deb17ccc421fef4e0d5ba29e66179d112b552f6caa5680064f8e7bd8a389b788a60404c8e + languageName: node + linkType: hard + "@xtuc/ieee754@npm:^1.2.0": version: 1.2.0 resolution: "@xtuc/ieee754@npm:1.2.0" @@ -7420,6 +7502,13 @@ __metadata: languageName: node linkType: hard +"@zxing/text-encoding@npm:0.9.0": + version: 0.9.0 + resolution: "@zxing/text-encoding@npm:0.9.0" + checksum: c23b12aee7639382e4949961304a1294776afaffa40f579e09ffecd0e5e68cf26ef3edd75009de46da8a536e571448755ca68b3e2ea707d53793c0edb2e2c34a + languageName: node + linkType: hard + "AFFiNE@workspace:.": version: 0.0.0-use.local resolution: "AFFiNE@workspace:." @@ -7447,6 +7536,7 @@ __metadata: happy-dom: ^8.9.0 husky: ^8.0.3 lint-staged: ^13.2.0 + msw: ^1.2.0 nanoid: ^4.0.1 nyc: ^15.1.0 prettier: ^2.8.5 @@ -8665,6 +8755,16 @@ __metadata: languageName: node linkType: hard +"chalk@npm:4.1.1": + version: 4.1.1 + resolution: "chalk@npm:4.1.1" + dependencies: + ansi-styles: ^4.1.0 + supports-color: ^7.1.0 + checksum: 036e973e665ba1a32c975e291d5f3d549bceeb7b1b983320d4598fb75d70fe20c5db5d62971ec0fe76cdbce83985a00ee42372416abfc3a5584465005a7855ed + languageName: node + linkType: hard + "chalk@npm:5.2.0, chalk@npm:^5.2.0": version: 5.2.0 resolution: "chalk@npm:5.2.0" @@ -8693,7 +8793,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.2": +"chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -8717,6 +8817,13 @@ __metadata: languageName: node linkType: hard +"chardet@npm:^0.7.0": + version: 0.7.0 + resolution: "chardet@npm:0.7.0" + checksum: 6fd5da1f5d18ff5712c1e0aed41da200d7c51c28f11b36ee3c7b483f3696dabc08927fc6b227735eb8f0e1215c9a8abd8154637f3eff8cada5959df7f58b024d + languageName: node + linkType: hard + "check-error@npm:^1.0.2": version: 1.0.2 resolution: "check-error@npm:1.0.2" @@ -8724,7 +8831,7 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:^3.5.3": +"chokidar@npm:^3.4.2, chokidar@npm:^3.5.3": version: 3.5.3 resolution: "chokidar@npm:3.5.3" dependencies: @@ -8861,6 +8968,13 @@ __metadata: languageName: node linkType: hard +"cli-width@npm:^3.0.0": + version: 3.0.0 + resolution: "cli-width@npm:3.0.0" + checksum: 4c94af3769367a70e11ed69aa6095f1c600c0ff510f3921ab4045af961820d57c0233acfa8b6396037391f31b4c397e1f614d234294f979ff61430a6c166c3f6 + languageName: node + linkType: hard + "client-only@npm:0.0.1": version: 0.0.1 resolution: "client-only@npm:0.0.1" @@ -9219,6 +9333,13 @@ __metadata: languageName: node linkType: hard +"cookie@npm:^0.4.2": + version: 0.4.2 + resolution: "cookie@npm:0.4.2" + checksum: a00833c998bedf8e787b4c342defe5fa419abd96b32f4464f718b91022586b8f1bafbddd499288e75c037642493c83083da426c6a9080d309e3bd90fd11baa9b + languageName: node + linkType: hard + "core-js-compat@npm:^3.25.1": version: 3.29.1 resolution: "core-js-compat@npm:3.29.1" @@ -11028,7 +11149,7 @@ __metadata: languageName: node linkType: hard -"events@npm:^3.2.0": +"events@npm:^3.2.0, events@npm:^3.3.0": version: 3.3.0 resolution: "events@npm:3.3.0" checksum: f6f487ad2198aa41d878fa31452f1a3c00958f46e9019286ff4787c84aac329332ab45c9cdc8c445928fc6d7ded294b9e005a7fce9426488518017831b272780 @@ -11197,6 +11318,17 @@ __metadata: languageName: node linkType: hard +"external-editor@npm:^3.0.3": + version: 3.1.0 + resolution: "external-editor@npm:3.1.0" + dependencies: + chardet: ^0.7.0 + iconv-lite: ^0.4.24 + tmp: ^0.0.33 + checksum: 1c2a616a73f1b3435ce04030261bed0e22d4737e14b090bb48e58865da92529c9f2b05b893de650738d55e692d071819b45e1669259b2b354bc3154d27a698c7 + languageName: node + linkType: hard + "extract-zip@npm:^1.6.6": version: 1.7.0 resolution: "extract-zip@npm:1.7.0" @@ -11331,6 +11463,15 @@ __metadata: languageName: node linkType: hard +"figures@npm:^3.0.0": + version: 3.2.0 + resolution: "figures@npm:3.2.0" + dependencies: + escape-string-regexp: ^1.0.5 + checksum: 85a6ad29e9aca80b49b817e7c89ecc4716ff14e3779d9835af554db91bac41c0f289c418923519392a1e582b4d10482ad282021330cd045bb7b80c84152f2a2b + languageName: node + linkType: hard + "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" @@ -12392,7 +12533,7 @@ __metadata: languageName: node linkType: hard -"graphql@npm:^16.0.0": +"graphql@npm:^15.0.0 || ^16.0.0, graphql@npm:^16.0.0": version: 16.6.0 resolution: "graphql@npm:16.6.0" checksum: bf1d9e3c1938ce3c1a81e909bd3ead1ae4707c577f91cff1ca2eca474bfbc7873d5d7b942e1e9777ff5a8304421dba57a4b76d7a29eb19de8711cb70e3c2415e @@ -12536,6 +12677,13 @@ __metadata: languageName: node linkType: hard +"headers-polyfill@npm:^3.1.0": + version: 3.1.2 + resolution: "headers-polyfill@npm:3.1.2" + checksum: 510ca9637ef652404dbd432e680418f8d418ba18094ef2f64c3d8de955ebf6e68d553c7f0aeaa5fc937d130b139c1e2d7c2066cd4cf0f740a4627924eaaee9db + languageName: node + linkType: hard + "hoist-non-react-statics@npm:^3.3.1, hoist-non-react-statics@npm:^3.3.2": version: 3.3.2 resolution: "hoist-non-react-statics@npm:3.3.2" @@ -12751,7 +12899,7 @@ __metadata: languageName: node linkType: hard -"iconv-lite@npm:0.4.24": +"iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" dependencies: @@ -12889,6 +13037,29 @@ __metadata: languageName: node linkType: hard +"inquirer@npm:^8.2.0": + version: 8.2.5 + resolution: "inquirer@npm:8.2.5" + dependencies: + ansi-escapes: ^4.2.1 + chalk: ^4.1.1 + cli-cursor: ^3.1.0 + cli-width: ^3.0.0 + external-editor: ^3.0.3 + figures: ^3.0.0 + lodash: ^4.17.21 + mute-stream: 0.0.8 + ora: ^5.4.1 + run-async: ^2.4.0 + rxjs: ^7.5.5 + string-width: ^4.1.0 + strip-ansi: ^6.0.0 + through: ^2.3.6 + wrap-ansi: ^7.0.0 + checksum: f13ee4c444187786fb393609dedf6b30870115a57b603f2e6424f29a99abc13446fd45ee22461c33c9c40a92a60a8df62d0d6b25d74fc6676fa4cb211de55b55 + languageName: node + linkType: hard + "internal-slot@npm:^1.0.3, internal-slot@npm:^1.0.4, internal-slot@npm:^1.0.5": version: 1.0.5 resolution: "internal-slot@npm:1.0.5" @@ -13159,6 +13330,13 @@ __metadata: languageName: node linkType: hard +"is-node-process@npm:^1.0.1": + version: 1.0.1 + resolution: "is-node-process@npm:1.0.1" + checksum: 3ddb8a892a00f6eb9c2aea7e7e1426b8683512d9419933d95114f4f64b5455e26601c23a31c0682463890032136dd98a326988a770ab6b4eed54a43ade8bed50 + languageName: node + linkType: hard + "is-number-object@npm:^1.0.4": version: 1.0.7 resolution: "is-number-object@npm:1.0.7" @@ -14239,6 +14417,13 @@ __metadata: languageName: node linkType: hard +"js-levenshtein@npm:^1.1.6": + version: 1.1.6 + resolution: "js-levenshtein@npm:1.1.6" + checksum: 409f052a7f1141be4058d97da7860e08efd97fc588b7a4c5cfa0548bc04f6d576644dae65ab630266dff685d56fb90d494e03d4d79cb484c287746b4f1bf0694 + languageName: node + linkType: hard + "js-sdsl@npm:^4.1.4": version: 4.3.0 resolution: "js-sdsl@npm:4.3.0" @@ -15469,6 +15654,40 @@ __metadata: languageName: node linkType: hard +"msw@npm:^1.2.0": + version: 1.2.0 + resolution: "msw@npm:1.2.0" + dependencies: + "@mswjs/cookies": ^0.2.2 + "@mswjs/interceptors": ^0.17.5 + "@open-draft/until": ^1.0.3 + "@types/cookie": ^0.4.1 + "@types/js-levenshtein": ^1.1.1 + chalk: 4.1.1 + chokidar: ^3.4.2 + cookie: ^0.4.2 + graphql: ^15.0.0 || ^16.0.0 + headers-polyfill: ^3.1.0 + inquirer: ^8.2.0 + is-node-process: ^1.0.1 + js-levenshtein: ^1.1.6 + node-fetch: ^2.6.7 + outvariant: ^1.3.0 + path-to-regexp: ^6.2.0 + strict-event-emitter: ^0.4.3 + type-fest: ^2.19.0 + yargs: ^17.3.1 + peerDependencies: + typescript: ">= 4.4.x <= 5.0.x" + peerDependenciesMeta: + typescript: + optional: true + bin: + msw: cli/index.js + checksum: 2cea7fe0f2ebb59b11534896cbaa91dca65f4fb2272402549079cc4190847fc42f5367f331c552d576d59a3521f498da0384d53cf4960beaebd6165bbc44593b + languageName: node + linkType: hard + "multipipe@npm:^1.0.2": version: 1.0.2 resolution: "multipipe@npm:1.0.2" @@ -15490,6 +15709,13 @@ __metadata: languageName: node linkType: hard +"mute-stream@npm:0.0.8": + version: 0.0.8 + resolution: "mute-stream@npm:0.0.8" + checksum: ff48d251fc3f827e5b1206cda0ffdaec885e56057ee86a3155e1951bc940fd5f33531774b1cc8414d7668c10a8907f863f6561875ee6e8768931a62121a531a1 + languageName: node + linkType: hard + "mz@npm:^2.7.0": version: 2.7.0 resolution: "mz@npm:2.7.0" @@ -16150,7 +16376,7 @@ __metadata: languageName: node linkType: hard -"ora@npm:^5.1.0": +"ora@npm:^5.1.0, ora@npm:^5.4.1": version: 5.4.1 resolution: "ora@npm:5.4.1" dependencies: @@ -16174,6 +16400,20 @@ __metadata: languageName: node linkType: hard +"os-tmpdir@npm:~1.0.2": + version: 1.0.2 + resolution: "os-tmpdir@npm:1.0.2" + checksum: 5666560f7b9f10182548bf7013883265be33620b1c1b4a4d405c25be2636f970c5488ff3e6c48de75b55d02bde037249fe5dbfbb4c0fb7714953d56aed062e6d + languageName: node + linkType: hard + +"outvariant@npm:^1.2.1, outvariant@npm:^1.3.0": + version: 1.3.0 + resolution: "outvariant@npm:1.3.0" + checksum: ac76ca375c1c642989e1c74f0e9ebac84c05bc9fdc8f28be949c16fae1658e9f1f2fb1133fe3cc1e98afabef78fe4298fe9360b5734baf8e6ad440c182680848 + languageName: node + linkType: hard + "p-cancelable@npm:^2.0.0": version: 2.1.1 resolution: "p-cancelable@npm:2.1.1" @@ -16477,6 +16717,13 @@ __metadata: languageName: node linkType: hard +"path-to-regexp@npm:^6.2.0": + version: 6.2.1 + resolution: "path-to-regexp@npm:6.2.1" + checksum: f0227af8284ea13300f4293ba111e3635142f976d4197f14d5ad1f124aebd9118783dd2e5f1fe16f7273743cc3dbeddfb7493f237bb27c10fdae07020cc9b698 + languageName: node + linkType: hard + "path-type@npm:^2.0.0": version: 2.0.0 resolution: "path-type@npm:2.0.0" @@ -17848,6 +18095,13 @@ __metadata: languageName: node linkType: hard +"run-async@npm:^2.4.0": + version: 2.4.1 + resolution: "run-async@npm:2.4.1" + checksum: a2c88aa15df176f091a2878eb840e68d0bdee319d8d97bbb89112223259cebecb94bc0defd735662b83c2f7a30bed8cddb7d1674eb48ae7322dc602b22d03797 + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -17866,7 +18120,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^7.8.0": +"rxjs@npm:^7.5.5, rxjs@npm:^7.8.0": version: 7.8.0 resolution: "rxjs@npm:7.8.0" dependencies: @@ -18057,6 +18311,13 @@ __metadata: languageName: node linkType: hard +"set-cookie-parser@npm:^2.4.6": + version: 2.6.0 + resolution: "set-cookie-parser@npm:2.6.0" + checksum: bf11ebc594c53d84588f1b4c04f1b8ce14e0498b1c011b3d76b5c6d5aac481bbc3f7c5260ec4ce99bdc1d9aed19f9fc315e73166a36ca74d0f12349a73f6bdc9 + languageName: node + linkType: hard + "setprototypeof@npm:1.2.0": version: 1.2.0 resolution: "setprototypeof@npm:1.2.0" @@ -18541,6 +18802,22 @@ __metadata: languageName: node linkType: hard +"strict-event-emitter@npm:^0.2.4": + version: 0.2.8 + resolution: "strict-event-emitter@npm:0.2.8" + dependencies: + events: ^3.3.0 + checksum: 6ac06fe72a6ee6ae64d20f1dd42838ea67342f1b5f32b03b3050d73ee6ecee44b4d5c4ed2965a7154b47991e215f373d4e789e2b2be2769cd80e356126c2ca53 + languageName: node + linkType: hard + +"strict-event-emitter@npm:^0.4.3": + version: 0.4.6 + resolution: "strict-event-emitter@npm:0.4.6" + checksum: 4f4f2909613e7811de789991c06bfb770d6d6987e2ec5c66fa7485d0f07cc4e7e32eba0dcf26cee6d86af6c92946d7f4acdfaff57d0c4114df2cfa1bf0e3c091 + languageName: node + linkType: hard + "strict-uri-encode@npm:^2.0.0": version: 2.0.0 resolution: "strict-uri-encode@npm:2.0.0" @@ -19101,7 +19378,7 @@ __metadata: languageName: node linkType: hard -"through@npm:2, through@npm:^2.3.8, through@npm:~2.3, through@npm:~2.3.1": +"through@npm:2, through@npm:^2.3.6, through@npm:^2.3.8, through@npm:~2.3, through@npm:~2.3.1": version: 2.3.8 resolution: "through@npm:2.3.8" checksum: a38c3e059853c494af95d50c072b83f8b676a9ba2818dcc5b108ef252230735c54e0185437618596c790bbba8fcdaef5b290405981ffa09dce67b1f1bf190cbd @@ -19165,6 +19442,15 @@ __metadata: languageName: node linkType: hard +"tmp@npm:^0.0.33": + version: 0.0.33 + resolution: "tmp@npm:0.0.33" + dependencies: + os-tmpdir: ~1.0.2 + checksum: 902d7aceb74453ea02abbf58c203f4a8fc1cead89b60b31e354f74ed5b3fb09ea817f94fb310f884a5d16987dd9fa5a735412a7c2dd088dd3d415aa819ae3a28 + languageName: node + linkType: hard + "tmp@npm:^0.2.0": version: 0.2.1 resolution: "tmp@npm:0.2.1" @@ -19817,7 +20103,7 @@ __metadata: languageName: node linkType: hard -"util@npm:^0.12.0, util@npm:^0.12.4": +"util@npm:^0.12.0, util@npm:^0.12.3, util@npm:^0.12.4": version: 0.12.5 resolution: "util@npm:0.12.5" dependencies: @@ -20090,6 +20376,19 @@ __metadata: languageName: node linkType: hard +"web-encoding@npm:^1.1.5": + version: 1.1.5 + resolution: "web-encoding@npm:1.1.5" + dependencies: + "@zxing/text-encoding": 0.9.0 + util: ^0.12.3 + dependenciesMeta: + "@zxing/text-encoding": + optional: true + checksum: 2234a2b122f41006ce07859b3c0bf2e18f46144fda2907d5db0b571b76aa5c26977c646100ad9c00d2f8a4f6f2b848bc02147845d8c447ab365ec4eff376338d + languageName: node + linkType: hard + "web-streams-polyfill@npm:^3.0.3": version: 3.2.1 resolution: "web-streams-polyfill@npm:3.2.1"