From 466b1bb1737e41715e0b9d22ac976791cdbbc430 Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Wed, 5 Jul 2023 00:43:30 +0800 Subject: [PATCH] feat(electron): move preload to infra (#3011) --- .github/actions/setup-node/action.yml | 4 + .github/workflows/build.yml | 6 - .github/workflows/nightly-build.yml | 3 - .github/workflows/release-desktop-app.yml | 3 - .husky/pre-commit | 3 + apps/electron/src/helper/index.ts | 3 +- apps/electron/src/helper/main-rpc.ts | 8 +- apps/electron/src/main/helper-process.ts | 10 +- apps/electron/src/preload/bootstrap.ts | 6 +- apps/electron/src/types.d.ts | 35 ---- packages/infra/package.json | 52 ++++-- packages/infra/preload/electron.d.ts | 3 + packages/infra/preload/electron.js | 3 + packages/infra/src/core/event-emitter.ts | 74 ++++++++ packages/infra/src/handler.ts | 116 ++----------- packages/infra/src/index.ts | 1 + .../infra/src/preload/electron.ts | 46 +++-- packages/infra/src/type.ts | 162 ++++++++++++++++++ packages/infra/vite.config.ts | 6 + yarn.lock | 16 ++ 20 files changed, 372 insertions(+), 188 deletions(-) delete mode 100644 apps/electron/src/types.d.ts create mode 100644 packages/infra/preload/electron.d.ts create mode 100644 packages/infra/preload/electron.js create mode 100644 packages/infra/src/core/event-emitter.ts rename apps/electron/src/preload/affine-apis.ts => packages/infra/src/preload/electron.ts (81%) create mode 100644 packages/infra/src/type.ts diff --git a/.github/actions/setup-node/action.yml b/.github/actions/setup-node/action.yml index dab9d80df9..b52a5fb073 100644 --- a/.github/actions/setup-node/action.yml +++ b/.github/actions/setup-node/action.yml @@ -121,3 +121,7 @@ runs: run: node apps/electron/node_modules/electron/install.js env: ELECTRON_OVERRIDE_DIST_PATH: ./node_modules/.cache/electron + + - name: Build Infra + shell: bash + run: yarn run build:infra diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3318eaeb4c..96fe81f57d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -352,9 +352,6 @@ jobs: env: NATIVE_TEST: 'true' - - name: Build Infra - run: yarn run build:infra - - name: Build Plugins run: yarn run build:plugins @@ -412,9 +409,6 @@ jobs: with: electron-install: false - - name: Build Infra - run: yarn run build:infra - - name: Unit Test run: yarn nx test:coverage @affine/monorepo diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index d834e3f867..1d9323a110 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -123,9 +123,6 @@ jobs: name: before-make-web-static path: apps/electron/resources/web-static - - name: Build Infra - run: yarn run build:infra - - name: Build Plugins run: yarn run build:plugins diff --git a/.github/workflows/release-desktop-app.yml b/.github/workflows/release-desktop-app.yml index de1a050bbb..866a0ee800 100644 --- a/.github/workflows/release-desktop-app.yml +++ b/.github/workflows/release-desktop-app.yml @@ -123,9 +123,6 @@ jobs: name: before-make-web-static path: apps/electron/resources/web-static - - name: Build Infra - run: yarn run build:infra - - name: Build Plugins run: yarn run build:plugins diff --git a/.husky/pre-commit b/.husky/pre-commit index 4b4fd81acc..1707175e87 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -4,6 +4,9 @@ # check lockfile is up to date yarn install --mode=update-lockfile +# build infra code +yarn -T run build:infra + # lint staged files yarn exec lint-staged diff --git a/apps/electron/src/helper/index.ts b/apps/electron/src/helper/index.ts index cfeef501e3..9af19b2a46 100644 --- a/apps/electron/src/helper/index.ts +++ b/apps/electron/src/helper/index.ts @@ -1,3 +1,4 @@ +import type { RendererToHelper } from '@toeverything/infra/preload/electron'; import { AsyncCall } from 'async-call-rpc'; import { events, handlers } from './exposed'; @@ -30,7 +31,7 @@ function setupRendererConnection(rendererPort: Electron.MessagePortMain) { }); } ); - const rpc = AsyncCall( + const rpc = AsyncCall( Object.fromEntries(flattenedHandlers), { channel: { diff --git a/apps/electron/src/helper/main-rpc.ts b/apps/electron/src/helper/main-rpc.ts index aa93c5da63..3ffde2aea5 100644 --- a/apps/electron/src/helper/main-rpc.ts +++ b/apps/electron/src/helper/main-rpc.ts @@ -1,12 +1,16 @@ +import type { + HelperToMain, + MainToHelper, +} from '@toeverything/infra/preload/electron'; import { AsyncCall } from 'async-call-rpc'; import { getExposedMeta } from './exposed'; -const helperToMainServer: PeersAPIs.HelperToMain = { +const helperToMainServer: HelperToMain = { getMeta: () => getExposedMeta(), }; -export const mainRPC = AsyncCall(helperToMainServer, { +export const mainRPC = AsyncCall(helperToMainServer, { strict: { unknownMessage: false, }, diff --git a/apps/electron/src/main/helper-process.ts b/apps/electron/src/main/helper-process.ts index 66bef0c73b..8cfeb13412 100644 --- a/apps/electron/src/main/helper-process.ts +++ b/apps/electron/src/main/helper-process.ts @@ -1,5 +1,9 @@ import path from 'node:path'; +import type { + HelperToMain, + MainToHelper, +} from '@toeverything/infra/preload/electron'; import { type _AsyncVersionOf, AsyncCall } from 'async-call-rpc'; import { app, @@ -36,7 +40,7 @@ class HelperProcessManager { #process: UtilityProcess; // a rpc server for the main process -> helper process - rpc?: _AsyncVersionOf; + rpc?: _AsyncVersionOf; static instance = new HelperProcessManager(); @@ -86,13 +90,13 @@ class HelperProcessManager { ]); const appMethods = pickAndBind(app, ['getPath']); - const mainToHelperServer: PeersAPIs.MainToHelper = { + const mainToHelperServer: MainToHelper = { ...dialogMethods, ...shellMethods, ...appMethods, }; - this.rpc = AsyncCall(mainToHelperServer, { + this.rpc = AsyncCall(mainToHelperServer, { strict: { // the channel is shared for other purposes as well so that we do not want to // restrict to only JSONRPC messages diff --git a/apps/electron/src/preload/bootstrap.ts b/apps/electron/src/preload/bootstrap.ts index 98536f94ef..83a5b3e0fc 100644 --- a/apps/electron/src/preload/bootstrap.ts +++ b/apps/electron/src/preload/bootstrap.ts @@ -1,8 +1,10 @@ import { contextBridge, ipcRenderer } from 'electron'; (async () => { - const { appInfo, getAffineAPIs } = await import('./affine-apis'); - const { apis, events } = getAffineAPIs(); + const { appInfo, getElectronAPIs } = await import( + '@toeverything/infra/preload/electron' + ); + const { apis, events } = getElectronAPIs(); contextBridge.exposeInMainWorld('appInfo', appInfo); contextBridge.exposeInMainWorld('apis', apis); diff --git a/apps/electron/src/types.d.ts b/apps/electron/src/types.d.ts deleted file mode 100644 index cbb08e321e..0000000000 --- a/apps/electron/src/types.d.ts +++ /dev/null @@ -1,35 +0,0 @@ -declare namespace PeersAPIs { - import type { app, dialog, shell } from 'electron'; - - interface ExposedMeta { - handlers: [string, string[]][]; - events: [string, string[]][]; - } - - // render <-> helper - interface RendererToHelper { - postEvent: (channel: string, ...args: any[]) => void; - } - - interface HelperToRenderer { - [key: string]: (...args: any[]) => Promise; - } - - // helper <-> main - interface HelperToMain { - getMeta: () => ExposedMeta; - } - - type MainToHelper = Pick< - typeof dialog & typeof shell & typeof app, - | 'showOpenDialog' - | 'showSaveDialog' - | 'openExternal' - | 'showItemInFolder' - | 'getPath' - >; - - // render <-> main - // these are handled via IPC - // TODO: fix type -} diff --git a/packages/infra/package.json b/packages/infra/package.json index 62bc99d66c..fa19036c13 100644 --- a/packages/infra/package.json +++ b/packages/infra/package.json @@ -1,32 +1,50 @@ { "name": "@toeverything/infra", - "main": "./src/index.ts", - "module": "./src/index.ts", + "type": "module", + "module": "./dist/index.mjs", + "main": "./dist/index.js", + "types": "./dist/index.d.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" - } + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "require": "./dist/index.cjs" }, - "files": [ - "dist" - ] + "./core/*": { + "types": "./dist/core/*.d.ts", + "import": "./dist/core/*.js", + "require": "./dist/core/*.cjs" + }, + "./preload/*": { + "types": "./dist/preload/*.d.ts", + "import": "./dist/preload/*.js", + "require": "./dist/preload/*.cjs" + } }, + "files": [ + "dist" + ], "scripts": { "build": "vite build", "dev": "vite build --watch" }, "devDependencies": { + "async-call-rpc": "^6.3.1", + "electron": "link:../../apps/electron/node_modules/electron", "vite": "^4.3.9", "vite-plugin-dts": "3.0.2" }, + "peerDependencies": { + "async-call-rpc": "*", + "electron": "*" + }, + "peerDependenciesMeta": { + "async-call-rpc": { + "optional": true + }, + "electron": { + "optional": true + } + }, "version": "0.7.0-beta.0" } diff --git a/packages/infra/preload/electron.d.ts b/packages/infra/preload/electron.d.ts new file mode 100644 index 0000000000..7c9e855cda --- /dev/null +++ b/packages/infra/preload/electron.d.ts @@ -0,0 +1,3 @@ +/* eslint-disable */ +// @ts-ignore +export * from '../dist/preload/electron'; diff --git a/packages/infra/preload/electron.js b/packages/infra/preload/electron.js new file mode 100644 index 0000000000..a83b2722bf --- /dev/null +++ b/packages/infra/preload/electron.js @@ -0,0 +1,3 @@ +/* eslint-disable */ +/// +export * from '../dist/preload/electron.js'; diff --git a/packages/infra/src/core/event-emitter.ts b/packages/infra/src/core/event-emitter.ts new file mode 100644 index 0000000000..2e019f6319 --- /dev/null +++ b/packages/infra/src/core/event-emitter.ts @@ -0,0 +1,74 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 Andy Wermke + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +export type EventMap = { + [key: string]: (...args: any[]) => void; +}; + +/** + * Type-safe event emitter. + * + * Use it like this: + * + * ```typescript + * type MyEvents = { + * error: (error: Error) => void; + * message: (from: string, content: string) => void; + * } + * + * const myEmitter = new EventEmitter() as TypedEmitter; + * + * myEmitter.emit("error", "x") // <- Will catch this type error; + * ``` + * + * Lifecycle: + * invoke -> handle -> emit -> on/once + */ +export interface TypedEventEmitter { + addListener(event: E, listener: Events[E]): this; + on(event: E, listener: Events[E]): this; + once(event: E, listener: Events[E]): this; + + off(event: E, listener: Events[E]): this; + removeAllListeners(event?: E): this; + removeListener(event: E, listener: Events[E]): this; + + emit( + event: E, + ...args: Parameters + ): boolean; + // The sloppy `eventNames()` return type is to mitigate type incompatibilities - see #5 + eventNames(): (keyof Events | string | symbol)[]; + rawListeners(event: E): Events[E][]; + listeners(event: E): Events[E][]; + listenerCount(event: E): number; + + handle(event: E, handler: Events[E]): this; + invoke( + event: E, + ...args: Parameters + ): Promise>; + + getMaxListeners(): number; + setMaxListeners(maxListeners: number): this; +} diff --git a/packages/infra/src/handler.ts b/packages/infra/src/handler.ts index 14717d160f..6590a82b2e 100644 --- a/packages/infra/src/handler.ts +++ b/packages/infra/src/handler.ts @@ -1,145 +1,51 @@ -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: ( - workspaceId: string, - subdocId?: string - ) => Promise; - applyDocUpdate: ( - id: string, - update: Uint8Array, - subdocId?: string - ) => 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; -}; +import type { + ClipboardHandlers, + DBHandlers, + DebugHandlers, + DialogHandlers, + ExportHandlers, + UIHandlers, + UpdaterHandlers, + WorkspaceHandlers, +} from './type'; +import { HandlerManager } from './type'; 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 ClipboardHandlers = { - copyAsImageFromString: (dataURL: string) => Promise; -}; - export abstract class ClipboardHandlerManager extends HandlerManager< 'clipboard', ClipboardHandlers > {} -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 > {} - -export type UnwrapManagerHandlerToServerSide< - ElectronEvent extends { - frameId: number; - processId: number; - }, - Manager extends HandlerManager> -> = { - [K in keyof Manager['handlers']]: Manager['handlers'][K] extends ( - ...args: infer Args - ) => Promise - ? (event: ElectronEvent, ...args: Args) => Promise - : never; -}; - -export type UnwrapManagerHandlerToClientSide< - Manager extends HandlerManager> -> = { - [K in keyof Manager['handlers']]: Manager['handlers'][K] extends ( - ...args: infer Args - ) => Promise - ? (...args: Args) => Promise - : never; -}; diff --git a/packages/infra/src/index.ts b/packages/infra/src/index.ts index 68ae53f6c3..18ebff2c02 100644 --- a/packages/infra/src/index.ts +++ b/packages/infra/src/index.ts @@ -1 +1,2 @@ export * from './handler'; +export * from './type'; diff --git a/apps/electron/src/preload/affine-apis.ts b/packages/infra/src/preload/electron.ts similarity index 81% rename from apps/electron/src/preload/affine-apis.ts rename to packages/infra/src/preload/electron.ts index ddf3a5874a..bd6695d1da 100644 --- a/apps/electron/src/preload/affine-apis.ts +++ b/packages/infra/src/preload/electron.ts @@ -1,14 +1,38 @@ -// NOTE: we will generate preload types from this file +// Please add modules to `external` in `rollupOptions` to avoid wrong bundling. import { AsyncCall, type EventBasedChannel } from 'async-call-rpc'; +import type { app, dialog, shell } from 'electron'; import { ipcRenderer } from 'electron'; import { Subject } from 'rxjs'; -type ExposedMeta = { - handlers: [namespace: string, handlerNames: string[]][]; - events: [namespace: string, eventNames: string[]][]; -}; +export interface ExposedMeta { + handlers: [string, string[]][]; + events: [string, string[]][]; +} -export function getAffineAPIs() { +// render <-> helper +export interface RendererToHelper { + postEvent: (channel: string, ...args: any[]) => void; +} + +export interface HelperToRenderer { + [key: string]: (...args: any[]) => Promise; +} + +// helper <-> main +export interface HelperToMain { + getMeta: () => ExposedMeta; +} + +export type MainToHelper = Pick< + typeof dialog & typeof shell & typeof app, + | 'showOpenDialog' + | 'showSaveDialog' + | 'openExternal' + | 'showItemInFolder' + | 'getPath' +>; + +export function getElectronAPIs() { const mainAPIs = getMainAPIs(); const helperAPIs = getHelperAPIs(); @@ -126,13 +150,13 @@ function getHelperAPIs() { return val ? JSON.parse(val) : null; })(); - const rendererToHelperServer: PeersAPIs.RendererToHelper = { + const rendererToHelperServer: RendererToHelper = { postEvent: (channel, ...args) => { events$.next({ channel, args }); }, }; - const rpc = AsyncCall(rendererToHelperServer, { + const rpc = AsyncCall(rendererToHelperServer, { channel: helperPort$.then(helperPort => createMessagePortChannel(helperPort) ), @@ -157,10 +181,10 @@ function getHelperAPIs() { }; const setup = (meta: ExposedMeta) => { - const { handlers: handlersMeta, events: eventsMeta } = meta; + const { handlers, events } = meta; const helperHandlers = Object.fromEntries( - handlersMeta.map(([namespace, functionNames]) => { + handlers.map(([namespace, functionNames]) => { return [ namespace, Object.fromEntries( @@ -173,7 +197,7 @@ function getHelperAPIs() { ); const helperEvents = Object.fromEntries( - eventsMeta.map(([namespace, eventNames]) => { + events.map(([namespace, eventNames]) => { return [ namespace, Object.fromEntries( diff --git a/packages/infra/src/type.ts b/packages/infra/src/type.ts new file mode 100644 index 0000000000..60c4d9b181 --- /dev/null +++ b/packages/infra/src/type.ts @@ -0,0 +1,162 @@ +import type { TypedEventEmitter } from './core/event-emitter'; + +export abstract class HandlerManager< + Namespace extends string, + Handlers extends Record +> { + static instance: HandlerManager>; + private _app: App; + private _namespace: Namespace; + private _handlers: Handlers; + + constructor() { + throw new Error('Method not implemented.'); + } + + private _initialized = false; + + registerHandlers(handlers: Handlers) { + if (this._initialized) { + throw new Error('Already initialized'); + } + this._handlers = handlers; + for (const [name, handler] of Object.entries(this._handlers)) { + this._app.handle(`${this._namespace}:${name}`, (async (...args: any[]) => + handler(...args)) as any); + } + this._initialized = true; + } + + invokeHandler( + name: K, + ...args: Parameters + ): Promise> { + return this._handlers[name](...args); + } + + static getInstance(): HandlerManager< + string, + Record + > { + throw new Error('Method not implemented.'); + } +} + +export interface WorkspaceMeta { + id: string; + mainDBPath: string; + secondaryDBPath?: string; // assume there will be only one +} + +export type PrimitiveHandlers = (...args: any[]) => Promise; + +export type DBHandlers = { + getDocAsUpdates: ( + workspaceId: string, + subdocId?: string + ) => Promise; + applyDocUpdate: ( + id: string, + update: Uint8Array, + subdocId?: string + ) => 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 type DebugHandlers = { + revealLogFile: () => Promise; + logFilePath: () => Promise; +}; + +export 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 type UIHandlers = { + handleThemeChange: (theme: 'system' | 'light' | 'dark') => Promise; + handleSidebarVisibilityChange: (visible: boolean) => Promise; + handleMinimizeApp: () => Promise; + handleMaximizeApp: () => Promise; + handleCloseApp: () => Promise; + getGoogleOauthCode: () => Promise; +}; + +export type ClipboardHandlers = { + copyAsImageFromString: (dataURL: string) => Promise; +}; + +export type ExportHandlers = { + savePDFFileAs: (title: string) => Promise; +}; + +export type UpdaterHandlers = { + currentVersion: () => Promise; + quitAndInstall: () => Promise; + checkForUpdatesAndNotify: () => Promise; +}; + +export type WorkspaceHandlers = { + list: () => Promise<[workspaceId: string, meta: WorkspaceMeta][]>; + delete: (id: string) => Promise; + getMeta: (id: string) => Promise; +}; + +export type EventMap = DBHandlers & + DebugHandlers & + DialogHandlers & + UIHandlers & + ClipboardHandlers & + ExportHandlers & + UpdaterHandlers & + WorkspaceHandlers; + +export type UnwrapManagerHandlerToServerSide< + ElectronEvent extends { + frameId: number; + processId: number; + }, + Manager extends HandlerManager> +> = Manager extends HandlerManager + ? { + [K in keyof Handlers]: Handlers[K] extends ( + ...args: infer Args + ) => Promise + ? (event: ElectronEvent, ...args: Args) => Promise + : never; + } + : never; + +export type UnwrapManagerHandlerToClientSide< + Manager extends HandlerManager> +> = Manager extends HandlerManager + ? { + [K in keyof Handlers]: Handlers[K] extends ( + ...args: infer Args + ) => Promise + ? (...args: Args) => Promise + : never; + } + : never; + +/** + * @internal + */ +export type App< + Namespace extends string, + Handlers extends Record +> = TypedEventEmitter<{ + [K in keyof Handlers as `${Namespace}:${K & string}`]: Handlers[K]; +}>; diff --git a/packages/infra/vite.config.ts b/packages/infra/vite.config.ts index 25fde726d7..a44252c580 100644 --- a/packages/infra/vite.config.ts +++ b/packages/infra/vite.config.ts @@ -8,13 +8,19 @@ const root = fileURLToPath(new URL('.', import.meta.url)); export default defineConfig({ build: { + minify: false, lib: { entry: { index: resolve(root, 'src/index.ts'), + 'core/event-emitter': resolve(root, 'src/core/event-emitter.ts'), + 'preload/electron': resolve(root, 'src/preload/electron.ts'), }, formats: ['es', 'cjs'], name: 'AffineInfra', }, + rollupOptions: { + external: ['electron', 'async-call-rpc', 'rxjs'], + }, }, plugins: [ dts({ diff --git a/yarn.lock b/yarn.lock index a88128fd6f..aff801959b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11436,8 +11436,18 @@ __metadata: version: 0.0.0-use.local resolution: "@toeverything/infra@workspace:packages/infra" dependencies: + async-call-rpc: ^6.3.1 + electron: "link:../../apps/electron/node_modules/electron" vite: ^4.3.9 vite-plugin-dts: 3.0.2 + peerDependencies: + async-call-rpc: "*" + electron: "*" + peerDependenciesMeta: + async-call-rpc: + optional: true + electron: + optional: true languageName: unknown linkType: soft @@ -17052,6 +17062,12 @@ __metadata: languageName: node linkType: hard +"electron@link:../../apps/electron/node_modules/electron::locator=%40toeverything%2Finfra%40workspace%3Apackages%2Finfra": + version: 0.0.0-use.local + resolution: "electron@link:../../apps/electron/node_modules/electron::locator=%40toeverything%2Finfra%40workspace%3Apackages%2Finfra" + languageName: node + linkType: soft + "electron@npm:^25.2.0": version: 25.2.0 resolution: "electron@npm:25.2.0"