mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-27 02:42:25 +08:00
feat(electron): move preload to infra (#3011)
This commit is contained in:
@@ -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-canary.33"
|
||||
}
|
||||
|
||||
3
packages/infra/preload/electron.d.ts
vendored
Normal file
3
packages/infra/preload/electron.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/* eslint-disable */
|
||||
// @ts-ignore
|
||||
export * from '../dist/preload/electron';
|
||||
3
packages/infra/preload/electron.js
Normal file
3
packages/infra/preload/electron.js
Normal file
@@ -0,0 +1,3 @@
|
||||
/* eslint-disable */
|
||||
/// <reference types="../dist/preload/electron.d.ts" />
|
||||
export * from '../dist/preload/electron.js';
|
||||
74
packages/infra/src/core/event-emitter.ts
Normal file
74
packages/infra/src/core/event-emitter.ts
Normal file
@@ -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<MyEvents>;
|
||||
*
|
||||
* myEmitter.emit("error", "x") // <- Will catch this type error;
|
||||
* ```
|
||||
*
|
||||
* Lifecycle:
|
||||
* invoke -> handle -> emit -> on/once
|
||||
*/
|
||||
export interface TypedEventEmitter<Events extends EventMap> {
|
||||
addListener<E extends keyof Events>(event: E, listener: Events[E]): this;
|
||||
on<E extends keyof Events>(event: E, listener: Events[E]): this;
|
||||
once<E extends keyof Events>(event: E, listener: Events[E]): this;
|
||||
|
||||
off<E extends keyof Events>(event: E, listener: Events[E]): this;
|
||||
removeAllListeners<E extends keyof Events>(event?: E): this;
|
||||
removeListener<E extends keyof Events>(event: E, listener: Events[E]): this;
|
||||
|
||||
emit<E extends keyof Events>(
|
||||
event: E,
|
||||
...args: Parameters<Events[E]>
|
||||
): boolean;
|
||||
// The sloppy `eventNames()` return type is to mitigate type incompatibilities - see #5
|
||||
eventNames(): (keyof Events | string | symbol)[];
|
||||
rawListeners<E extends keyof Events>(event: E): Events[E][];
|
||||
listeners<E extends keyof Events>(event: E): Events[E][];
|
||||
listenerCount<E extends keyof Events>(event: E): number;
|
||||
|
||||
handle<E extends keyof Events>(event: E, handler: Events[E]): this;
|
||||
invoke<E extends keyof Events>(
|
||||
event: E,
|
||||
...args: Parameters<Events[E]>
|
||||
): Promise<ReturnType<Events[E]>>;
|
||||
|
||||
getMaxListeners(): number;
|
||||
setMaxListeners(maxListeners: number): this;
|
||||
}
|
||||
@@ -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<any>;
|
||||
type TODO = any;
|
||||
|
||||
export abstract class HandlerManager<
|
||||
Namespace extends string,
|
||||
Handlers extends Record<string, PrimitiveHandlers>
|
||||
> {
|
||||
abstract readonly app: TODO;
|
||||
abstract readonly namespace: Namespace;
|
||||
abstract readonly handlers: Handlers;
|
||||
}
|
||||
|
||||
type DBHandlers = {
|
||||
getDocAsUpdates: (
|
||||
workspaceId: string,
|
||||
subdocId?: string
|
||||
) => Promise<Uint8Array>;
|
||||
applyDocUpdate: (
|
||||
id: string,
|
||||
update: Uint8Array,
|
||||
subdocId?: string
|
||||
) => Promise<void>;
|
||||
addBlob: (
|
||||
workspaceId: string,
|
||||
key: string,
|
||||
data: Uint8Array
|
||||
) => Promise<void>;
|
||||
getBlob: (workspaceId: string, key: string) => Promise<any>;
|
||||
deleteBlob: (workspaceId: string, key: string) => Promise<void>;
|
||||
getBlobKeys: (workspaceId: string) => Promise<any>;
|
||||
getDefaultStorageLocation: () => Promise<string>;
|
||||
};
|
||||
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<string>;
|
||||
logFilePath: () => Promise<string>;
|
||||
};
|
||||
|
||||
export abstract class DebugHandlerManager extends HandlerManager<
|
||||
'debug',
|
||||
DebugHandlers
|
||||
> {}
|
||||
|
||||
type DialogHandlers = {
|
||||
revealDBFile: (workspaceId: string) => Promise<any>;
|
||||
loadDBFile: () => Promise<any>;
|
||||
saveDBFileAs: (workspaceId: string) => Promise<any>;
|
||||
moveDBFile: (workspaceId: string, dbFileLocation?: string) => Promise<any>;
|
||||
selectDBFileLocation: () => Promise<any>;
|
||||
setFakeDialogResult: (result: any) => Promise<any>;
|
||||
};
|
||||
|
||||
export abstract class DialogHandlerManager extends HandlerManager<
|
||||
'dialog',
|
||||
DialogHandlers
|
||||
> {}
|
||||
|
||||
type UIHandlers = {
|
||||
handleThemeChange: (theme: 'system' | 'light' | 'dark') => Promise<any>;
|
||||
handleSidebarVisibilityChange: (visible: boolean) => Promise<any>;
|
||||
handleMinimizeApp: () => Promise<any>;
|
||||
handleMaximizeApp: () => Promise<any>;
|
||||
handleCloseApp: () => Promise<any>;
|
||||
getGoogleOauthCode: () => Promise<any>;
|
||||
};
|
||||
|
||||
export abstract class UIHandlerManager extends HandlerManager<
|
||||
'ui',
|
||||
UIHandlers
|
||||
> {}
|
||||
|
||||
type ClipboardHandlers = {
|
||||
copyAsImageFromString: (dataURL: string) => Promise<void>;
|
||||
};
|
||||
|
||||
export abstract class ClipboardHandlerManager extends HandlerManager<
|
||||
'clipboard',
|
||||
ClipboardHandlers
|
||||
> {}
|
||||
|
||||
type ExportHandlers = {
|
||||
savePDFFileAs: (title: string) => Promise<any>;
|
||||
};
|
||||
|
||||
export abstract class ExportHandlerManager extends HandlerManager<
|
||||
'export',
|
||||
ExportHandlers
|
||||
> {}
|
||||
|
||||
type UpdaterHandlers = {
|
||||
currentVersion: () => Promise<any>;
|
||||
quitAndInstall: () => Promise<any>;
|
||||
checkForUpdatesAndNotify: () => Promise<any>;
|
||||
};
|
||||
|
||||
export abstract class UpdaterHandlerManager extends HandlerManager<
|
||||
'updater',
|
||||
UpdaterHandlers
|
||||
> {}
|
||||
|
||||
type WorkspaceHandlers = {
|
||||
list: () => Promise<[workspaceId: string, meta: WorkspaceMeta][]>;
|
||||
delete: (id: string) => Promise<void>;
|
||||
getMeta: (id: string) => Promise<WorkspaceMeta>;
|
||||
};
|
||||
|
||||
export abstract class WorkspaceHandlerManager extends HandlerManager<
|
||||
'workspace',
|
||||
WorkspaceHandlers
|
||||
> {}
|
||||
|
||||
export type UnwrapManagerHandlerToServerSide<
|
||||
ElectronEvent extends {
|
||||
frameId: number;
|
||||
processId: number;
|
||||
},
|
||||
Manager extends HandlerManager<string, Record<string, PrimitiveHandlers>>
|
||||
> = {
|
||||
[K in keyof Manager['handlers']]: Manager['handlers'][K] extends (
|
||||
...args: infer Args
|
||||
) => Promise<infer R>
|
||||
? (event: ElectronEvent, ...args: Args) => Promise<R>
|
||||
: never;
|
||||
};
|
||||
|
||||
export type UnwrapManagerHandlerToClientSide<
|
||||
Manager extends HandlerManager<string, Record<string, PrimitiveHandlers>>
|
||||
> = {
|
||||
[K in keyof Manager['handlers']]: Manager['handlers'][K] extends (
|
||||
...args: infer Args
|
||||
) => Promise<infer R>
|
||||
? (...args: Args) => Promise<R>
|
||||
: never;
|
||||
};
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './handler';
|
||||
export * from './type';
|
||||
|
||||
217
packages/infra/src/preload/electron.ts
Normal file
217
packages/infra/src/preload/electron.ts
Normal file
@@ -0,0 +1,217 @@
|
||||
// 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';
|
||||
|
||||
export interface ExposedMeta {
|
||||
handlers: [string, string[]][];
|
||||
events: [string, string[]][];
|
||||
}
|
||||
|
||||
// render <-> helper
|
||||
export interface RendererToHelper {
|
||||
postEvent: (channel: string, ...args: any[]) => void;
|
||||
}
|
||||
|
||||
export interface HelperToRenderer {
|
||||
[key: string]: (...args: any[]) => Promise<any>;
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
return {
|
||||
apis: {
|
||||
...mainAPIs.apis,
|
||||
...helperAPIs.apis,
|
||||
},
|
||||
events: {
|
||||
...mainAPIs.events,
|
||||
...helperAPIs.events,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const appInfo = {
|
||||
electron: true,
|
||||
};
|
||||
|
||||
function getMainAPIs() {
|
||||
const meta: ExposedMeta = (() => {
|
||||
const val = process.argv
|
||||
.find(arg => arg.startsWith('--main-exposed-meta='))
|
||||
?.split('=')[1];
|
||||
|
||||
return val ? JSON.parse(val) : null;
|
||||
})();
|
||||
|
||||
// main handlers that can be invoked from the renderer process
|
||||
const apis: any = (() => {
|
||||
const { handlers: handlersMeta } = meta;
|
||||
|
||||
const all = handlersMeta.map(([namespace, functionNames]) => {
|
||||
const namespaceApis = functionNames.map(name => {
|
||||
const channel = `${namespace}:${name}`;
|
||||
return [
|
||||
name,
|
||||
(...args: any[]) => {
|
||||
return ipcRenderer.invoke(channel, ...args);
|
||||
},
|
||||
];
|
||||
});
|
||||
return [namespace, Object.fromEntries(namespaceApis)];
|
||||
});
|
||||
|
||||
return Object.fromEntries(all);
|
||||
})();
|
||||
|
||||
// main events that can be listened to from the renderer process
|
||||
const events: any = (() => {
|
||||
const { events: eventsMeta } = meta;
|
||||
|
||||
// NOTE: ui may try to listen to a lot of the same events, so we increase the limit...
|
||||
ipcRenderer.setMaxListeners(100);
|
||||
|
||||
const all = eventsMeta.map(([namespace, eventNames]) => {
|
||||
const namespaceEvents = eventNames.map(name => {
|
||||
const channel = `${namespace}:${name}`;
|
||||
return [
|
||||
name,
|
||||
(callback: (...args: any[]) => void) => {
|
||||
const fn: (
|
||||
event: Electron.IpcRendererEvent,
|
||||
...args: any[]
|
||||
) => void = (_, ...args) => {
|
||||
callback(...args);
|
||||
};
|
||||
ipcRenderer.on(channel, fn);
|
||||
return () => {
|
||||
ipcRenderer.off(channel, fn);
|
||||
};
|
||||
},
|
||||
];
|
||||
});
|
||||
return [namespace, Object.fromEntries(namespaceEvents)];
|
||||
});
|
||||
return Object.fromEntries(all);
|
||||
})();
|
||||
|
||||
return { apis, events };
|
||||
}
|
||||
|
||||
const helperPort$ = new Promise<MessagePort>(resolve =>
|
||||
ipcRenderer.on('helper-connection', async e => {
|
||||
console.info('[preload] helper-connection', e);
|
||||
resolve(e.ports[0]);
|
||||
})
|
||||
);
|
||||
|
||||
const createMessagePortChannel = (port: MessagePort): EventBasedChannel => {
|
||||
return {
|
||||
on(listener) {
|
||||
port.onmessage = e => {
|
||||
listener(e.data);
|
||||
};
|
||||
port.start();
|
||||
return () => {
|
||||
port.onmessage = null;
|
||||
port.close();
|
||||
};
|
||||
},
|
||||
send(data) {
|
||||
port.postMessage(data);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
function getHelperAPIs() {
|
||||
const events$ = new Subject<{ channel: string; args: any[] }>();
|
||||
const meta: ExposedMeta = (() => {
|
||||
const val = process.argv
|
||||
.find(arg => arg.startsWith('--helper-exposed-meta='))
|
||||
?.split('=')[1];
|
||||
|
||||
return val ? JSON.parse(val) : null;
|
||||
})();
|
||||
|
||||
const rendererToHelperServer: RendererToHelper = {
|
||||
postEvent: (channel, ...args) => {
|
||||
events$.next({ channel, args });
|
||||
},
|
||||
};
|
||||
|
||||
const rpc = AsyncCall<HelperToRenderer>(rendererToHelperServer, {
|
||||
channel: helperPort$.then(helperPort =>
|
||||
createMessagePortChannel(helperPort)
|
||||
),
|
||||
log: false,
|
||||
});
|
||||
|
||||
const toHelperHandler = (namespace: string, name: string) => {
|
||||
return rpc[`${namespace}:${name}`];
|
||||
};
|
||||
|
||||
const toHelperEventSubscriber = (namespace: string, name: string) => {
|
||||
return (callback: (...args: any[]) => void) => {
|
||||
const subscription = events$.subscribe(({ channel, args }) => {
|
||||
if (channel === `${namespace}:${name}`) {
|
||||
callback(...args);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
subscription.unsubscribe();
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const setup = (meta: ExposedMeta) => {
|
||||
const { handlers, events } = meta;
|
||||
|
||||
const helperHandlers = Object.fromEntries(
|
||||
handlers.map(([namespace, functionNames]) => {
|
||||
return [
|
||||
namespace,
|
||||
Object.fromEntries(
|
||||
functionNames.map(name => {
|
||||
return [name, toHelperHandler(namespace, name)];
|
||||
})
|
||||
),
|
||||
];
|
||||
})
|
||||
);
|
||||
|
||||
const helperEvents = Object.fromEntries(
|
||||
events.map(([namespace, eventNames]) => {
|
||||
return [
|
||||
namespace,
|
||||
Object.fromEntries(
|
||||
eventNames.map(name => {
|
||||
return [name, toHelperEventSubscriber(namespace, name)];
|
||||
})
|
||||
),
|
||||
];
|
||||
})
|
||||
);
|
||||
return [helperHandlers, helperEvents];
|
||||
};
|
||||
|
||||
const [apis, events] = setup(meta);
|
||||
|
||||
return { apis, events };
|
||||
}
|
||||
162
packages/infra/src/type.ts
Normal file
162
packages/infra/src/type.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import type { TypedEventEmitter } from './core/event-emitter';
|
||||
|
||||
export abstract class HandlerManager<
|
||||
Namespace extends string,
|
||||
Handlers extends Record<string, PrimitiveHandlers>
|
||||
> {
|
||||
static instance: HandlerManager<string, Record<string, PrimitiveHandlers>>;
|
||||
private _app: App<Namespace, Handlers>;
|
||||
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<K extends keyof Handlers>(
|
||||
name: K,
|
||||
...args: Parameters<Handlers[K]>
|
||||
): Promise<ReturnType<Handlers[K]>> {
|
||||
return this._handlers[name](...args);
|
||||
}
|
||||
|
||||
static getInstance(): HandlerManager<
|
||||
string,
|
||||
Record<string, PrimitiveHandlers>
|
||||
> {
|
||||
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<any>;
|
||||
|
||||
export type DBHandlers = {
|
||||
getDocAsUpdates: (
|
||||
workspaceId: string,
|
||||
subdocId?: string
|
||||
) => Promise<Uint8Array>;
|
||||
applyDocUpdate: (
|
||||
id: string,
|
||||
update: Uint8Array,
|
||||
subdocId?: string
|
||||
) => Promise<void>;
|
||||
addBlob: (
|
||||
workspaceId: string,
|
||||
key: string,
|
||||
data: Uint8Array
|
||||
) => Promise<void>;
|
||||
getBlob: (workspaceId: string, key: string) => Promise<any>;
|
||||
deleteBlob: (workspaceId: string, key: string) => Promise<void>;
|
||||
getBlobKeys: (workspaceId: string) => Promise<any>;
|
||||
getDefaultStorageLocation: () => Promise<string>;
|
||||
};
|
||||
|
||||
export type DebugHandlers = {
|
||||
revealLogFile: () => Promise<string>;
|
||||
logFilePath: () => Promise<string>;
|
||||
};
|
||||
|
||||
export type DialogHandlers = {
|
||||
revealDBFile: (workspaceId: string) => Promise<any>;
|
||||
loadDBFile: () => Promise<any>;
|
||||
saveDBFileAs: (workspaceId: string) => Promise<any>;
|
||||
moveDBFile: (workspaceId: string, dbFileLocation?: string) => Promise<any>;
|
||||
selectDBFileLocation: () => Promise<any>;
|
||||
setFakeDialogResult: (result: any) => Promise<any>;
|
||||
};
|
||||
|
||||
export type UIHandlers = {
|
||||
handleThemeChange: (theme: 'system' | 'light' | 'dark') => Promise<any>;
|
||||
handleSidebarVisibilityChange: (visible: boolean) => Promise<any>;
|
||||
handleMinimizeApp: () => Promise<any>;
|
||||
handleMaximizeApp: () => Promise<any>;
|
||||
handleCloseApp: () => Promise<any>;
|
||||
getGoogleOauthCode: () => Promise<any>;
|
||||
};
|
||||
|
||||
export type ClipboardHandlers = {
|
||||
copyAsImageFromString: (dataURL: string) => Promise<void>;
|
||||
};
|
||||
|
||||
export type ExportHandlers = {
|
||||
savePDFFileAs: (title: string) => Promise<any>;
|
||||
};
|
||||
|
||||
export type UpdaterHandlers = {
|
||||
currentVersion: () => Promise<any>;
|
||||
quitAndInstall: () => Promise<any>;
|
||||
checkForUpdatesAndNotify: () => Promise<any>;
|
||||
};
|
||||
|
||||
export type WorkspaceHandlers = {
|
||||
list: () => Promise<[workspaceId: string, meta: WorkspaceMeta][]>;
|
||||
delete: (id: string) => Promise<void>;
|
||||
getMeta: (id: string) => Promise<WorkspaceMeta>;
|
||||
};
|
||||
|
||||
export type EventMap = DBHandlers &
|
||||
DebugHandlers &
|
||||
DialogHandlers &
|
||||
UIHandlers &
|
||||
ClipboardHandlers &
|
||||
ExportHandlers &
|
||||
UpdaterHandlers &
|
||||
WorkspaceHandlers;
|
||||
|
||||
export type UnwrapManagerHandlerToServerSide<
|
||||
ElectronEvent extends {
|
||||
frameId: number;
|
||||
processId: number;
|
||||
},
|
||||
Manager extends HandlerManager<string, Record<string, PrimitiveHandlers>>
|
||||
> = Manager extends HandlerManager<infer _, infer Handlers>
|
||||
? {
|
||||
[K in keyof Handlers]: Handlers[K] extends (
|
||||
...args: infer Args
|
||||
) => Promise<infer R>
|
||||
? (event: ElectronEvent, ...args: Args) => Promise<R>
|
||||
: never;
|
||||
}
|
||||
: never;
|
||||
|
||||
export type UnwrapManagerHandlerToClientSide<
|
||||
Manager extends HandlerManager<string, Record<string, PrimitiveHandlers>>
|
||||
> = Manager extends HandlerManager<infer _, infer Handlers>
|
||||
? {
|
||||
[K in keyof Handlers]: Handlers[K] extends (
|
||||
...args: infer Args
|
||||
) => Promise<infer R>
|
||||
? (...args: Args) => Promise<R>
|
||||
: never;
|
||||
}
|
||||
: never;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type App<
|
||||
Namespace extends string,
|
||||
Handlers extends Record<string, PrimitiveHandlers>
|
||||
> = TypedEventEmitter<{
|
||||
[K in keyof Handlers as `${Namespace}:${K & string}`]: Handlers[K];
|
||||
}>;
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user