refactor(workspace): split workspace interface and implementation (#5463)

@affine/workspace -> (@affine/workspace, @affine/workspace-impl)
This commit is contained in:
EYHN
2024-01-02 10:58:01 +00:00
parent 9d0b3b4947
commit 104c21d84c
77 changed files with 325 additions and 163 deletions
+24
View File
@@ -0,0 +1,24 @@
{
"name": "@affine/workspace",
"private": true,
"main": "./src/index.ts",
"exports": {
".": "./src/index.ts"
},
"peerDependencies": {
"@blocksuite/blocks": "*",
"@blocksuite/global": "*",
"@blocksuite/store": "*"
},
"dependencies": {
"@affine/debug": "workspace:*",
"@affine/env": "workspace:*",
"@toeverything/infra": "workspace:*",
"lodash-es": "^4.17.21",
"yjs": "^13.6.10"
},
"devDependencies": {
"vitest": "1.1.0"
},
"version": "0.11.0"
}
+7
View File
@@ -0,0 +1,7 @@
export * from './engine';
export * from './factory';
export * from './global-schema';
export * from './list';
export * from './manager';
export * from './metadata';
export * from './workspace';
@@ -7,7 +7,6 @@ import { applyUpdate, encodeStateAsUpdate } from 'yjs';
import type { BlobStorage } from '.';
import type { WorkspaceFactory } from './factory';
import { LOCAL_WORKSPACE_LOCAL_STORAGE_KEY } from './impl/local/consts';
import type { WorkspaceList } from './list';
import type { WorkspaceMetadata } from './metadata';
import { WorkspacePool } from './pool';
@@ -151,21 +150,6 @@ export class WorkspaceManager {
return factory.getWorkspaceBlob(metadata.id, blobKey);
}
/**
* a hack for directly add local workspace to workspace list
* Used after copying sqlite database file to appdata folder
*/
_addLocalWorkspace(id: string) {
const allWorkspaceIDs: string[] = JSON.parse(
localStorage.getItem(LOCAL_WORKSPACE_LOCAL_STORAGE_KEY) ?? '[]'
);
allWorkspaceIDs.push(id);
localStorage.setItem(
LOCAL_WORKSPACE_LOCAL_STORAGE_KEY,
JSON.stringify(allWorkspaceIDs)
);
}
private open(metadata: WorkspaceMetadata) {
logger.info(`open workspace [${metadata.flavour}] ${metadata.id} `);
const factory = this.factories.find(x => x.name === metadata.flavour);
+1 -1
View File
@@ -14,7 +14,7 @@
{
"path": "../../frontend/electron-api"
},
{ "path": "../../frontend/workspace" },
{ "path": "../../common/workspace" },
{
"path": "../../common/debug"
},
+1
View File
@@ -26,6 +26,7 @@
"@affine/i18n": "workspace:*",
"@affine/templates": "workspace:*",
"@affine/workspace": "workspace:*",
"@affine/workspace-impl": "workspace:*",
"@blocksuite/block-std": "0.11.0-nightly-202312220916-e3abcbb",
"@blocksuite/blocks": "0.11.0-nightly-202312220916-e3abcbb",
"@blocksuite/global": "0.11.0-nightly-202312220916-e3abcbb",
@@ -1,7 +1,7 @@
import { DebugLogger } from '@affine/debug';
import { DEFAULT_WORKSPACE_NAME } from '@affine/env/constant';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { workspaceManager } from '@affine/workspace';
import { workspaceManager } from '@affine/workspace-impl';
import { getCurrentStore } from '@toeverything/infra/atom';
import {
buildShowcaseWorkspace,
@@ -10,6 +10,7 @@ import { DebugLogger } from '@affine/debug';
import { apis } from '@affine/electron-api';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { _addLocalWorkspace } from '@affine/workspace-impl';
import { getCurrentStore } from '@toeverything/infra/atom';
import {
buildShowcaseWorkspace,
@@ -119,7 +120,7 @@ export const CreateWorkspaceModal = ({
setStep(undefined);
const result = await apis.dialog.loadDBFile();
if (result.workspaceId && !canceled) {
workspaceManager._addLocalWorkspace(result.workspaceId);
_addLocalWorkspace(result.workspaceId);
onCreate(result.workspaceId);
} else if (result.error || result.canceled) {
if (result.error) {
@@ -7,10 +7,8 @@ import {
listHistoryQuery,
recoverDocMutation,
} from '@affine/graphql';
import {
createAffineCloudBlobStorage,
globalBlockSuiteSchema,
} from '@affine/workspace';
import { globalBlockSuiteSchema } from '@affine/workspace';
import { createAffineCloudBlobStorage } from '@affine/workspace-impl';
import { assertEquals } from '@blocksuite/global/utils';
import { Workspace } from '@blocksuite/store';
import { revertUpdate } from '@toeverything/y-indexeddb';
@@ -1,9 +1,6 @@
import { DebugLogger } from '@affine/debug';
import {
type Workspace,
workspaceManager,
type WorkspaceMetadata,
} from '@affine/workspace';
import type { Workspace, WorkspaceMetadata } from '@affine/workspace';
import { workspaceManager } from '@affine/workspace-impl';
import { atom } from 'jotai';
import { atomWithObservable } from 'jotai/utils';
import { Observable } from 'rxjs';
@@ -1,11 +1,11 @@
import { MainContainer } from '@affine/component/workspace';
import { DebugLogger } from '@affine/debug';
import { fetchWithTraceReport } from '@affine/graphql';
import { globalBlockSuiteSchema } from '@affine/workspace';
import {
createAffineCloudBlobStorage,
createStaticBlobStorage,
globalBlockSuiteSchema,
} from '@affine/workspace';
} from '@affine/workspace-impl';
import { assertExists } from '@blocksuite/global/utils';
import { type Page, Workspace } from '@blocksuite/store';
import { noop } from 'foxact/noop';
@@ -2,7 +2,7 @@ import { pushNotificationAtom } from '@affine/component/notification-center';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { affine } from '@affine/electron-api';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CLOUD_WORKSPACE_CHANGED_BROADCAST_CHANNEL_KEY } from '@affine/workspace';
import { CLOUD_WORKSPACE_CHANGED_BROADCAST_CHANNEL_KEY } from '@affine/workspace-impl';
import { useAtom, useSetAtom } from 'jotai';
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
import { SessionProvider, useSession } from 'next-auth/react';
@@ -4,7 +4,7 @@ import {
TRACE_ID_BYTES,
traceReporter,
} from '@affine/graphql';
import { CLOUD_WORKSPACE_CHANGED_BROADCAST_CHANNEL_KEY } from '@affine/workspace';
import { CLOUD_WORKSPACE_CHANGED_BROADCAST_CHANNEL_KEY } from '@affine/workspace-impl';
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
import { signIn, signOut } from 'next-auth/react';
+4 -1
View File
@@ -18,7 +18,10 @@
"path": "../../frontend/i18n"
},
{
"path": "../../frontend/workspace"
"path": "../../common/workspace"
},
{
"path": "../../frontend/workspace-impl"
},
{
"path": "../../frontend/electron-api"
+6 -4
View File
@@ -27,10 +27,12 @@ type ClientHandler = {
} & HelperHandlers;
type ClientEvents = MainEvents & HelperEvents;
export const appInfo = (window as any).appInfo as typeof exposedAppInfo | null;
export const apis = (window as any).apis as ClientHandler | null;
export const events = (window as any).events as ClientEvents | null;
export const affine = (window as any).affine as
export const appInfo = (globalThis as any).appInfo as
| typeof exposedAppInfo
| null;
export const apis = (globalThis as any).apis as ClientHandler | null;
export const events = (globalThis as any).events as ClientEvents | null;
export const affine = (globalThis as any).affine as
| typeof exposedAffineGlobal
| null;
@@ -0,0 +1 @@
lib
@@ -1,10 +1,9 @@
{
"name": "@affine/workspace",
"name": "@affine/workspace-impl",
"private": true,
"main": "./src/index.ts",
"exports": {
".": "./src/index.ts",
"./atom": "./src/atom.ts",
"./affine/*": "./src/affine/*.ts"
".": "./src/index.ts"
},
"peerDependencies": {
"@blocksuite/blocks": "*",
@@ -12,37 +11,23 @@
"@blocksuite/store": "*"
},
"dependencies": {
"@affine-test/fixtures": "workspace:*",
"@affine/debug": "workspace:*",
"@affine/electron-api": "workspace:*",
"@affine/env": "workspace:*",
"@affine/graphql": "workspace:*",
"@affine/workspace": "workspace:*",
"@toeverything/infra": "workspace:*",
"async-call-rpc": "^6.3.1",
"idb": "^8.0.0",
"idb-keyval": "^6.2.1",
"is-svg": "^5.0.0",
"jotai": "^2.5.1",
"js-base64": "^3.7.5",
"ky": "^1.0.1",
"lib0": "^0.2.87",
"lodash-es": "^4.17.21",
"nanoid": "^5.0.3",
"next-auth": "^4.24.5",
"react": "18.2.0",
"react-dom": "18.2.0",
"rxjs": "^7.8.1",
"socket.io-client": "^4.7.2",
"swr": "2.2.4",
"valtio": "^1.11.2",
"y-protocols": "^1.0.6",
"y-provider": "workspace:*",
"yjs": "^13.6.10",
"zod": "^3.22.4"
"yjs": "^13.6.10"
},
"devDependencies": {
"@testing-library/react": "^14.0.0",
"@types/ws": "^8.5.7",
"fake-indexeddb": "^5.0.0",
"vitest": "1.1.1",
"ws": "^8.14.2"
@@ -1,4 +1,5 @@
import { DebugLogger } from '@affine/debug';
import type { AwarenessProvider } from '@affine/workspace';
import {
applyAwarenessUpdate,
type Awareness,
@@ -6,9 +7,8 @@ import {
removeAwarenessStates,
} from 'y-protocols/awareness';
import type { AwarenessProvider } from '../../engine/awareness';
import { getIoManager } from '../../utils/affine-io';
import { base64ToUint8Array, uint8ArrayToBase64 } from '../../utils/base64';
import { getIoManager } from '../utils/affine-io';
import { base64ToUint8Array, uint8ArrayToBase64 } from '../utils/base64';
const logger = new DebugLogger('affine:awareness:socketio');
@@ -7,9 +7,9 @@ import {
setBlobMutation,
} from '@affine/graphql';
import { fetcher } from '@affine/graphql';
import type { BlobStorage } from '@affine/workspace';
import type { BlobStorage } from '../../engine/blob';
import { bufferToBlob } from '../../utils/buffer-to-blob';
import { bufferToBlob } from '../utils/buffer-to-blob';
export const createAffineCloudBlobStorage = (
workspaceId: string
@@ -5,13 +5,13 @@ import {
getWorkspacesQuery,
} from '@affine/graphql';
import { fetcher } from '@affine/graphql';
import type { WorkspaceListProvider } from '@affine/workspace';
import { globalBlockSuiteSchema } from '@affine/workspace';
import { Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
import { difference } from 'lodash-es';
import { nanoid } from 'nanoid';
import { applyUpdate, encodeStateAsUpdate } from 'yjs';
import { globalBlockSuiteSchema } from '../../global-schema';
import type { WorkspaceListProvider } from '../../list';
import { createLocalBlobStorage } from '../local/blob';
import { createLocalStorage } from '../local/sync';
import { CLOUD_WORKSPACE_CHANGED_BROADCAST_CHANNEL_KEY } from './consts';
@@ -1,9 +1,9 @@
import { DebugLogger } from '@affine/debug';
import { fetchWithTraceReport } from '@affine/graphql';
import type { SyncStorage } from '@affine/workspace';
import type { SyncStorage } from '../../../engine/sync';
import { getIoManager } from '../../../utils/affine-io';
import { base64ToUint8Array, uint8ArrayToBase64 } from '../../../utils/base64';
import { getIoManager } from '../../utils/affine-io';
import { base64ToUint8Array, uint8ArrayToBase64 } from '../../utils/base64';
import { MultipleBatchSyncSender } from './batch-sync-sender';
const logger = new DebugLogger('affine:storage:socketio');
@@ -1,11 +1,11 @@
import { setupEditorFlags } from '@affine/env/global';
import type { WorkspaceFactory } from '@affine/workspace';
import { BlobEngine, SyncEngine, WorkspaceEngine } from '@affine/workspace';
import { globalBlockSuiteSchema } from '@affine/workspace';
import { Workspace } from '@affine/workspace';
import { Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
import { nanoid } from 'nanoid';
import { BlobEngine, SyncEngine, WorkspaceEngine } from '../../engine';
import type { WorkspaceFactory } from '../../factory';
import { globalBlockSuiteSchema } from '../../global-schema';
import { Workspace } from '../../workspace';
import { createBroadcastChannelAwarenessProvider } from '../local/awareness';
import { createLocalBlobStorage } from '../local/blob';
import { createStaticBlobStorage } from '../local/blob-static';
@@ -0,0 +1,41 @@
import { WorkspaceList, WorkspaceManager } from '@affine/workspace';
import {
cloudWorkspaceFactory,
createCloudWorkspaceListProvider,
} from './cloud';
import {
createLocalWorkspaceListProvider,
LOCAL_WORKSPACE_LOCAL_STORAGE_KEY,
localWorkspaceFactory,
} from './local';
const list = new WorkspaceList([
createLocalWorkspaceListProvider(),
createCloudWorkspaceListProvider(),
]);
export const workspaceManager = new WorkspaceManager(list, [
localWorkspaceFactory,
cloudWorkspaceFactory,
]);
(window as any).workspaceManager = workspaceManager;
export * from './cloud';
export * from './local';
/**
* a hack for directly add local workspace to workspace list
* Used after copying sqlite database file to appdata folder
*/
export function _addLocalWorkspace(id: string) {
const allWorkspaceIDs: string[] = JSON.parse(
localStorage.getItem(LOCAL_WORKSPACE_LOCAL_STORAGE_KEY) ?? '[]'
);
allWorkspaceIDs.push(id);
localStorage.setItem(
LOCAL_WORKSPACE_LOCAL_STORAGE_KEY,
JSON.stringify(allWorkspaceIDs)
);
}
@@ -1,12 +1,12 @@
import 'fake-indexeddb/auto';
import { SyncEngine, SyncEngineStep, SyncPeerStep } from '@affine/workspace';
import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models';
import { Schema, Workspace } from '@blocksuite/store';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { Doc } from 'yjs';
import { createIndexedDBStorage } from '../../../impl/local/sync-indexeddb';
import { SyncEngine, SyncEngineStep, SyncPeerStep } from '../';
import { createIndexedDBStorage } from '..';
import { createTestStorage } from './test-storage';
const schema = new Schema();
@@ -1,11 +1,11 @@
import 'fake-indexeddb/auto';
import { SyncPeer, SyncPeerStep } from '@affine/workspace';
import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models';
import { Schema, Workspace } from '@blocksuite/store';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { createIndexedDBStorage } from '../../../impl/local/sync-indexeddb';
import { SyncPeer, SyncPeerStep } from '../';
import { createIndexedDBStorage } from '..';
const schema = new Schema();
@@ -1,4 +1,4 @@
import type { SyncStorage } from '..';
import type { SyncStorage } from '@affine/workspace';
export function createTestStorage(origin: SyncStorage) {
const controler = {
@@ -1,11 +1,10 @@
import type { AwarenessProvider } from '@affine/workspace';
import type { Awareness } from 'y-protocols/awareness.js';
import {
applyAwarenessUpdate,
encodeAwarenessUpdate,
} from 'y-protocols/awareness.js';
import type { AwarenessProvider } from '../../engine/awareness';
type AwarenessChanges = Record<'added' | 'updated' | 'removed', number[]>;
type ChannelMessage =
@@ -1,7 +1,7 @@
import type { BlobStorage } from '@affine/workspace';
import { createStore, del, get, keys, set } from 'idb-keyval';
import type { BlobStorage } from '../../engine/blob';
import { bufferToBlob } from '../../utils/buffer-to-blob';
import { bufferToBlob } from '../utils/buffer-to-blob';
export const createIndexeddbBlobStorage = (
workspaceId: string
@@ -1,8 +1,8 @@
import { apis } from '@affine/electron-api';
import type { BlobStorage } from '@affine/workspace';
import { assertExists } from '@blocksuite/global/utils';
import type { BlobStorage } from '../../engine/blob';
import { bufferToBlob } from '../../utils/buffer-to-blob';
import { bufferToBlob } from '../utils/buffer-to-blob';
export const createSQLiteBlobStorage = (workspaceId: string): BlobStorage => {
assertExists(apis);
@@ -1,4 +1,4 @@
import type { BlobStorage } from '../../engine/blob';
import type { BlobStorage } from '@affine/workspace';
export const predefinedStaticFiles = [
'029uztLz2CzJezK7UUhrbGiWUdZ0J7NVs_qR6RDsvb8=',
@@ -1,12 +1,12 @@
import { apis } from '@affine/electron-api';
import { WorkspaceFlavour } from '@affine/env/workspace';
import type { WorkspaceListProvider } from '@affine/workspace';
import { globalBlockSuiteSchema } from '@affine/workspace';
import { Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
import { difference } from 'lodash-es';
import { nanoid } from 'nanoid';
import { applyUpdate, encodeStateAsUpdate } from 'yjs';
import { globalBlockSuiteSchema } from '../../global-schema';
import type { WorkspaceListProvider } from '../../list';
import { createLocalBlobStorage } from './blob';
import {
LOCAL_WORKSPACE_CREATED_BROADCAST_CHANNEL_KEY,
@@ -1,8 +1,8 @@
import type { SyncStorage } from '@affine/workspace';
import { type DBSchema, type IDBPDatabase, openDB } from 'idb';
import { diffUpdate, encodeStateVectorFromUpdate } from 'yjs';
import type { SyncStorage } from '../../engine/sync';
import { mergeUpdates } from '../../utils/merge-updates';
import { mergeUpdates } from '../utils/merge-updates';
export const dbVersion = 1;
export const DEFAULT_DB_NAME = 'affine-local';
@@ -1,8 +1,7 @@
import { apis } from '@affine/electron-api';
import type { SyncStorage } from '@affine/workspace';
import { encodeStateVectorFromUpdate } from 'yjs';
import type { SyncStorage } from '../../engine/sync';
export function createSQLiteStorage(workspaceId: string): SyncStorage {
if (!apis?.db) {
throw new Error('sqlite datasource is not available');
@@ -1,13 +1,13 @@
import { setupEditorFlags } from '@affine/env/global';
import type { WorkspaceFactory } from '@affine/workspace';
import { WorkspaceEngine } from '@affine/workspace';
import { BlobEngine } from '@affine/workspace';
import { SyncEngine } from '@affine/workspace';
import { globalBlockSuiteSchema } from '@affine/workspace';
import { Workspace } from '@affine/workspace';
import { Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
import { nanoid } from 'nanoid';
import { WorkspaceEngine } from '../../engine';
import { BlobEngine } from '../../engine/blob';
import { SyncEngine } from '../../engine/sync';
import type { WorkspaceFactory } from '../../factory';
import { globalBlockSuiteSchema } from '../../global-schema';
import { Workspace } from '../../workspace';
import { createBroadcastChannelAwarenessProvider } from './awareness';
import { createLocalBlobStorage } from './blob';
import { createStaticBlobStorage } from './blob-static';
@@ -0,0 +1,17 @@
import { applyUpdate, Doc, encodeStateAsUpdate } from 'yjs';
export function mergeUpdates(updates: Uint8Array[]) {
if (updates.length === 0) {
return new Uint8Array();
}
if (updates.length === 1) {
return updates[0];
}
const doc = new Doc();
doc.transact(() => {
updates.forEach(update => {
applyUpdate(doc, update);
});
});
return encodeStateAsUpdate(doc);
}
@@ -0,0 +1,16 @@
{
"extends": "../../../tsconfig.json",
"include": ["./src"],
"compilerOptions": {
"noEmit": false,
"outDir": "lib"
},
"references": [
{ "path": "../../../tests/fixtures" },
{ "path": "../../common/env" },
{ "path": "../../common/debug" },
{ "path": "../../common/infra" },
{ "path": "../../frontend/graphql" },
{ "path": "../../frontend/electron-api" }
]
}
@@ -1,2 +0,0 @@
export * from './cloud';
export * from './local';
-29
View File
@@ -1,29 +0,0 @@
import {
cloudWorkspaceFactory,
createCloudWorkspaceListProvider,
createLocalWorkspaceListProvider,
localWorkspaceFactory,
} from './impl';
import { WorkspaceList } from './list';
import { WorkspaceManager } from './manager';
const list = new WorkspaceList([
createLocalWorkspaceListProvider(),
createCloudWorkspaceListProvider(),
]);
export const workspaceManager = new WorkspaceManager(list, [
localWorkspaceFactory,
cloudWorkspaceFactory,
]);
(window as any).workspaceManager = workspaceManager;
export * from './engine';
export * from './factory';
export * from './global-schema';
export * from './impl';
export * from './list';
export * from './manager';
export * from './metadata';
export * from './workspace';