diff --git a/apps/electron/layers/main/src/handlers.ts b/apps/electron/layers/main/src/handlers.ts index 53f0165b78..cac9b32d3d 100644 --- a/apps/electron/layers/main/src/handlers.ts +++ b/apps/electron/layers/main/src/handlers.ts @@ -1,10 +1,19 @@ +import type { + DBHandlerManager, + DebugHandlerManager, + DialogHandlerManager, + ExportHandlerManager, + UIHandlerManager, + UpdaterHandlerManager, + WorkspaceHandlerManager, +} from '@toeverything/infra'; +import type { HandlerManager, PrimitiveHandlers } from '@toeverything/infra'; import { ipcMain } from 'electron'; import { dbHandlers } from './db'; import { dialogHandlers } from './dialog'; import { exportHandlers } from './export'; import { getLogFilePath, logger, revealLogFile } from './logger'; -import type { NamespaceHandlers } from './type'; import { uiHandlers } from './ui'; import { updaterHandlers } from './updater'; import { workspaceHandlers } from './workspace'; @@ -18,6 +27,26 @@ export const debugHandlers = { }, }; +type UnwrapManagerHandler< + Manager extends HandlerManager> +> = { + [K in keyof Manager['handlers']]: Manager['handlers'][K] extends ( + ...args: infer Args + ) => Promise + ? (event: Electron.IpcMainInvokeEvent, ...args: Args) => Promise + : never; +}; + +type AllHandlers = { + db: UnwrapManagerHandler; + debug: UnwrapManagerHandler; + dialog: UnwrapManagerHandler; + export: UnwrapManagerHandler; + ui: UnwrapManagerHandler; + updater: UnwrapManagerHandler; + workspace: UnwrapManagerHandler; +}; + // Note: all of these handlers will be the single-source-of-truth for the apis exposed to the renderer process export const allHandlers = { db: dbHandlers, @@ -27,7 +56,7 @@ export const allHandlers = { export: exportHandlers, updater: updaterHandlers, workspace: workspaceHandlers, -} satisfies Record; +} satisfies AllHandlers; export const registerHandlers = () => { // TODO: listen to namespace instead of individual event types diff --git a/apps/electron/package.json b/apps/electron/package.json index 76f842f097..1354c5846f 100644 --- a/apps/electron/package.json +++ b/apps/electron/package.json @@ -35,6 +35,7 @@ "@electron-forge/maker-zip": "^6.1.1", "@electron-forge/shared-types": "^6.1.1", "@electron/remote": "2.0.9", + "@toeverything/infra": "workspace:*", "@types/fs-extra": "^11.0.1", "@types/uuid": "^9.0.1", "cross-env": "7.0.3", diff --git a/apps/electron/scripts/common.mjs b/apps/electron/scripts/common.mjs index 18e0dde292..ef71914392 100644 --- a/apps/electron/scripts/common.mjs +++ b/apps/electron/scripts/common.mjs @@ -46,7 +46,7 @@ export const config = () => { bundle: true, target: `node${NODE_MAJOR_VERSION}`, platform: 'node', - external: ['electron', 'yjs', 'electron-updater'], + external: ['electron', 'yjs', 'electron-updater', '@toeverything/infra'], define: define, format: 'cjs', loader: { diff --git a/apps/electron/tsconfig.json b/apps/electron/tsconfig.json index 9253fde0ff..160499adb2 100644 --- a/apps/electron/tsconfig.json +++ b/apps/electron/tsconfig.json @@ -26,6 +26,9 @@ { "path": "../../packages/env" }, + { + "path": "../../packages/infra" + }, { "path": "../../tests/kit" } ], "ts-node": { diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs index 03870475c3..ac07dc959d 100644 --- a/apps/web/next.config.mjs +++ b/apps/web/next.config.mjs @@ -112,6 +112,7 @@ const nextConfig = { '@affine/copilot', '@toeverything/hooks', '@toeverything/y-indexeddb', + '@toeverything/infra', '@toeverything/plugin-infra', ], publicRuntimeConfig: { diff --git a/apps/web/package.json b/apps/web/package.json index 7ce3d91e0a..afb22db3ef 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -35,6 +35,7 @@ "@react-hookz/web": "^23.0.1", "@sentry/nextjs": "^7.54.0", "@toeverything/hooks": "workspace:*", + "@toeverything/infra": "workspace:*", "@toeverything/plugin-infra": "workspace:*", "cmdk": "^0.2.0", "css-spring": "^4.1.0", diff --git a/apps/web/src/atoms/index.ts b/apps/web/src/atoms/index.ts index 84a5798fd3..1aa19d2f52 100644 --- a/apps/web/src/atoms/index.ts +++ b/apps/web/src/atoms/index.ts @@ -50,10 +50,8 @@ rootWorkspacesMetadataAtom.onMount = setAtom => { }, 0); if (environment.isDesktop) { - // @ts-expect-error window.apis?.workspace.list().then(workspaceIDs => { if (abortController.signal.aborted) return; - // @ts-expect-error const newMetadata = workspaceIDs.map(w => ({ id: w[0], flavour: WorkspaceFlavour.LOCAL, @@ -61,7 +59,6 @@ rootWorkspacesMetadataAtom.onMount = setAtom => { setAtom(metadata => { return [ ...metadata, - // @ts-expect-error ...newMetadata.filter(m => !metadata.find(m2 => m2.id === m.id)), ]; }); diff --git a/apps/web/src/components/affine/create-workspace-modal/index.tsx b/apps/web/src/components/affine/create-workspace-modal/index.tsx index 9f428a41b2..1550adb0ca 100644 --- a/apps/web/src/components/affine/create-workspace-modal/index.tsx +++ b/apps/web/src/components/affine/create-workspace-modal/index.tsx @@ -122,7 +122,6 @@ const useDefaultDBLocation = () => { const [defaultDBLocation, setDefaultDBLocation] = useState(''); useEffect(() => { - // @ts-expect-error window.apis?.db.getDefaultStorageLocation().then(dir => { setDefaultDBLocation(dir); }); diff --git a/apps/web/src/components/affine/workspace-setting-detail/panel/general/index.tsx b/apps/web/src/components/affine/workspace-setting-detail/panel/general/index.tsx index 2db576a187..423622f37c 100644 --- a/apps/web/src/components/affine/workspace-setting-detail/panel/general/index.tsx +++ b/apps/web/src/components/affine/workspace-setting-detail/panel/general/index.tsx @@ -27,7 +27,6 @@ const useShowOpenDBFile = (workspaceId: string) => { const [show, setShow] = useState(false); useEffect(() => { if (window.apis && window.events && environment.isDesktop) { - // @ts-expect-error window.apis.workspace.getMeta(workspaceId).then(meta => { setShow(!!meta.secondaryDBPath); }); diff --git a/packages/env/package.json b/packages/env/package.json index e45a7c863d..2dfdec8582 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -4,7 +4,6 @@ "main": "./src/index.ts", "module": "./src/index.ts", "devDependencies": { - "@affine/templates": "workspace:*", "@blocksuite/global": "0.0.0-20230606130340-805f430b-nightly", "next": "=13.4.2", "react": "18.3.0-canary-16d053d59-20230506", @@ -21,7 +20,9 @@ "./blocksuite": "./src/blocksuite/index.ts" }, "peerDependencies": { - "@blocksuite/global": "0.0.0-20230409084303-221991d4-nightly" + "@affine/templates": "workspace:*", + "@blocksuite/global": "0.0.0-20230409084303-221991d4-nightly", + "@toeverything/infra": "workspace:*" }, "dependencies": { "lit": "^2.7.5" diff --git a/packages/env/src/config.ts b/packages/env/src/config.ts index 35b5d3b762..8f0aeea679 100644 --- a/packages/env/src/config.ts +++ b/packages/env/src/config.ts @@ -1,16 +1,45 @@ /// import { assertEquals } from '@blocksuite/global/utils'; +import type { + DBHandlerManager, + DebugHandlerManager, + DialogHandlerManager, + ExportHandlerManager, + HandlerManager, + PrimitiveHandlers, + UIHandlerManager, + UpdaterHandlerManager, + WorkspaceHandlerManager, +} from '@toeverything/infra'; import getConfig from 'next/config'; import { z } from 'zod'; import { UaHelper } from './ua-helper'; +type UnwrapManagerHandler< + Manager extends HandlerManager> +> = { + [K in keyof Manager['handlers']]: Manager['handlers'][K] extends ( + ...args: infer Args + ) => Promise + ? (...args: Args) => Promise + : never; +}; + declare global { interface Window { appInfo: { electron: boolean; }; - apis: any; + apis: { + db: UnwrapManagerHandler; + debug: UnwrapManagerHandler; + dialog: UnwrapManagerHandler; + export: UnwrapManagerHandler; + ui: UnwrapManagerHandler; + updater: UnwrapManagerHandler; + workspace: UnwrapManagerHandler; + }; events: any; } } diff --git a/packages/env/tsconfig.json b/packages/env/tsconfig.json index 8034f93ce4..d11c8e5e35 100644 --- a/packages/env/tsconfig.json +++ b/packages/env/tsconfig.json @@ -5,5 +5,10 @@ "composite": true, "noEmit": false, "outDir": "lib" - } + }, + "references": [ + { + "path": "../infra" + } + ] } diff --git a/packages/infra/package.json b/packages/infra/package.json new file mode 100644 index 0000000000..7fdd7b85ee --- /dev/null +++ b/packages/infra/package.json @@ -0,0 +1,31 @@ +{ + "name": "@toeverything/infra", + "main": "./src/index.ts", + "module": "./src/index.ts", + "exports": { + ".": "./src/index.ts" + }, + "publishConfig": { + "module": "./dist/index.mjs", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + } + }, + "files": [ + "dist" + ] + }, + "scripts": { + "build": "vite build", + "dev": "vite build --watch" + }, + "devDependencies": { + "vite": "^4.3.9", + "vite-plugin-dts": "^2.3.0" + } +} diff --git a/packages/infra/src/handler.ts b/packages/infra/src/handler.ts new file mode 100644 index 0000000000..fd1fd35daa --- /dev/null +++ b/packages/infra/src/handler.ts @@ -0,0 +1,105 @@ +export interface WorkspaceMeta { + id: string; + mainDBPath: string; + secondaryDBPath?: string; // assume there will be only one +} + +export type PrimitiveHandlers = (...args: any[]) => Promise; +type TODO = any; + +export abstract class HandlerManager< + Namespace extends string, + Handlers extends Record +> { + abstract readonly app: TODO; + abstract readonly namespace: Namespace; + abstract readonly handlers: Handlers; +} + +type DBHandlers = { + getDocAsUpdates: (id: string) => Promise; + applyDocUpdate: (id: string, update: Uint8Array) => Promise; + addBlob: ( + workspaceId: string, + key: string, + data: Uint8Array + ) => Promise; + getBlob: (workspaceId: string, key: string) => Promise; + deleteBlob: (workspaceId: string, key: string) => Promise; + getBlobKeys: (workspaceId: string) => Promise; + getDefaultStorageLocation: () => Promise; +}; + +export abstract class DBHandlerManager extends HandlerManager< + 'db', + DBHandlers +> {} + +type DebugHandlers = { + revealLogFile: () => Promise; + logFilePath: () => Promise; +}; + +export abstract class DebugHandlerManager extends HandlerManager< + 'debug', + DebugHandlers +> {} + +type DialogHandlers = { + revealDBFile: (workspaceId: string) => Promise; + loadDBFile: () => Promise; + saveDBFileAs: (workspaceId: string) => Promise; + moveDBFile: (workspaceId: string, dbFileLocation?: string) => Promise; + selectDBFileLocation: () => Promise; + setFakeDialogResult: (result: any) => Promise; +}; + +export abstract class DialogHandlerManager extends HandlerManager< + 'dialog', + DialogHandlers +> {} + +type UIHandlers = { + handleThemeChange: (theme: 'system' | 'light' | 'dark') => Promise; + handleSidebarVisibilityChange: (visible: boolean) => Promise; + handleMinimizeApp: () => Promise; + handleMaximizeApp: () => Promise; + handleCloseApp: () => Promise; + getGoogleOauthCode: () => Promise; +}; + +export abstract class UIHandlerManager extends HandlerManager< + 'ui', + UIHandlers +> {} + +type ExportHandlers = { + savePDFFileAs: (title: string) => Promise; +}; + +export abstract class ExportHandlerManager extends HandlerManager< + 'export', + ExportHandlers +> {} + +type UpdaterHandlers = { + currentVersion: () => Promise; + quitAndInstall: () => Promise; + checkForUpdatesAndNotify: () => Promise; +}; + +export abstract class UpdaterHandlerManager extends HandlerManager< + 'updater', + UpdaterHandlers +> {} + +type WorkspaceHandlers = { + list: () => Promise<[workspaceId: string, meta: WorkspaceMeta][]>; + delete: (id: string) => Promise; + getMeta: (id: string) => Promise; +}; + +export abstract class WorkspaceHandlerManager extends HandlerManager< + 'workspace', + WorkspaceHandlers +> {} diff --git a/packages/infra/src/index.ts b/packages/infra/src/index.ts new file mode 100644 index 0000000000..68ae53f6c3 --- /dev/null +++ b/packages/infra/src/index.ts @@ -0,0 +1 @@ +export * from './handler'; diff --git a/packages/infra/tsconfig.json b/packages/infra/tsconfig.json new file mode 100644 index 0000000000..e9197b23ff --- /dev/null +++ b/packages/infra/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.json", + "include": ["./src"], + "compilerOptions": { + "composite": true, + "noEmit": false, + "outDir": "lib" + }, + "references": [ + { + "path": "./tsconfig.node.json" + } + ] +} diff --git a/packages/infra/tsconfig.node.json b/packages/infra/tsconfig.node.json new file mode 100644 index 0000000000..ff5308300e --- /dev/null +++ b/packages/infra/tsconfig.node.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true, + "outDir": "lib", + "noEmit": false + }, + "include": ["vite.config.ts"] +} diff --git a/packages/infra/vite.config.ts b/packages/infra/vite.config.ts new file mode 100644 index 0000000000..25fde726d7 --- /dev/null +++ b/packages/infra/vite.config.ts @@ -0,0 +1,24 @@ +import { resolve } from 'node:path'; + +import { fileURLToPath } from 'url'; +import dts from 'vite-plugin-dts'; +import { defineConfig } from 'vitest/config'; + +const root = fileURLToPath(new URL('.', import.meta.url)); + +export default defineConfig({ + build: { + lib: { + entry: { + index: resolve(root, 'src/index.ts'), + }, + formats: ['es', 'cjs'], + name: 'AffineInfra', + }, + }, + plugins: [ + dts({ + insertTypesEntry: true, + }), + ], +}); diff --git a/packages/workspace/src/local/crud.ts b/packages/workspace/src/local/crud.ts index e0e6d4620d..35e1fe99ee 100644 --- a/packages/workspace/src/local/crud.ts +++ b/packages/workspace/src/local/crud.ts @@ -102,11 +102,9 @@ export const CRUD: WorkspaceCRUD = { // workspaces in desktop if (window.apis && environment.isDesktop) { - // @ts-expect-error const desktopIds = (await window.apis.workspace.list()).map(([id]) => id); // the ids maybe a subset of the local storage const moreWorkspaces = desktopIds.filter( - // @ts-expect-error id => !allWorkspaceIDs.includes(id) ); allWorkspaceIDs = [...allWorkspaceIDs, ...moreWorkspaces]; diff --git a/scripts/publish.sh b/scripts/publish.sh index a4a2deedca..4a032cfe9e 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -2,6 +2,7 @@ packages=( "y-indexeddb" + "infra" ) for package in "${packages[@]}"; do diff --git a/tsconfig.json b/tsconfig.json index 13944954ee..006828f096 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,7 +28,7 @@ "@affine/i18n/hooks": ["./packages/i18n/src/i18n-generated"], "@affine/debug": ["./packages/debug"], "@affine/jotai": ["./packages/jotai"], - "@affine/env": ["./packages/env"], + "@affine/env": ["./packages/env/src"], "@affine/env/*": ["./packages/env/src/*"], "@affine/utils": ["./packages/utils"], "@affine/workspace/*": ["./packages/workspace/src/*"], @@ -41,6 +41,7 @@ ], "@affine-test/kit/*": ["./tests/kit/*"], "@affine-test/fixtures/*": ["./tests/fixtures/*"], + "@toeverything/infra": ["./packages/infra/src"], "@toeverything/y-indexeddb": ["./packages/y-indexeddb/src"], "@toeverything/hooks/*": ["./packages/hooks/src/*"], "@toeverything/plugin-infra": ["./packages/plugin-infra/src"], @@ -63,6 +64,9 @@ { "path": "./apps/server" }, + { + "path": "./packages/infra" + }, { "path": "./packages/component" }, diff --git a/yarn.lock b/yarn.lock index 3f3eb7c6b1..9586f4662f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -158,6 +158,7 @@ __metadata: "@electron-forge/maker-zip": ^6.1.1 "@electron-forge/shared-types": ^6.1.1 "@electron/remote": 2.0.9 + "@toeverything/infra": "workspace:*" "@types/fs-extra": ^11.0.1 "@types/uuid": ^9.0.1 cheerio: ^1.0.0-rc.12 @@ -188,7 +189,6 @@ __metadata: version: 0.0.0-use.local resolution: "@affine/env@workspace:packages/env" dependencies: - "@affine/templates": "workspace:*" "@blocksuite/global": 0.0.0-20230606130340-805f430b-nightly lit: ^2.7.5 next: =13.4.2 @@ -196,7 +196,9 @@ __metadata: react-dom: 18.3.0-canary-16d053d59-20230506 zod: ^3.21.4 peerDependencies: + "@affine/templates": "workspace:*" "@blocksuite/global": 0.0.0-20230409084303-221991d4-nightly + "@toeverything/infra": "workspace:*" languageName: unknown linkType: soft @@ -389,6 +391,7 @@ __metadata: "@swc-jotai/debug-label": ^0.0.10 "@swc-jotai/react-refresh": ^0.0.8 "@toeverything/hooks": "workspace:*" + "@toeverything/infra": "workspace:*" "@toeverything/plugin-infra": "workspace:*" "@types/react": ^18.2.6 "@types/react-dom": ^18.2.4 @@ -9204,6 +9207,15 @@ __metadata: languageName: unknown linkType: soft +"@toeverything/infra@workspace:*, @toeverything/infra@workspace:packages/infra": + version: 0.0.0-use.local + resolution: "@toeverything/infra@workspace:packages/infra" + dependencies: + vite: ^4.3.9 + vite-plugin-dts: ^2.3.0 + languageName: unknown + linkType: soft + "@toeverything/plugin-infra@workspace:*, @toeverything/plugin-infra@workspace:packages/plugin-infra": version: 0.0.0-use.local resolution: "@toeverything/plugin-infra@workspace:packages/plugin-infra"