refactor: init package @affine/workspace (#1661)

This commit is contained in:
Himself65
2023-03-23 16:17:38 +00:00
committed by GitHub
parent 84d27e939d
commit 69721f2a61
44 changed files with 952 additions and 236 deletions
+1
View File
@@ -15,6 +15,7 @@
"@affine/env": "workspace:*", "@affine/env": "workspace:*",
"@affine/i18n": "workspace:*", "@affine/i18n": "workspace:*",
"@affine/templates": "workspace:*", "@affine/templates": "workspace:*",
"@affine/workspace": "workspace:*",
"@blocksuite/blocks": "0.5.0-20230323085636-3110abb", "@blocksuite/blocks": "0.5.0-20230323085636-3110abb",
"@blocksuite/editor": "0.5.0-20230323085636-3110abb", "@blocksuite/editor": "0.5.0-20230323085636-3110abb",
"@blocksuite/icons": "2.0.23", "@blocksuite/icons": "2.0.23",
+3 -2
View File
@@ -1,3 +1,4 @@
import type { WorkspaceFlavour } from '@affine/workspace/type';
import type { EditorContainer } from '@blocksuite/editor'; import type { EditorContainer } from '@blocksuite/editor';
import type { Page } from '@blocksuite/store'; import type { Page } from '@blocksuite/store';
import { assertExists } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store';
@@ -6,7 +7,7 @@ import { atomWithStorage } from 'jotai/utils';
import { unstable_batchedUpdates } from 'react-dom'; import { unstable_batchedUpdates } from 'react-dom';
import { WorkspacePlugins } from '../plugins'; import { WorkspacePlugins } from '../plugins';
import type { RemWorkspace, RemWorkspaceFlavour } from '../shared'; import type { RemWorkspace } from '../shared';
// workspace necessary atoms // workspace necessary atoms
export const currentWorkspaceIdAtom = atom<string | null>(null); export const currentWorkspaceIdAtom = atom<string | null>(null);
export const currentPageIdAtom = atom<string | null>(null); export const currentPageIdAtom = atom<string | null>(null);
@@ -37,7 +38,7 @@ export const jotaiStore = createStore();
type JotaiWorkspace = { type JotaiWorkspace = {
id: string; id: string;
flavour: RemWorkspaceFlavour; flavour: WorkspaceFlavour;
}; };
export const jotaiWorkspacesAtom = atomWithStorage<JotaiWorkspace[]>( export const jotaiWorkspacesAtom = atomWithStorage<JotaiWorkspace[]>(
+1 -1
View File
@@ -1,8 +1,8 @@
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
import { atom } from 'jotai/index'; import { atom } from 'jotai/index';
import { BlockSuiteWorkspace } from '../../shared'; import { BlockSuiteWorkspace } from '../../shared';
import { apis } from '../../shared/apis'; import { apis } from '../../shared/apis';
import { createEmptyBlockSuiteWorkspace } from '../../utils';
export const publicWorkspaceIdAtom = atom<string | null>(null); export const publicWorkspaceIdAtom = atom<string | null>(null);
export const publicBlockSuiteAtom = atom<Promise<BlockSuiteWorkspace>>( export const publicBlockSuiteAtom = atom<Promise<BlockSuiteWorkspace>>(
@@ -1,11 +1,11 @@
'use client'; 'use client';
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
import type { EditorContainer } from '@blocksuite/editor'; import type { EditorContainer } from '@blocksuite/editor';
import type { Page } from '@blocksuite/store'; import type { Page } from '@blocksuite/store';
import { Generator } from '@blocksuite/store'; import { Generator } from '@blocksuite/store';
import type React from 'react'; import type React from 'react';
import { useCallback, useRef } from 'react'; import { useCallback, useRef } from 'react';
import { createEmptyBlockSuiteWorkspace } from '../../../utils';
import { BlockSuiteEditor } from '../../blocksuite/block-suite-editor'; import { BlockSuiteEditor } from '../../blocksuite/block-suite-editor';
const blockSuiteWorkspace = createEmptyBlockSuiteWorkspace( const blockSuiteWorkspace = createEmptyBlockSuiteWorkspace(
@@ -3,6 +3,7 @@
*/ */
import 'fake-indexeddb/auto'; import 'fake-indexeddb/auto';
import { WorkspaceFlavour } from '@affine/workspace/type';
import { assertExists } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store';
import { render, renderHook } from '@testing-library/react'; import { render, renderHook } from '@testing-library/react';
import { createStore, getDefaultStore, Provider } from 'jotai'; import { createStore, getDefaultStore, Provider } from 'jotai';
@@ -20,7 +21,7 @@ import {
import { useBlockSuiteWorkspaceHelper } from '../../hooks/use-blocksuite-workspace-helper'; import { useBlockSuiteWorkspaceHelper } from '../../hooks/use-blocksuite-workspace-helper';
import { useWorkspacesHelper } from '../../hooks/use-workspaces'; import { useWorkspacesHelper } from '../../hooks/use-workspaces';
import { ThemeProvider } from '../../providers/ThemeProvider'; import { ThemeProvider } from '../../providers/ThemeProvider';
import { pathGenerator, RemWorkspaceFlavour } from '../../shared'; import { pathGenerator } from '../../shared';
import { WorkSpaceSliderBar } from '../pure/workspace-slider-bar'; import { WorkSpaceSliderBar } from '../pure/workspace-slider-bar';
vi.mock('../blocksuite/header/editor-mode-switch/CustomLottie', () => ({ vi.mock('../blocksuite/header/editor-mode-switch/CustomLottie', () => ({
@@ -92,7 +93,7 @@ describe('WorkSpaceSliderBar', () => {
currentWorkspaceHook.result.current[1](id); currentWorkspaceHook.result.current[1](id);
const currentWorkspace = await store.get(currentWorkspaceAtom); const currentWorkspace = await store.get(currentWorkspaceAtom);
expect(currentWorkspace).toBeDefined(); expect(currentWorkspace).toBeDefined();
expect(currentWorkspace?.flavour).toBe(RemWorkspaceFlavour.LOCAL); expect(currentWorkspace?.flavour).toBe(WorkspaceFlavour.LOCAL);
expect(currentWorkspace?.id).toBe(id); expect(currentWorkspace?.id).toBe(id);
const app = render(<App />); const app = render(<App />);
const card = await app.findByTestId('current-workspace'); const card = await app.findByTestId('current-workspace');
@@ -1,4 +1,6 @@
import { useTranslation } from '@affine/i18n'; 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 { MouseEvent } from 'react';
import type React from 'react'; import type React from 'react';
import { Suspense, useCallback, useEffect, useMemo, useRef } 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 { useIsWorkspaceOwner } from '../../../hooks/affine/use-is-workspace-owner';
import { fetcher, QueryKey } from '../../../plugins/affine/fetcher'; import { fetcher, QueryKey } from '../../../plugins/affine/fetcher';
import type { import type { AffineOfficialWorkspace } from '../../../shared';
AffineOfficialWorkspace,
FlavourToWorkspace,
SettingPanel,
} from '../../../shared';
import { RemWorkspaceFlavour, settingPanel } from '../../../shared';
import { CollaborationPanel } from './panel/collaboration'; import { CollaborationPanel } from './panel/collaboration';
import { ExportPanel } from './panel/export'; import { ExportPanel } from './panel/export';
import { GeneralPanel } from './panel/general'; import { GeneralPanel } from './panel/general';
@@ -31,12 +28,12 @@ export type WorkspaceSettingDetailProps = {
onChangeTab: (tab: SettingPanel) => void; onChangeTab: (tab: SettingPanel) => void;
onDeleteWorkspace: () => void; onDeleteWorkspace: () => void;
onTransferWorkspace: < onTransferWorkspace: <
From extends RemWorkspaceFlavour, From extends WorkspaceFlavour,
To extends RemWorkspaceFlavour To extends WorkspaceFlavour
>( >(
from: From, from: From,
to: To, to: To,
workspace: FlavourToWorkspace[From] workspace: WorkspaceRegistry[From]
) => void; ) => void;
}; };
@@ -49,8 +46,7 @@ const panelMap = {
}, },
[settingPanel.Sync]: { [settingPanel.Sync]: {
name: 'Sync', name: 'Sync',
enable: (flavour: RemWorkspaceFlavour) => enable: (flavour: WorkspaceFlavour) => flavour === WorkspaceFlavour.AFFINE,
flavour === RemWorkspaceFlavour.AFFINE,
ui: SyncPanel, ui: SyncPanel,
}, },
[settingPanel.Collaboration]: { [settingPanel.Collaboration]: {
@@ -68,7 +64,7 @@ const panelMap = {
} satisfies { } satisfies {
[Key in SettingPanel]: { [Key in SettingPanel]: {
name: string; name: string;
enable?: (flavour: RemWorkspaceFlavour) => boolean; enable?: (flavour: WorkspaceFlavour) => boolean;
ui: React.FC<PanelProps>; ui: React.FC<PanelProps>;
}; };
}; };
@@ -8,6 +8,7 @@ import {
} from '@affine/component'; } from '@affine/component';
import { PermissionType } from '@affine/datacenter'; import { PermissionType } from '@affine/datacenter';
import { useTranslation } from '@affine/i18n'; import { useTranslation } from '@affine/i18n';
import { WorkspaceFlavour } from '@affine/workspace/type';
import { import {
DeleteTemporarilyIcon, DeleteTemporarilyIcon,
EmailIcon, EmailIcon,
@@ -18,7 +19,6 @@ import { useCallback, useState } from 'react';
import { useMembers } from '../../../../../hooks/affine/use-members'; import { useMembers } from '../../../../../hooks/affine/use-members';
import type { AffineWorkspace, LocalWorkspace } from '../../../../../shared'; import type { AffineWorkspace, LocalWorkspace } from '../../../../../shared';
import { RemWorkspaceFlavour } from '../../../../../shared';
import { Unreachable } from '../../../affine-error-eoundary'; import { Unreachable } from '../../../affine-error-eoundary';
import { TransformWorkspaceToAffineModal } from '../../../transform-workspace-to-affine-modal'; import { TransformWorkspaceToAffineModal } from '../../../transform-workspace-to-affine-modal';
import type { PanelProps } from '../../index'; import type { PanelProps } from '../../index';
@@ -194,8 +194,8 @@ const LocalCollaborationPanel: React.FC<
}} }}
onConform={() => { onConform={() => {
onTransferWorkspace( onTransferWorkspace(
RemWorkspaceFlavour.LOCAL, WorkspaceFlavour.LOCAL,
RemWorkspaceFlavour.AFFINE, WorkspaceFlavour.AFFINE,
workspace workspace
); );
setOpen(false); setOpen(false);
@@ -207,13 +207,13 @@ const LocalCollaborationPanel: React.FC<
export const CollaborationPanel: React.FC<PanelProps> = props => { export const CollaborationPanel: React.FC<PanelProps> = props => {
switch (props.workspace.flavour) { switch (props.workspace.flavour) {
case RemWorkspaceFlavour.AFFINE: { case WorkspaceFlavour.AFFINE: {
const workspace = props.workspace as AffineWorkspace; const workspace = props.workspace as AffineWorkspace;
return ( return (
<AffineRemoteCollaborationPanel {...props} workspace={workspace} /> <AffineRemoteCollaborationPanel {...props} workspace={workspace} />
); );
} }
case RemWorkspaceFlavour.LOCAL: { case WorkspaceFlavour.LOCAL: {
const workspace = props.workspace as LocalWorkspace; const workspace = props.workspace as LocalWorkspace;
return <LocalCollaborationPanel {...props} workspace={workspace} />; return <LocalCollaborationPanel {...props} workspace={workspace} />;
} }
@@ -1,10 +1,10 @@
import { Button, Input, Modal, ModalCloseButton } from '@affine/component'; import { Button, Input, Modal, ModalCloseButton } from '@affine/component';
import { Trans, useTranslation } from '@affine/i18n'; import { Trans, useTranslation } from '@affine/i18n';
import { WorkspaceFlavour } from '@affine/workspace/type';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { useBlockSuiteWorkspaceName } from '../../../../../../hooks/use-blocksuite-workspace-name'; import { useBlockSuiteWorkspaceName } from '../../../../../../hooks/use-blocksuite-workspace-name';
import type { AffineOfficialWorkspace } from '../../../../../../shared'; import type { AffineOfficialWorkspace } from '../../../../../../shared';
import { RemWorkspaceFlavour } from '../../../../../../shared';
import { import {
StyledButtonContent, StyledButtonContent,
StyledInputContent, StyledInputContent,
@@ -43,7 +43,7 @@ export const WorkspaceDeleteModal = ({
<StyledModalWrapper> <StyledModalWrapper>
<ModalCloseButton onClick={onClose} /> <ModalCloseButton onClick={onClose} />
<StyledModalHeader>{t('Delete Workspace')}?</StyledModalHeader> <StyledModalHeader>{t('Delete Workspace')}?</StyledModalHeader>
{workspace.flavour === RemWorkspaceFlavour.LOCAL ? ( {workspace.flavour === WorkspaceFlavour.LOCAL ? (
<StyledTextContent> <StyledTextContent>
<Trans i18nKey="Delete Workspace Description"> <Trans i18nKey="Delete Workspace Description">
Deleting ( Deleting (
@@ -1,12 +1,12 @@
import { Button, FlexWrapper, MuiFade } from '@affine/component'; import { Button, FlexWrapper, MuiFade } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useTranslation } from '@affine/i18n';
import { WorkspaceFlavour } from '@affine/workspace/type';
import type React from 'react'; import type React from 'react';
import { useState } from 'react'; import { useState } from 'react';
import { useIsWorkspaceOwner } from '../../../../../hooks/affine/use-is-workspace-owner'; import { useIsWorkspaceOwner } from '../../../../../hooks/affine/use-is-workspace-owner';
import { useBlockSuiteWorkspaceAvatarUrl } from '../../../../../hooks/use-blocksuite-workspace-avatar-url'; import { useBlockSuiteWorkspaceAvatarUrl } from '../../../../../hooks/use-blocksuite-workspace-avatar-url';
import { useBlockSuiteWorkspaceName } from '../../../../../hooks/use-blocksuite-workspace-name'; import { useBlockSuiteWorkspaceName } from '../../../../../hooks/use-blocksuite-workspace-name';
import { RemWorkspaceFlavour } from '../../../../../shared';
import { Upload } from '../../../../pure/file-upload'; import { Upload } from '../../../../pure/file-upload';
import { import {
CloudWorkspaceIcon, CloudWorkspaceIcon,
@@ -161,7 +161,7 @@ export const GeneralPanel: React.FC<PanelProps> = ({
<StyledRow> <StyledRow>
<StyledSettingKey>{t('Workspace Type')}</StyledSettingKey> <StyledSettingKey>{t('Workspace Type')}</StyledSettingKey>
{isOwner ? ( {isOwner ? (
workspace.flavour === RemWorkspaceFlavour.LOCAL ? ( workspace.flavour === WorkspaceFlavour.LOCAL ? (
<StyledWorkspaceInfo> <StyledWorkspaceInfo>
<LocalWorkspaceIcon /> <LocalWorkspaceIcon />
<span>{t('Local Workspace')}</span> <span>{t('Local Workspace')}</span>
@@ -7,6 +7,7 @@ import {
Wrapper, Wrapper,
} from '@affine/component'; } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useTranslation } from '@affine/i18n';
import { WorkspaceFlavour } from '@affine/workspace/type';
import { Box } from '@mui/material'; import { Box } from '@mui/material';
import type React from 'react'; import type React from 'react';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
@@ -17,7 +18,6 @@ import type {
AffineWorkspace, AffineWorkspace,
LocalWorkspace, LocalWorkspace,
} from '../../../../../shared'; } from '../../../../../shared';
import { RemWorkspaceFlavour } from '../../../../../shared';
import { Unreachable } from '../../../affine-error-eoundary'; import { Unreachable } from '../../../affine-error-eoundary';
import { EnableAffineCloudModal } from '../../../enable-affine-cloud-modal'; import { EnableAffineCloudModal } from '../../../enable-affine-cloud-modal';
import type { WorkspaceSettingDetailProps } from '../../index'; import type { WorkspaceSettingDetailProps } from '../../index';
@@ -133,8 +133,8 @@ const PublishPanelLocal: React.FC<PublishPanelLocalProps> = ({
}} }}
onConfirm={() => { onConfirm={() => {
onTransferWorkspace( onTransferWorkspace(
RemWorkspaceFlavour.LOCAL, WorkspaceFlavour.LOCAL,
RemWorkspaceFlavour.AFFINE, WorkspaceFlavour.AFFINE,
workspace workspace
); );
setOpen(false); setOpen(false);
@@ -145,9 +145,9 @@ const PublishPanelLocal: React.FC<PublishPanelLocalProps> = ({
}; };
export const PublishPanel: React.FC<PublishPanelProps> = props => { export const PublishPanel: React.FC<PublishPanelProps> = props => {
if (props.workspace.flavour === RemWorkspaceFlavour.AFFINE) { if (props.workspace.flavour === WorkspaceFlavour.AFFINE) {
return <PublishPanelAffine {...props} workspace={props.workspace} />; return <PublishPanelAffine {...props} workspace={props.workspace} />;
} else if (props.workspace.flavour === RemWorkspaceFlavour.LOCAL) { } else if (props.workspace.flavour === WorkspaceFlavour.LOCAL) {
return <PublishPanelLocal {...props} workspace={props.workspace} />; return <PublishPanelLocal {...props} workspace={props.workspace} />;
} }
throw new Unreachable(); throw new Unreachable();
@@ -1,11 +1,11 @@
import { Content, FlexWrapper, styled } from '@affine/component'; import { Content, FlexWrapper, styled } from '@affine/component';
import { Trans, useTranslation } from '@affine/i18n'; import { Trans, useTranslation } from '@affine/i18n';
import { WorkspaceFlavour } from '@affine/workspace/type';
import type React from 'react'; import type React from 'react';
import { useCurrentUser } from '../../../../../hooks/current/use-current-user'; import { useCurrentUser } from '../../../../../hooks/current/use-current-user';
import { useBlockSuiteWorkspaceAvatarUrl } from '../../../../../hooks/use-blocksuite-workspace-avatar-url'; import { useBlockSuiteWorkspaceAvatarUrl } from '../../../../../hooks/use-blocksuite-workspace-avatar-url';
import { useBlockSuiteWorkspaceName } from '../../../../../hooks/use-blocksuite-workspace-name'; import { useBlockSuiteWorkspaceName } from '../../../../../hooks/use-blocksuite-workspace-name';
import { RemWorkspaceFlavour } from '../../../../../shared';
import { WorkspaceAvatar } from '../../../../pure/footer'; import { WorkspaceAvatar } from '../../../../pure/footer';
import type { PanelProps } from '../../index'; import type { PanelProps } from '../../index';
@@ -17,7 +17,7 @@ export const StyledWorkspaceName = styled('span')(({ theme }) => {
}); });
export const SyncPanel: React.FC<PanelProps> = ({ workspace }) => { export const SyncPanel: React.FC<PanelProps> = ({ workspace }) => {
if (workspace.flavour !== RemWorkspaceFlavour.AFFINE) { if (workspace.flavour !== WorkspaceFlavour.AFFINE) {
throw new TypeError('SyncPanel can only be used with Affine workspace'); throw new TypeError('SyncPanel can only be used with Affine workspace');
} }
const [name] = useBlockSuiteWorkspaceName(workspace.blockSuiteWorkspace); const [name] = useBlockSuiteWorkspaceName(workspace.blockSuiteWorkspace);
@@ -1,5 +1,6 @@
import { displayFlex, IconButton, styled, Tooltip } from '@affine/component'; import { displayFlex, IconButton, styled, Tooltip } from '@affine/component';
import { useTranslation } from '@affine/i18n'; import { useTranslation } from '@affine/i18n';
import { WorkspaceFlavour } from '@affine/workspace/type';
import { import {
CloudWorkspaceIcon, CloudWorkspaceIcon,
LocalWorkspaceIcon, LocalWorkspaceIcon,
@@ -15,7 +16,6 @@ import type {
AffineOfficialWorkspace, AffineOfficialWorkspace,
LocalWorkspace, LocalWorkspace,
} from '../../../../shared'; } from '../../../../shared';
import { RemWorkspaceFlavour } from '../../../../shared';
import { apis } from '../../../../shared/apis'; import { apis } from '../../../../shared/apis';
import { TransformWorkspaceToAffineModal } from '../../../affine/transform-workspace-to-affine-modal'; import { TransformWorkspaceToAffineModal } from '../../../affine/transform-workspace-to-affine-modal';
@@ -117,10 +117,10 @@ export const SyncUser = () => {
router.reload(); router.reload();
return; return;
} }
assertEquals(workspace.flavour, RemWorkspaceFlavour.LOCAL); assertEquals(workspace.flavour, WorkspaceFlavour.LOCAL);
const id = await transformWorkspace( const id = await transformWorkspace(
RemWorkspaceFlavour.LOCAL, WorkspaceFlavour.LOCAL,
RemWorkspaceFlavour.AFFINE, WorkspaceFlavour.AFFINE,
workspace as LocalWorkspace workspace as LocalWorkspace
); );
// fixme(himself65): refactor this // fixme(himself65): refactor this
@@ -1,12 +1,12 @@
import { PermissionType } from '@affine/datacenter'; import { PermissionType } from '@affine/datacenter';
import { useTranslation } from '@affine/i18n'; import { useTranslation } from '@affine/i18n';
import { WorkspaceFlavour } from '@affine/workspace/type';
import { SettingsIcon } from '@blocksuite/icons'; import { SettingsIcon } from '@blocksuite/icons';
import type React from 'react'; import type React from 'react';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useBlockSuiteWorkspaceName } from '../../../hooks/use-blocksuite-workspace-name'; import { useBlockSuiteWorkspaceName } from '../../../hooks/use-blocksuite-workspace-name';
import type { RemWorkspace } from '../../../shared'; import type { RemWorkspace } from '../../../shared';
import { RemWorkspaceFlavour } from '../../../shared';
import { import {
CloudWorkspaceIcon, CloudWorkspaceIcon,
JoinedWorkspaceIcon, JoinedWorkspaceIcon,
@@ -28,13 +28,13 @@ export type WorkspaceTypeProps = {
const WorkspaceType: React.FC<WorkspaceTypeProps> = ({ workspace }) => { const WorkspaceType: React.FC<WorkspaceTypeProps> = ({ workspace }) => {
const { t } = useTranslation(); const { t } = useTranslation();
let isOwner = true; let isOwner = true;
if (workspace.flavour === RemWorkspaceFlavour.AFFINE) { if (workspace.flavour === WorkspaceFlavour.AFFINE) {
isOwner = workspace.permission === PermissionType.Owner; isOwner = workspace.permission === PermissionType.Owner;
} else if (workspace.flavour === RemWorkspaceFlavour.LOCAL) { } else if (workspace.flavour === WorkspaceFlavour.LOCAL) {
isOwner = true; isOwner = true;
} }
if (workspace.flavour === RemWorkspaceFlavour.LOCAL) { if (workspace.flavour === WorkspaceFlavour.LOCAL) {
return ( return (
<p title={t('Local Workspace')}> <p title={t('Local Workspace')}>
<LocalWorkspaceIcon /> <LocalWorkspaceIcon />
@@ -85,19 +85,18 @@ export const WorkspaceCard: React.FC<WorkspaceCardProps> = ({
<StyleWorkspaceInfo> <StyleWorkspaceInfo>
<StyleWorkspaceTitle>{name}</StyleWorkspaceTitle> <StyleWorkspaceTitle>{name}</StyleWorkspaceTitle>
<WorkspaceType workspace={workspace} /> <WorkspaceType workspace={workspace} />
{workspace.flavour === RemWorkspaceFlavour.LOCAL && ( {workspace.flavour === WorkspaceFlavour.LOCAL && (
<p title={t('Available Offline')}> <p title={t('Available Offline')}>
<LocalDataIcon /> <LocalDataIcon />
<span>{t('Available Offline')}</span> <span>{t('Available Offline')}</span>
</p> </p>
)} )}
{workspace.flavour === RemWorkspaceFlavour.AFFINE && {workspace.flavour === WorkspaceFlavour.AFFINE && workspace.public && (
workspace.public && ( <p title={t('Published to Web')}>
<p title={t('Published to Web')}> <PublishIcon />
<PublishIcon /> <span>{t('Published to Web')}</span>
<span>{t('Published to Web')}</span> </p>
</p> )}
)}
</StyleWorkspaceInfo> </StyleWorkspaceInfo>
<StyledSettingLink <StyledSettingLink
className="setting-entry" className="setting-entry"
+5 -4
View File
@@ -5,6 +5,7 @@ import 'fake-indexeddb/auto';
import assert from 'node:assert'; import assert from 'node:assert';
import { WorkspaceFlavour } from '@affine/workspace/type';
import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models'; import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models';
import type { Page } from '@blocksuite/store'; import type { Page } from '@blocksuite/store';
import { assertExists } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store';
@@ -23,7 +24,7 @@ import {
} from '../../atoms'; } from '../../atoms';
import { LocalPlugin } from '../../plugins/local'; import { LocalPlugin } from '../../plugins/local';
import type { LocalWorkspace } from '../../shared'; import type { LocalWorkspace } from '../../shared';
import { BlockSuiteWorkspace, RemWorkspaceFlavour } from '../../shared'; import { BlockSuiteWorkspace } from '../../shared';
import { useIsFirstLoad, useOpenTips } from '../affine/use-is-first-load'; import { useIsFirstLoad, useOpenTips } from '../affine/use-is-first-load';
import { import {
useRecentlyViewed, useRecentlyViewed,
@@ -181,7 +182,7 @@ describe('useWorkspaces', () => {
expect(result2.current.length).toEqual(1); expect(result2.current.length).toEqual(1);
const firstWorkspace = result2.current[0]; const firstWorkspace = result2.current[0];
expect(firstWorkspace.flavour).toBe('local'); expect(firstWorkspace.flavour).toBe('local');
assert(firstWorkspace.flavour === RemWorkspaceFlavour.LOCAL); assert(firstWorkspace.flavour === WorkspaceFlavour.LOCAL);
expect(firstWorkspace.blockSuiteWorkspace.meta.name).toBe('test'); expect(firstWorkspace.blockSuiteWorkspace.meta.name).toBe('test');
}); });
}); });
@@ -266,12 +267,12 @@ describe('useRecentlyViewed', () => {
store.set(jotaiWorkspacesAtom, [ store.set(jotaiWorkspacesAtom, [
{ {
id: workspaceId, id: workspaceId,
flavour: RemWorkspaceFlavour.LOCAL, flavour: WorkspaceFlavour.LOCAL,
}, },
]); ]);
LocalPlugin.CRUD.get = vi.fn().mockResolvedValue({ LocalPlugin.CRUD.get = vi.fn().mockResolvedValue({
id: workspaceId, id: workspaceId,
flavour: RemWorkspaceFlavour.LOCAL, flavour: WorkspaceFlavour.LOCAL,
blockSuiteWorkspace, blockSuiteWorkspace,
providers: [], providers: [],
} satisfies LocalWorkspace); } satisfies LocalWorkspace);
@@ -1,12 +1,12 @@
import { DEFAULT_WORKSPACE_NAME } from '@affine/env'; 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 { assertEquals, assertExists, nanoid } from '@blocksuite/store';
import { useAtom } from 'jotai/index'; import { useAtom } from 'jotai';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { jotaiWorkspacesAtom } from '../atoms'; import { jotaiWorkspacesAtom } from '../atoms';
import { LocalPlugin } from '../plugins/local'; import { LocalPlugin } from '../plugins/local';
import { RemWorkspaceFlavour } from '../shared';
import { createEmptyBlockSuiteWorkspace } from '../utils';
export function useCreateFirstWorkspace() { export function useCreateFirstWorkspace() {
const [jotaiWorkspaces, set] = useAtom(jotaiWorkspacesAtom); const [jotaiWorkspaces, set] = useAtom(jotaiWorkspacesAtom);
@@ -29,7 +29,7 @@ export function useCreateFirstWorkspace() {
set([ set([
{ {
id: workspace.id, id: workspace.id,
flavour: RemWorkspaceFlavour.LOCAL, flavour: WorkspaceFlavour.LOCAL,
}, },
]); ]);
} }
@@ -1,9 +1,9 @@
import { WorkspaceFlavour } from '@affine/workspace/type';
import type { NextRouter } from 'next/router'; import type { NextRouter } from 'next/router';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { currentPageIdAtom, jotaiStore } from '../atoms'; import { currentPageIdAtom, jotaiStore } from '../atoms';
import type { RemWorkspace } from '../shared'; import type { RemWorkspace } from '../shared';
import { RemWorkspaceFlavour } from '../shared';
import { useCurrentPageId } from './current/use-current-page-id'; import { useCurrentPageId } from './current/use-current-page-id';
import { useCurrentWorkspace } from './current/use-current-workspace'; import { useCurrentWorkspace } from './current/use-current-workspace';
import { useWorkspaces } from './use-workspaces'; import { useWorkspaces } from './use-workspaces';
@@ -13,7 +13,7 @@ export function findSuitablePageId(
targetId: string targetId: string
): string | null { ): string | null {
switch (workspace.flavour) { switch (workspace.flavour) {
case RemWorkspaceFlavour.AFFINE: { case WorkspaceFlavour.AFFINE: {
return ( return (
workspace.blockSuiteWorkspace.meta.pageMetas.find( workspace.blockSuiteWorkspace.meta.pageMetas.find(
page => page.id === targetId page => page.id === targetId
@@ -22,7 +22,7 @@ export function findSuitablePageId(
null null
); );
} }
case RemWorkspaceFlavour.LOCAL: { case WorkspaceFlavour.LOCAL: {
return ( return (
workspace.blockSuiteWorkspace.meta.pageMetas.find( workspace.blockSuiteWorkspace.meta.pageMetas.find(
page => page.id === targetId page => page.id === targetId
@@ -1,9 +1,10 @@
import type { WorkspaceFlavour } from '@affine/workspace/type';
import type { WorkspaceRegistry } from '@affine/workspace/type';
import { useSetAtom } from 'jotai'; import { useSetAtom } from 'jotai';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { jotaiWorkspacesAtom } from '../atoms'; import { jotaiWorkspacesAtom } from '../atoms';
import { WorkspacePlugins } from '../plugins'; import { WorkspacePlugins } from '../plugins';
import type { FlavourToWorkspace, RemWorkspaceFlavour } from '../shared';
/** /**
* Transform workspace from one flavour to another * Transform workspace from one flavour to another
@@ -13,10 +14,10 @@ import type { FlavourToWorkspace, RemWorkspaceFlavour } from '../shared';
export function useTransformWorkspace() { export function useTransformWorkspace() {
const set = useSetAtom(jotaiWorkspacesAtom); const set = useSetAtom(jotaiWorkspacesAtom);
return useCallback( return useCallback(
async <From extends RemWorkspaceFlavour, To extends RemWorkspaceFlavour>( async <From extends WorkspaceFlavour, To extends WorkspaceFlavour>(
from: From, from: From,
to: To, to: To,
workspace: FlavourToWorkspace[From] workspace: WorkspaceRegistry[From]
): Promise<string> => { ): Promise<string> => {
await WorkspacePlugins[from].CRUD.delete(workspace as any); await WorkspacePlugins[from].CRUD.delete(workspace as any);
const newId = await WorkspacePlugins[to].CRUD.create( const newId = await WorkspacePlugins[to].CRUD.create(
+3 -3
View File
@@ -1,3 +1,5 @@
import { WorkspaceFlavour } from '@affine/workspace/type';
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
import { nanoid } from '@blocksuite/store'; import { nanoid } from '@blocksuite/store';
import { useAtomValue, useSetAtom } from 'jotai'; import { useAtomValue, useSetAtom } from 'jotai';
import { useCallback, useEffect } from 'react'; import { useCallback, useEffect } from 'react';
@@ -6,8 +8,6 @@ import { jotaiWorkspacesAtom, workspacesAtom } from '../atoms';
import { WorkspacePlugins } from '../plugins'; import { WorkspacePlugins } from '../plugins';
import { LocalPlugin } from '../plugins/local'; import { LocalPlugin } from '../plugins/local';
import type { LocalWorkspace, RemWorkspace } from '../shared'; import type { LocalWorkspace, RemWorkspace } from '../shared';
import { RemWorkspaceFlavour } from '../shared';
import { createEmptyBlockSuiteWorkspace } from '../utils';
export function useWorkspaces(): RemWorkspace[] { export function useWorkspaces(): RemWorkspace[] {
return useAtomValue(workspacesAtom); return useAtomValue(workspacesAtom);
@@ -43,7 +43,7 @@ export function useWorkspacesHelper() {
...workspaces, ...workspaces,
{ {
id, id,
flavour: RemWorkspaceFlavour.LOCAL, flavour: WorkspaceFlavour.LOCAL,
}, },
]); ]);
return id; return id;
+1 -1
View File
@@ -1,5 +1,6 @@
import { Button, toast } from '@affine/component'; import { Button, toast } from '@affine/component';
import { DebugLogger } from '@affine/debug'; import { DebugLogger } from '@affine/debug';
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
import { nanoid } from '@blocksuite/store'; import { nanoid } from '@blocksuite/store';
import { Typography } from '@mui/material'; import { Typography } from '@mui/material';
import type React from 'react'; 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 PageList from '../../components/blocksuite/block-suite-page-list/page-list';
import { StyledPage, StyledWrapper } from '../../layouts/styles'; import { StyledPage, StyledWrapper } from '../../layouts/styles';
import type { BroadCastChannelProvider } from '../../shared'; import type { BroadCastChannelProvider } from '../../shared';
import { createEmptyBlockSuiteWorkspace } from '../../utils';
const logger = new DebugLogger('broadcast'); const logger = new DebugLogger('broadcast');
+78
View File
@@ -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 (
<StyledPage>
<StyledWrapper>
<h1>LoginDevPage</h1>
<Button
onClick={async () => {
const storage = getLoginStorage();
if (storage) {
const user = parseIdToken(storage.token);
if (isExpired(user)) {
await auth.refreshToken(storage);
}
}
const response = await auth.generateToken(SignMethod.Google);
if (response) {
setLoginStorage(response);
const user = parseIdToken(response.token);
setUser(user);
} else {
toast('Login failed');
}
}}
>
Login
</Button>
<Button
onClick={async () => {
const storage = getLoginStorage();
if (!storage) {
throw new Error('No storage');
}
const response = await auth.refreshToken(storage);
if (response) {
setLoginStorage(response);
const user = parseIdToken(response.token);
setUser(user);
} else {
toast('Login failed');
}
}}
>
Refresh Token
</Button>
<Button
onClick={() => {
clearLoginStorage();
setUser(null);
}}
>
Reset Storage
</Button>
{user && JSON.stringify(user)}
</StyledWrapper>
</StyledPage>
);
};
export default LoginDevPage;
+1 -1
View File
@@ -1,3 +1,4 @@
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
import { ContentParser } from '@blocksuite/blocks/content-parser'; import { ContentParser } from '@blocksuite/blocks/content-parser';
import type { import type {
GetStaticPaths, GetStaticPaths,
@@ -16,7 +17,6 @@ import {
StyledWrapper, StyledWrapper,
} from '../../layouts/styles'; } from '../../layouts/styles';
import type { BlockSuiteWorkspace } from '../../shared'; import type { BlockSuiteWorkspace } from '../../shared';
import { createEmptyBlockSuiteWorkspace } from '../../utils';
export type PreviewPageProps = { export type PreviewPageProps = {
text: string; text: string;
@@ -1,3 +1,4 @@
import { WorkspaceFlavour } from '@affine/workspace/type';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import type React from 'react'; import type React from 'react';
import { useEffect } from 'react'; import { useEffect } from 'react';
@@ -11,7 +12,6 @@ import { useSyncRouterWithCurrentWorkspaceAndPage } from '../../../hooks/use-syn
import { WorkspaceLayout } from '../../../layouts'; import { WorkspaceLayout } from '../../../layouts';
import { WorkspacePlugins } from '../../../plugins'; import { WorkspacePlugins } from '../../../plugins';
import type { BlockSuiteWorkspace, NextPageWithLayout } from '../../../shared'; import type { BlockSuiteWorkspace, NextPageWithLayout } from '../../../shared';
import { RemWorkspaceFlavour } from '../../../shared';
function enableFullFlags(blockSuiteWorkspace: BlockSuiteWorkspace) { function enableFullFlags(blockSuiteWorkspace: BlockSuiteWorkspace) {
blockSuiteWorkspace.awarenessStore.setFlag('enable_set_remote_flag', false); blockSuiteWorkspace.awarenessStore.setFlag('enable_set_remote_flag', false);
@@ -38,12 +38,12 @@ const WorkspaceDetail: React.FC = () => {
if (!pageId) { if (!pageId) {
return <PageLoading />; return <PageLoading />;
} }
if (currentWorkspace.flavour === RemWorkspaceFlavour.AFFINE) { if (currentWorkspace.flavour === WorkspaceFlavour.AFFINE) {
const PageDetail = WorkspacePlugins[currentWorkspace.flavour].UI.PageDetail; const PageDetail = WorkspacePlugins[currentWorkspace.flavour].UI.PageDetail;
return ( return (
<PageDetail currentWorkspace={currentWorkspace} currentPageId={pageId} /> <PageDetail currentWorkspace={currentWorkspace} currentPageId={pageId} />
); );
} else if (currentWorkspace.flavour === RemWorkspaceFlavour.LOCAL) { } else if (currentWorkspace.flavour === WorkspaceFlavour.LOCAL) {
const PageDetail = WorkspacePlugins[currentWorkspace.flavour].UI.PageDetail; const PageDetail = WorkspacePlugins[currentWorkspace.flavour].UI.PageDetail;
return ( return (
<PageDetail currentWorkspace={currentWorkspace} currentPageId={pageId} /> <PageDetail currentWorkspace={currentWorkspace} currentPageId={pageId} />
@@ -1,4 +1,5 @@
import { useTranslation } from '@affine/i18n'; import { useTranslation } from '@affine/i18n';
import { WorkspaceFlavour } from '@affine/workspace/type';
import { FolderIcon } from '@blocksuite/icons'; import { FolderIcon } from '@blocksuite/icons';
import { assertEquals, assertExists, nanoid } from '@blocksuite/store'; import { assertEquals, assertExists, nanoid } from '@blocksuite/store';
import Head from 'next/head'; import Head from 'next/head';
@@ -20,7 +21,6 @@ import type {
LocalIndexedDBProvider, LocalIndexedDBProvider,
NextPageWithLayout, NextPageWithLayout,
} from '../../../shared'; } from '../../../shared';
import { RemWorkspaceFlavour } from '../../../shared';
const AllPage: NextPageWithLayout = () => { const AllPage: NextPageWithLayout = () => {
const router = useRouter(); const router = useRouter();
@@ -78,7 +78,7 @@ const AllPage: NextPageWithLayout = () => {
if (currentWorkspace === null) { if (currentWorkspace === null) {
return <PageLoading />; return <PageLoading />;
} }
if (currentWorkspace.flavour === RemWorkspaceFlavour.AFFINE) { if (currentWorkspace.flavour === WorkspaceFlavour.AFFINE) {
const PageList = WorkspacePlugins[currentWorkspace.flavour].UI.PageList; const PageList = WorkspacePlugins[currentWorkspace.flavour].UI.PageList;
return ( 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; const PageList = WorkspacePlugins[currentWorkspace.flavour].UI.PageList;
return ( return (
<> <>
@@ -1,4 +1,10 @@
import { useTranslation } from '@affine/i18n'; 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 { SettingsIcon } from '@blocksuite/icons';
import { assertExists } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
@@ -16,16 +22,7 @@ import { useTransformWorkspace } from '../../../hooks/use-transform-workspace';
import { useWorkspacesHelper } from '../../../hooks/use-workspaces'; import { useWorkspacesHelper } from '../../../hooks/use-workspaces';
import { WorkspaceLayout } from '../../../layouts'; import { WorkspaceLayout } from '../../../layouts';
import { WorkspacePlugins } from '../../../plugins'; import { WorkspacePlugins } from '../../../plugins';
import type { import type { NextPageWithLayout } from '../../../shared';
FlavourToWorkspace,
NextPageWithLayout,
SettingPanel,
} from '../../../shared';
import {
RemWorkspaceFlavour,
settingPanel,
settingPanelValues,
} from '../../../shared';
import { apis } from '../../../shared/apis'; import { apis } from '../../../shared/apis';
const settingPanelAtom = atomWithStorage<SettingPanel>( const settingPanelAtom = atomWithStorage<SettingPanel>(
@@ -105,13 +102,12 @@ const SettingPage: NextPageWithLayout = () => {
}, [currentWorkspace, helper]); }, [currentWorkspace, helper]);
const transformWorkspace = useTransformWorkspace(); const transformWorkspace = useTransformWorkspace();
const onTransformWorkspace = useCallback( const onTransformWorkspace = useCallback(
async <From extends RemWorkspaceFlavour, To extends RemWorkspaceFlavour>( async <From extends WorkspaceFlavour, To extends WorkspaceFlavour>(
from: From, from: From,
to: To, to: To,
workspace: FlavourToWorkspace[From] workspace: WorkspaceRegistry[From]
): Promise<void> => { ): Promise<void> => {
const needRefresh = const needRefresh = to === WorkspaceFlavour.AFFINE && !apis.auth.isLogin;
to === RemWorkspaceFlavour.AFFINE && !apis.auth.isLogin;
if (needRefresh) { if (needRefresh) {
await apis.signInWithGoogle(); await apis.signInWithGoogle();
} }
@@ -135,7 +131,7 @@ const SettingPage: NextPageWithLayout = () => {
return <PageLoading />; return <PageLoading />;
} else if (settingPanelValues.indexOf(currentTab as SettingPanel) === -1) { } else if (settingPanelValues.indexOf(currentTab as SettingPanel) === -1) {
return <PageLoading />; return <PageLoading />;
} else if (currentWorkspace.flavour === RemWorkspaceFlavour.AFFINE) { } else if (currentWorkspace.flavour === WorkspaceFlavour.AFFINE) {
const Setting = const Setting =
WorkspacePlugins[currentWorkspace.flavour].UI.SettingsDetail; WorkspacePlugins[currentWorkspace.flavour].UI.SettingsDetail;
return ( return (
@@ -155,7 +151,7 @@ const SettingPage: NextPageWithLayout = () => {
/> />
</> </>
); );
} else if (currentWorkspace.flavour === RemWorkspaceFlavour.LOCAL) { } else if (currentWorkspace.flavour === WorkspaceFlavour.LOCAL) {
const Setting = const Setting =
WorkspacePlugins[currentWorkspace.flavour].UI.SettingsDetail; WorkspacePlugins[currentWorkspace.flavour].UI.SettingsDetail;
return ( return (
+3 -3
View File
@@ -1,12 +1,12 @@
import { WorkspaceFlavour } from '@affine/workspace/type';
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
import { assertExists } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store';
import { jotaiStore, workspacesAtom } from '../../atoms'; import { jotaiStore, workspacesAtom } from '../../atoms';
import { createAffineProviders } from '../../blocksuite'; import { createAffineProviders } from '../../blocksuite';
import { Unreachable } from '../../components/affine/affine-error-eoundary'; import { Unreachable } from '../../components/affine/affine-error-eoundary';
import type { AffineWorkspace } from '../../shared'; import type { AffineWorkspace } from '../../shared';
import { RemWorkspaceFlavour } from '../../shared';
import { apis } from '../../shared/apis'; import { apis } from '../../shared/apis';
import { createEmptyBlockSuiteWorkspace } from '../../utils';
type Query = (typeof QueryKey)[keyof typeof QueryKey]; type Query = (typeof QueryKey)[keyof typeof QueryKey];
@@ -73,7 +73,7 @@ export const fetcher = async (
); );
const remWorkspace: AffineWorkspace = { const remWorkspace: AffineWorkspace = {
...workspace, ...workspace,
flavour: RemWorkspaceFlavour.AFFINE, flavour: WorkspaceFlavour.AFFINE,
blockSuiteWorkspace, blockSuiteWorkspace,
providers: [...createAffineProviders(blockSuiteWorkspace)], providers: [...createAffineProviders(blockSuiteWorkspace)],
}; };
+7 -10
View File
@@ -1,3 +1,5 @@
import { LoadPriority, WorkspaceFlavour } from '@affine/workspace/type';
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
import { createJSONStorage } from 'jotai/utils'; import { createJSONStorage } from 'jotai/utils';
import React from 'react'; import React from 'react';
import { mutate } from 'swr'; 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 { BlockSuitePageList } from '../../components/blocksuite/block-suite-page-list';
import { PageDetailEditor } from '../../components/page-detail-editor'; import { PageDetailEditor } from '../../components/page-detail-editor';
import type { AffineWorkspace } from '../../shared'; import type { AffineWorkspace } from '../../shared';
import { import { BlockSuiteWorkspace } from '../../shared';
BlockSuiteWorkspace,
LoadPriority,
RemWorkspaceFlavour,
} from '../../shared';
import { apis, clientAuth } from '../../shared/apis'; import { apis, clientAuth } from '../../shared/apis';
import { createEmptyBlockSuiteWorkspace } from '../../utils';
import { initPage } from '../../utils/blocksuite'; import { initPage } from '../../utils/blocksuite';
import type { WorkspacePlugin } from '..'; import type { WorkspacePlugin } from '..';
import { QueryKey } from './fetcher'; import { QueryKey } from './fetcher';
@@ -46,7 +43,7 @@ const getPersistenceAllWorkspace = () => {
); );
const affineWorkspace: AffineWorkspace = { const affineWorkspace: AffineWorkspace = {
...item, ...item,
flavour: RemWorkspaceFlavour.AFFINE, flavour: WorkspaceFlavour.AFFINE,
blockSuiteWorkspace, blockSuiteWorkspace,
providers: [...createAffineProviders(blockSuiteWorkspace)], providers: [...createAffineProviders(blockSuiteWorkspace)],
}; };
@@ -57,8 +54,8 @@ const getPersistenceAllWorkspace = () => {
return allWorkspaces; return allWorkspaces;
}; };
export const AffinePlugin: WorkspacePlugin<RemWorkspaceFlavour.AFFINE> = { export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
flavour: RemWorkspaceFlavour.AFFINE, flavour: WorkspaceFlavour.AFFINE,
loadPriority: LoadPriority.HIGH, loadPriority: LoadPriority.HIGH,
cleanup: () => { cleanup: () => {
storage.removeItem(kAffineLocal); storage.removeItem(kAffineLocal);
@@ -167,7 +164,7 @@ export const AffinePlugin: WorkspacePlugin<RemWorkspaceFlavour.AFFINE> = {
const affineWorkspace: AffineWorkspace = { const affineWorkspace: AffineWorkspace = {
...workspace, ...workspace,
flavour: RemWorkspaceFlavour.AFFINE, flavour: WorkspaceFlavour.AFFINE,
blockSuiteWorkspace, blockSuiteWorkspace,
providers: [...createAffineProviders(blockSuiteWorkspace)], providers: [...createAffineProviders(blockSuiteWorkspace)],
}; };
+18 -57
View File
@@ -1,74 +1,35 @@
import type React from 'react';
import type { import type {
BlockSuiteWorkspace,
FlavourToWorkspace,
LoadPriority, LoadPriority,
SettingPanel, WorkspaceCRUD,
} from '../shared'; WorkspaceUISchema,
import { RemWorkspaceFlavour } from '../shared'; } from '@affine/workspace/type';
import { WorkspaceFlavour } from '@affine/workspace/type';
import type { AffineWorkspace, LocalWorkspace } from '../shared';
import { AffinePlugin } from './affine'; import { AffinePlugin } from './affine';
import { LocalPlugin } from './local'; import { LocalPlugin } from './local';
type UIBaseProps<Flavour extends RemWorkspaceFlavour> = { declare module '@affine/workspace/type' {
currentWorkspace: FlavourToWorkspace[Flavour]; interface WorkspaceRegistry {
}; [WorkspaceFlavour.AFFINE]: AffineWorkspace;
[WorkspaceFlavour.LOCAL]: LocalWorkspace;
}
}
type SettingProps<Flavour extends RemWorkspaceFlavour> = export interface WorkspacePlugin<Flavour extends WorkspaceFlavour> {
UIBaseProps<Flavour> & {
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<Flavour extends RemWorkspaceFlavour> =
UIBaseProps<Flavour> & {
currentPageId: string;
};
type PageListProps<Flavour extends RemWorkspaceFlavour> = {
blockSuiteWorkspace: BlockSuiteWorkspace;
onOpenPage: (pageId: string, newTab?: boolean) => void;
};
type SideBarMenuProps<Flavour extends RemWorkspaceFlavour> =
UIBaseProps<Flavour> & {
setSideBarOpen: (open: boolean) => void;
};
export interface WorkspacePlugin<Flavour extends RemWorkspaceFlavour> {
flavour: Flavour; flavour: Flavour;
// Plugin will be loaded according to the priority // Plugin will be loaded according to the priority
loadPriority: LoadPriority; loadPriority: LoadPriority;
// fixme: this is a hack // fixme: this is a hack
cleanup?: () => void; cleanup?: () => void;
// Fetch necessary data for the first render // Fetch necessary data for the first render
CRUD: { CRUD: WorkspaceCRUD<Flavour>;
create: (blockSuiteWorkspace: BlockSuiteWorkspace) => Promise<string>; UI: WorkspaceUISchema<Flavour>;
delete: (workspace: FlavourToWorkspace[Flavour]) => Promise<void>;
get: (workspaceId: string) => Promise<FlavourToWorkspace[Flavour] | null>;
// not supported yet
// update: (workspace: FlavourToWorkspace[Flavour]) => Promise<void>;
list: () => Promise<FlavourToWorkspace[Flavour][]>;
};
UI: {
PageDetail: React.FC<PageDetailProps<Flavour>>;
PageList: React.FC<PageListProps<Flavour>>;
SettingsDetail: React.FC<SettingProps<Flavour>>;
};
} }
export const WorkspacePlugins = { export const WorkspacePlugins = {
[RemWorkspaceFlavour.AFFINE]: AffinePlugin, [WorkspaceFlavour.AFFINE]: AffinePlugin,
[RemWorkspaceFlavour.LOCAL]: LocalPlugin, [WorkspaceFlavour.LOCAL]: LocalPlugin,
} satisfies { } satisfies {
[Key in RemWorkspaceFlavour]: WorkspacePlugin<Key>; [Key in WorkspaceFlavour]: WorkspacePlugin<Key>;
}; };
+6 -9
View File
@@ -1,4 +1,6 @@
import { DEFAULT_WORKSPACE_NAME } from '@affine/env'; 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 { nanoid } from '@blocksuite/store';
import { createJSONStorage } from 'jotai/utils'; import { createJSONStorage } from 'jotai/utils';
import React from 'react'; 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 { BlockSuitePageList } from '../../components/blocksuite/block-suite-page-list';
import { PageDetailEditor } from '../../components/page-detail-editor'; import { PageDetailEditor } from '../../components/page-detail-editor';
import type { LocalWorkspace } from '../../shared'; import type { LocalWorkspace } from '../../shared';
import { import { BlockSuiteWorkspace } from '../../shared';
BlockSuiteWorkspace,
LoadPriority,
RemWorkspaceFlavour,
} from '../../shared';
import { createEmptyBlockSuiteWorkspace } from '../../utils';
import { initPage } from '../../utils/blocksuite'; import { initPage } from '../../utils/blocksuite';
import type { WorkspacePlugin } from '..'; import type { WorkspacePlugin } from '..';
@@ -25,8 +22,8 @@ const getStorage = () => createJSONStorage(() => localStorage);
export const kStoreKey = 'affine-local-workspace'; export const kStoreKey = 'affine-local-workspace';
const schema = z.array(z.string()); const schema = z.array(z.string());
export const LocalPlugin: WorkspacePlugin<RemWorkspaceFlavour.LOCAL> = { export const LocalPlugin: WorkspacePlugin<WorkspaceFlavour.LOCAL> = {
flavour: RemWorkspaceFlavour.LOCAL, flavour: WorkspaceFlavour.LOCAL,
loadPriority: LoadPriority.LOW, loadPriority: LoadPriority.LOW,
CRUD: { CRUD: {
get: async workspaceId => { get: async workspaceId => {
@@ -44,7 +41,7 @@ export const LocalPlugin: WorkspacePlugin<RemWorkspaceFlavour.LOCAL> = {
); );
const workspace: LocalWorkspace = { const workspace: LocalWorkspace = {
id, id,
flavour: RemWorkspaceFlavour.LOCAL, flavour: WorkspaceFlavour.LOCAL,
blockSuiteWorkspace: blockSuiteWorkspace, blockSuiteWorkspace: blockSuiteWorkspace,
providers: [...createLocalProviders(blockSuiteWorkspace)], providers: [...createLocalProviders(blockSuiteWorkspace)],
}; };
+1 -1
View File
@@ -12,7 +12,7 @@ let prefixUrl = '/';
if (typeof window === 'undefined') { if (typeof window === 'undefined') {
// SSR // SSR
const serverAPI = config.serverAPI; const serverAPI = config.serverAPI;
if (isValidIPAddress(serverAPI)) { if (isValidIPAddress(serverAPI.split(':')[0])) {
// This is for Server side rendering support // This is for Server side rendering support
prefixUrl = new URL('http://' + config.serverAPI + '/').origin; prefixUrl = new URL('http://' + config.serverAPI + '/').origin;
} else { } else {
+3 -28
View File
@@ -1,4 +1,5 @@
import type { Workspace as RemoteWorkspace } from '@affine/datacenter'; import type { Workspace as RemoteWorkspace } from '@affine/datacenter';
import type { WorkspaceFlavour } from '@affine/workspace/type';
import { Workspace as BlockSuiteWorkspace } from '@blocksuite/store'; import { Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import type { ReactElement, ReactNode } from 'react'; 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 { export interface AffineWorkspace extends RemoteWorkspace {
flavour: RemWorkspaceFlavour.AFFINE; flavour: WorkspaceFlavour.AFFINE;
// empty // empty
blockSuiteWorkspace: BlockSuiteWorkspace; blockSuiteWorkspace: BlockSuiteWorkspace;
providers: Provider[]; providers: Provider[];
} }
export interface LocalWorkspace { export interface LocalWorkspace {
flavour: RemWorkspaceFlavour.LOCAL; flavour: WorkspaceFlavour.LOCAL;
id: string; id: string;
blockSuiteWorkspace: BlockSuiteWorkspace; blockSuiteWorkspace: BlockSuiteWorkspace;
providers: Provider[]; providers: Provider[];
@@ -89,16 +80,6 @@ export const enum WorkspaceSubPath {
TRASH = 'trash', 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 = { export const WorkspaceSubPathName = {
[WorkspaceSubPath.ALL]: 'All Pages', [WorkspaceSubPath.ALL]: 'All Pages',
[WorkspaceSubPath.FAVORITE]: 'Favorites', [WorkspaceSubPath.FAVORITE]: 'Favorites',
@@ -125,9 +106,3 @@ export const publicPathGenerator = {
} satisfies { } satisfies {
[Path in WorkspaceSubPath]: (workspaceId: string) => string; [Path in WorkspaceSubPath]: (workspaceId: string) => string;
}; };
export const enum LoadPriority {
HIGH = 1,
MEDIUM = 2,
LOW = 3,
}
-26
View File
@@ -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) { export function stringToColour(str: string) {
str = str || 'affine'; str = str || 'affine';
let colour = '#'; let colour = '#';
@@ -23,24 +18,3 @@ export function stringToColour(str: string) {
return colour; return colour;
} }
const hashMap = new Map<string, BlockSuiteWorkspace>();
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;
};
+1
View File
@@ -57,6 +57,7 @@
"happy-dom": "^8.9.0", "happy-dom": "^8.9.0",
"husky": "^8.0.3", "husky": "^8.0.3",
"lint-staged": "^13.2.0", "lint-staged": "^13.2.0",
"msw": "^1.2.0",
"nanoid": "^4.0.1", "nanoid": "^4.0.1",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"prettier": "^2.8.5", "prettier": "^2.8.5",
+5
View File
@@ -2,12 +2,17 @@
"name": "@affine/env", "name": "@affine/env",
"private": true, "private": true,
"main": "./src/index.ts", "main": "./src/index.ts",
"module": "./src/index.ts",
"devDependencies": { "devDependencies": {
"next": "=13.2.3", "next": "=13.2.3",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"zod": "^3.21.4" "zod": "^3.21.4"
}, },
"exports": {
".": "./src/index.ts",
"./constant": "./src/constant.ts"
},
"dependencies": { "dependencies": {
"@blocksuite/global": "0.5.0-20230323085636-3110abb", "@blocksuite/global": "0.5.0-20230323085636-3110abb",
"lit": "^2.6.1" "lit": "^2.6.1"
+66
View File
@@ -1,2 +1,68 @@
export const DEFAULT_WORKSPACE_NAME = 'Demo Workspace'; export const DEFAULT_WORKSPACE_NAME = 'Demo Workspace';
export const UNTITLED_WORKSPACE_NAME = 'Untitled'; 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;
+22
View File
@@ -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"
}
}
@@ -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);
});
});
@@ -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<void>(resolve => {
setTimeout(() => {
expect(isExpired({ exp: now + 1 } as AccessTokenMessage)).toBeTruthy();
resolve();
}, 2000);
});
expect(isExpired({ exp: now - 1 } as AccessTokenMessage)).toBeTruthy();
await promise;
});
});
+4
View File
@@ -0,0 +1,4 @@
import type { AccessTokenMessage } from '@affine/workspace/affine/login';
import { atom } from 'jotai';
export const currentAffineUserAtom = atom<AccessTokenMessage | null>(null);
+175
View File
@@ -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<LoginResponse | null> => {
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<LoginResponse>;
} 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<LoginResponse | null> => {
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<LoginResponse>;
},
} as const;
}
+74
View File
@@ -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<Flavour extends keyof WorkspaceRegistry> {
create: (blockSuiteWorkspace: BlockSuiteWorkspace) => Promise<string>;
delete: (workspace: WorkspaceRegistry[Flavour]) => Promise<void>;
get: (workspaceId: string) => Promise<WorkspaceRegistry[Flavour] | null>;
// not supported yet
// update: (workspace: FlavourToWorkspace[Flavour]) => Promise<void>;
list: () => Promise<WorkspaceRegistry[Flavour][]>;
}
type UIBaseProps<Flavour extends keyof WorkspaceRegistry> = {
currentWorkspace: WorkspaceRegistry[Flavour];
};
type SettingProps<Flavour extends keyof WorkspaceRegistry> =
UIBaseProps<Flavour> & {
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<Flavour extends keyof WorkspaceRegistry> =
UIBaseProps<Flavour> & {
currentPageId: string;
};
type PageListProps<Flavour extends keyof WorkspaceRegistry> = {
blockSuiteWorkspace: BlockSuiteWorkspace;
onOpenPage: (pageId: string, newTab?: boolean) => void;
};
type SideBarMenuProps<Flavour extends keyof WorkspaceRegistry> =
UIBaseProps<Flavour> & {
setSideBarOpen: (open: boolean) => void;
};
export interface WorkspaceUISchema<Flavour extends keyof WorkspaceRegistry> {
PageDetail: FC<PageDetailProps<Flavour>>;
PageList: FC<PageListProps<Flavour>>;
SettingsDetail: FC<SettingProps<Flavour>>;
}
+24
View File
@@ -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<string, Workspace>();
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;
};
+4
View File
@@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["./src"]
}
+4 -2
View File
@@ -26,7 +26,9 @@
"@affine/i18n": ["./packages/i18n/src"], "@affine/i18n": ["./packages/i18n/src"],
"@affine/debug": ["./packages/debug"], "@affine/debug": ["./packages/debug"],
"@affine/env": ["./packages/env"], "@affine/env": ["./packages/env"],
"@affine/utils": ["./packages/utils"] "@affine/env/*": ["./packages/env/src/*"],
"@affine/utils": ["./packages/utils"],
"@affine/workspace/*": ["./packages/workspace/src/*"]
} }
}, },
"references": [ "references": [
@@ -55,7 +57,7 @@
"path": "./packages/debug" "path": "./packages/debug"
}, },
{ {
"path": "./packages/cli" "path": "./packages/workspace"
} }
], ],
"files": [], "files": [],
+308 -9
View File
@@ -22,6 +22,7 @@ __metadata:
"@affine/env": "workspace:*" "@affine/env": "workspace:*"
"@affine/i18n": "workspace:*" "@affine/i18n": "workspace:*"
"@affine/templates": "workspace:*" "@affine/templates": "workspace:*"
"@affine/workspace": "workspace:*"
"@blocksuite/blocks": 0.5.0-20230323085636-3110abb "@blocksuite/blocks": 0.5.0-20230323085636-3110abb
"@blocksuite/editor": 0.5.0-20230323085636-3110abb "@blocksuite/editor": 0.5.0-20230323085636-3110abb
"@blocksuite/icons": 2.0.23 "@blocksuite/icons": 2.0.23
@@ -251,6 +252,24 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft 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": "@ampproject/remapping@npm:^2.2.0":
version: 2.2.0 version: 2.2.0
resolution: "@ampproject/remapping@npm:2.2.0" resolution: "@ampproject/remapping@npm:2.2.0"
@@ -3856,6 +3875,32 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "@mui/base@npm:5.0.0-alpha.121":
version: 5.0.0-alpha.121 version: 5.0.0-alpha.121
resolution: "@mui/base@npm:5.0.0-alpha.121" resolution: "@mui/base@npm:5.0.0-alpha.121"
@@ -4193,6 +4238,13 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "@perfsee/bundle-analyzer@npm:1.4.0":
version: 1.4.0 version: 1.4.0
resolution: "@perfsee/bundle-analyzer@npm:1.4.0" resolution: "@perfsee/bundle-analyzer@npm:1.4.0"
@@ -6475,6 +6527,13 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "@types/debug@npm:^4.1.7":
version: 4.1.7 version: 4.1.7
resolution: "@types/debug@npm:4.1.7" resolution: "@types/debug@npm:4.1.7"
@@ -6680,6 +6739,13 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "@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 version: 7.0.11
resolution: "@types/json-schema@npm:7.0.11" resolution: "@types/json-schema@npm:7.0.11"
@@ -6949,6 +7015,15 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "@types/stack-utils@npm:^2.0.0":
version: 2.0.1 version: 2.0.1
resolution: "@types/stack-utils@npm:2.0.1" resolution: "@types/stack-utils@npm:2.0.1"
@@ -7395,6 +7470,13 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "@xtuc/ieee754@npm:^1.2.0":
version: 1.2.0 version: 1.2.0
resolution: "@xtuc/ieee754@npm:1.2.0" resolution: "@xtuc/ieee754@npm:1.2.0"
@@ -7420,6 +7502,13 @@ __metadata:
languageName: node languageName: node
linkType: hard 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:.": "AFFiNE@workspace:.":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "AFFiNE@workspace:." resolution: "AFFiNE@workspace:."
@@ -7447,6 +7536,7 @@ __metadata:
happy-dom: ^8.9.0 happy-dom: ^8.9.0
husky: ^8.0.3 husky: ^8.0.3
lint-staged: ^13.2.0 lint-staged: ^13.2.0
msw: ^1.2.0
nanoid: ^4.0.1 nanoid: ^4.0.1
nyc: ^15.1.0 nyc: ^15.1.0
prettier: ^2.8.5 prettier: ^2.8.5
@@ -8665,6 +8755,16 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "chalk@npm:5.2.0, chalk@npm:^5.2.0":
version: 5.2.0 version: 5.2.0
resolution: "chalk@npm:5.2.0" resolution: "chalk@npm:5.2.0"
@@ -8693,7 +8793,7 @@ __metadata:
languageName: node languageName: node
linkType: hard 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 version: 4.1.2
resolution: "chalk@npm:4.1.2" resolution: "chalk@npm:4.1.2"
dependencies: dependencies:
@@ -8717,6 +8817,13 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "check-error@npm:^1.0.2":
version: 1.0.2 version: 1.0.2
resolution: "check-error@npm:1.0.2" resolution: "check-error@npm:1.0.2"
@@ -8724,7 +8831,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"chokidar@npm:^3.5.3": "chokidar@npm:^3.4.2, chokidar@npm:^3.5.3":
version: 3.5.3 version: 3.5.3
resolution: "chokidar@npm:3.5.3" resolution: "chokidar@npm:3.5.3"
dependencies: dependencies:
@@ -8861,6 +8968,13 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "client-only@npm:0.0.1":
version: 0.0.1 version: 0.0.1
resolution: "client-only@npm:0.0.1" resolution: "client-only@npm:0.0.1"
@@ -9219,6 +9333,13 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "core-js-compat@npm:^3.25.1":
version: 3.29.1 version: 3.29.1
resolution: "core-js-compat@npm:3.29.1" resolution: "core-js-compat@npm:3.29.1"
@@ -11028,7 +11149,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"events@npm:^3.2.0": "events@npm:^3.2.0, events@npm:^3.3.0":
version: 3.3.0 version: 3.3.0
resolution: "events@npm:3.3.0" resolution: "events@npm:3.3.0"
checksum: f6f487ad2198aa41d878fa31452f1a3c00958f46e9019286ff4787c84aac329332ab45c9cdc8c445928fc6d7ded294b9e005a7fce9426488518017831b272780 checksum: f6f487ad2198aa41d878fa31452f1a3c00958f46e9019286ff4787c84aac329332ab45c9cdc8c445928fc6d7ded294b9e005a7fce9426488518017831b272780
@@ -11197,6 +11318,17 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "extract-zip@npm:^1.6.6":
version: 1.7.0 version: 1.7.0
resolution: "extract-zip@npm:1.7.0" resolution: "extract-zip@npm:1.7.0"
@@ -11331,6 +11463,15 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "file-entry-cache@npm:^6.0.1":
version: 6.0.1 version: 6.0.1
resolution: "file-entry-cache@npm:6.0.1" resolution: "file-entry-cache@npm:6.0.1"
@@ -12392,7 +12533,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"graphql@npm:^16.0.0": "graphql@npm:^15.0.0 || ^16.0.0, graphql@npm:^16.0.0":
version: 16.6.0 version: 16.6.0
resolution: "graphql@npm:16.6.0" resolution: "graphql@npm:16.6.0"
checksum: bf1d9e3c1938ce3c1a81e909bd3ead1ae4707c577f91cff1ca2eca474bfbc7873d5d7b942e1e9777ff5a8304421dba57a4b76d7a29eb19de8711cb70e3c2415e checksum: bf1d9e3c1938ce3c1a81e909bd3ead1ae4707c577f91cff1ca2eca474bfbc7873d5d7b942e1e9777ff5a8304421dba57a4b76d7a29eb19de8711cb70e3c2415e
@@ -12536,6 +12677,13 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "hoist-non-react-statics@npm:^3.3.1, hoist-non-react-statics@npm:^3.3.2":
version: 3.3.2 version: 3.3.2
resolution: "hoist-non-react-statics@npm:3.3.2" resolution: "hoist-non-react-statics@npm:3.3.2"
@@ -12751,7 +12899,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"iconv-lite@npm:0.4.24": "iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.24":
version: 0.4.24 version: 0.4.24
resolution: "iconv-lite@npm:0.4.24" resolution: "iconv-lite@npm:0.4.24"
dependencies: dependencies:
@@ -12889,6 +13037,29 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "internal-slot@npm:^1.0.3, internal-slot@npm:^1.0.4, internal-slot@npm:^1.0.5":
version: 1.0.5 version: 1.0.5
resolution: "internal-slot@npm:1.0.5" resolution: "internal-slot@npm:1.0.5"
@@ -13159,6 +13330,13 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "is-number-object@npm:^1.0.4":
version: 1.0.7 version: 1.0.7
resolution: "is-number-object@npm:1.0.7" resolution: "is-number-object@npm:1.0.7"
@@ -14239,6 +14417,13 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "js-sdsl@npm:^4.1.4":
version: 4.3.0 version: 4.3.0
resolution: "js-sdsl@npm:4.3.0" resolution: "js-sdsl@npm:4.3.0"
@@ -15469,6 +15654,40 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "multipipe@npm:^1.0.2":
version: 1.0.2 version: 1.0.2
resolution: "multipipe@npm:1.0.2" resolution: "multipipe@npm:1.0.2"
@@ -15490,6 +15709,13 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "mz@npm:^2.7.0":
version: 2.7.0 version: 2.7.0
resolution: "mz@npm:2.7.0" resolution: "mz@npm:2.7.0"
@@ -16150,7 +16376,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"ora@npm:^5.1.0": "ora@npm:^5.1.0, ora@npm:^5.4.1":
version: 5.4.1 version: 5.4.1
resolution: "ora@npm:5.4.1" resolution: "ora@npm:5.4.1"
dependencies: dependencies:
@@ -16174,6 +16400,20 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "p-cancelable@npm:^2.0.0":
version: 2.1.1 version: 2.1.1
resolution: "p-cancelable@npm:2.1.1" resolution: "p-cancelable@npm:2.1.1"
@@ -16477,6 +16717,13 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "path-type@npm:^2.0.0":
version: 2.0.0 version: 2.0.0
resolution: "path-type@npm:2.0.0" resolution: "path-type@npm:2.0.0"
@@ -17848,6 +18095,13 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "run-parallel@npm:^1.1.9":
version: 1.2.0 version: 1.2.0
resolution: "run-parallel@npm:1.2.0" resolution: "run-parallel@npm:1.2.0"
@@ -17866,7 +18120,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"rxjs@npm:^7.8.0": "rxjs@npm:^7.5.5, rxjs@npm:^7.8.0":
version: 7.8.0 version: 7.8.0
resolution: "rxjs@npm:7.8.0" resolution: "rxjs@npm:7.8.0"
dependencies: dependencies:
@@ -18057,6 +18311,13 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "setprototypeof@npm:1.2.0":
version: 1.2.0 version: 1.2.0
resolution: "setprototypeof@npm:1.2.0" resolution: "setprototypeof@npm:1.2.0"
@@ -18541,6 +18802,22 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "strict-uri-encode@npm:^2.0.0":
version: 2.0.0 version: 2.0.0
resolution: "strict-uri-encode@npm:2.0.0" resolution: "strict-uri-encode@npm:2.0.0"
@@ -19101,7 +19378,7 @@ __metadata:
languageName: node languageName: node
linkType: hard 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 version: 2.3.8
resolution: "through@npm:2.3.8" resolution: "through@npm:2.3.8"
checksum: a38c3e059853c494af95d50c072b83f8b676a9ba2818dcc5b108ef252230735c54e0185437618596c790bbba8fcdaef5b290405981ffa09dce67b1f1bf190cbd checksum: a38c3e059853c494af95d50c072b83f8b676a9ba2818dcc5b108ef252230735c54e0185437618596c790bbba8fcdaef5b290405981ffa09dce67b1f1bf190cbd
@@ -19165,6 +19442,15 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "tmp@npm:^0.2.0":
version: 0.2.1 version: 0.2.1
resolution: "tmp@npm:0.2.1" resolution: "tmp@npm:0.2.1"
@@ -19817,7 +20103,7 @@ __metadata:
languageName: node languageName: node
linkType: hard 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 version: 0.12.5
resolution: "util@npm:0.12.5" resolution: "util@npm:0.12.5"
dependencies: dependencies:
@@ -20090,6 +20376,19 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "web-streams-polyfill@npm:^3.0.3":
version: 3.2.1 version: 3.2.1
resolution: "web-streams-polyfill@npm:3.2.1" resolution: "web-streams-polyfill@npm:3.2.1"