Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moved tRPC to Electron IPC from WebSocket #93

Merged
merged 1 commit into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions src/core/error-handling.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { IsAny } from 'type-fest'
import type { z } from 'zod'

export type ToError<E> = E extends Error ? E : Error

const toPath = (path: (number | string)[]) =>
path
.map((segment) => (typeof segment === 'number' ? `[${segment}]` : segment))
Expand Down Expand Up @@ -31,9 +30,12 @@ export function getMessage(cause: unknown) {
return cause.message
}

export function toError<Cause>(cause: Cause) {
if (cause instanceof Error) return cause as ToError<Cause>
return new Error(String(cause)) as ToError<Cause>
export type AsError<T> = IsAny<T> extends true ? Error : T extends Error ? T : Error

export function toError<Cause>(cause: Cause): AsError<Cause>
export function toError(cause: unknown) {
if (cause instanceof Error) return cause
return new Error(getMessage(cause))
}

export function raiseError(factory: () => Error): never {
Expand Down
21 changes: 0 additions & 21 deletions src/core/rpc.ts

This file was deleted.

7 changes: 7 additions & 0 deletions src/core/rpc/ipc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { IpcMainInvokeEvent } from 'electron'

export const theRpcChannel = 'trpc:msg'

export interface CreateContextOptions {
event: IpcMainInvokeEvent
}
36 changes: 36 additions & 0 deletions src/core/rpc/transformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Base64 } from 'js-base64'
import { SuperJSON } from 'superjson'
import { Attachment } from '../attachments'
import { raiseError } from '../error-handling'

export default function useSuperJson() {
SuperJSON.registerCustom(
{
isApplicable: (v) => v instanceof Attachment,
serialize: (attachment) => ({
name: attachment.name,
type: attachment.type,
data: Base64.fromUint8Array(attachment)
}),
deserialize: (attachment) =>
new Attachment(attachment.name, attachment.type, Base64.toUint8Array(attachment.data))
},
'Attachment'
)

// HACK: tRPC won't serialize functions; but doesn't filter
// them out, so IPC throws an error if they are passed.
// This can also ensure any types with functions
// won't be returned as seen in the return
// type information for the router.
SuperJSON.registerCustom(
{
isApplicable: (v): v is () => undefined => typeof v === 'function',
serialize: (_: () => undefined) => raiseError(() => new TypeError('Functions may not be serialized')),
deserialize: (_: never) => () => undefined
},
'Function'
)

return SuperJSON
}
27 changes: 14 additions & 13 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { app, shell, BrowserWindow, nativeTheme } from 'electron'
import Logger from 'electron-log'
import { sleep } from 'radash'
import appIcon from '../../resources/icon.png?asset&asarUnpack'
import useApiServer from './server'
import { getAuthToken } from './services/trpc'
import { useAppRouter } from './routes/router'
import { createIpcHandler } from './services/rpc/ipc'
import { logError } from './utilities'
import { toError } from '@/error-handling'

Expand All @@ -18,7 +18,7 @@ Logger.transports.console.format = '{h}:{i}:{s}.{ms} [{level}] › {text}'
Logger.transports.file.level = 'debug'
Logger.errorHandler.startCatching()

async function createWindow(port: number) {
async function createWindow() {
const willStartWithDark = nativeTheme.shouldUseDarkColors || nativeTheme.shouldUseInvertedColorScheme

const window = new BrowserWindow({
Expand All @@ -27,7 +27,11 @@ async function createWindow(port: number) {
backgroundColor: willStartWithDark ? '#121212' : 'white',
icon: appIcon,
show: true,
useContentSize: true
useContentSize: true,
webPreferences: {
preload: joinPath(__dirname, '../preload/index.mjs'),
sandbox: false
}
})

window.removeMenu()
Expand Down Expand Up @@ -55,14 +59,9 @@ async function createWindow(port: number) {
// HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production.
if (is.dev && process.env.ELECTRON_RENDERER_URL != null) {
const url = new URL(process.env.ELECTRON_RENDERER_URL)
url.searchParams.set('port', String(port))
url.searchParams.set('auth', getAuthToken())
await window.loadURL(url.toString())
await window.loadURL(process.env.ELECTRON_RENDERER_URL)
} else {
await window.loadFile(joinPath(__dirname, '../renderer/index.html'), {
query: { port: String(port), auth: getAuthToken() }
})
await window.loadFile(joinPath(__dirname, '../renderer/index.html'))
}

return window
Expand Down Expand Up @@ -110,5 +109,7 @@ await app.whenReady()
// Set app user model id for windows
electronApp.setAppUserModelId('org.sleepingcats.BridgeCmdr')

const port = useApiServer()
await createWindow(port)
// If macOS close vs quit behavior is reimplemented, we will have
// to make sure port and handler are accessible earlier.
const handler = createIpcHandler({ router: useAppRouter() })
handler.attachWindow(await createWindow())
2 changes: 1 addition & 1 deletion src/main/routes/data/sources.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { z } from 'zod'
import { NewSource, SourceUpdate, useSourcesDatabase } from '../../dao/sources'
import { DocumentId } from '../../services/database'
import { procedure, router } from '../../services/trpc'
import { procedure, router } from '../../services/rpc/trpc'
import { Attachment } from '@/attachments'

export type { Source, NewSource, SourceUpdate } from '../../dao/sources'
Expand Down
2 changes: 1 addition & 1 deletion src/main/routes/data/storage.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { memo } from 'radash'
import { z } from 'zod'
import useUserStore from '../../dao/storage'
import { procedure, router } from '../../services/trpc'
import { procedure, router } from '../../services/rpc/trpc'

const useUserStoreRouter = memo(function useUserStoreRouter() {
const storage = useUserStore()
Expand Down
2 changes: 1 addition & 1 deletion src/main/routes/data/switches.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NewSwitch, SwitchUpdate, useSwitchesDatabase } from '../../dao/switches'
import { DocumentId } from '../../services/database'
import { procedure, router } from '../../services/trpc'
import { procedure, router } from '../../services/rpc/trpc'

export { Switch, NewSwitch, SwitchUpdate } from '../../dao/switches'

Expand Down
2 changes: 1 addition & 1 deletion src/main/routes/data/ties.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import useTiesDatabase, { NewTie, TieUpdate } from '../../dao/ties'
import { DocumentId } from '../../services/database'
import { procedure, router } from '../../services/trpc'
import { procedure, router } from '../../services/rpc/trpc'

export type { Tie, NewTie, TieUpdate } from '../../dao/ties'

Expand Down
4 changes: 2 additions & 2 deletions src/main/routes/drivers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { memo } from 'radash'
import { z } from 'zod'
import useDrivers from '../services/drivers'
import { procedure, router } from '../services/trpc'
import { procedure, router } from '../services/rpc/trpc'

const Channel = z.number().int().nonnegative().finite()

Expand All @@ -12,7 +12,7 @@ const useDriversRouter = memo(function useDriversRoute() {
const drivers = useDrivers()

return router({
all: procedure.query(drivers.all),
all: procedure.query(drivers.allInfo),
get: procedure.input(z.string().uuid()).query(async ({ input }) => {
await drivers.get(input)
}),
Expand Down
2 changes: 1 addition & 1 deletion src/main/routes/ports.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { memo } from 'radash'
import { z } from 'zod'
import useSerialPorts from '../services/ports'
import { procedure, router } from '../services/trpc'
import { procedure, router } from '../services/rpc/trpc'

const useSerialPortRouter = memo(function useSerialPortRouter() {
const ports = useSerialPorts()
Expand Down
3 changes: 1 addition & 2 deletions src/main/routes/router.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { memo } from 'radash'
import useAppInfo from '../info/app'
import useUserInfo from '../info/user'
import { createCallerFactory, procedure, router } from '../services/trpc'
import { procedure, router } from '../services/rpc/trpc'
import useSourcesRouter from './data/sources'
import useUserStoreRouter from './data/storage'
import useSwitchesRouter from './data/switches'
Expand Down Expand Up @@ -32,4 +32,3 @@ export const useAppRouter = memo(() =>
)

export type AppRouter = ReturnType<typeof useAppRouter>
export const createCaller = createCallerFactory(useAppRouter())
2 changes: 1 addition & 1 deletion src/main/routes/startup.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { memo } from 'radash'
import { procedure, router } from '../services/rpc/trpc'
import useStartup from '../services/startup'
import { procedure, router } from '../services/trpc'

const useStartupRouter = memo(function useStartupRouter() {
const startup = useStartup()
Expand Down
2 changes: 1 addition & 1 deletion src/main/routes/system.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { memo } from 'radash'
import { z } from 'zod'
import { procedure, router } from '../services/rpc/trpc'
import useSystem from '../services/system'
import { procedure, router } from '../services/trpc'

const useSystemRouter = memo(function useSystemRouter() {
const system = useSystem()
Expand Down
2 changes: 1 addition & 1 deletion src/main/routes/updater.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { observable } from '@trpc/server/observable'
import { memo } from 'radash'
import { procedure, router } from '../services/trpc'
import { procedure, router } from '../services/rpc/trpc'
import useUpdater from '../services/updater'
import type { AppUpdaterEventMap } from '../services/updater'

Expand Down
69 changes: 0 additions & 69 deletions src/main/server.ts

This file was deleted.

11 changes: 9 additions & 2 deletions src/main/services/drivers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,13 @@ const useDrivers = memo(function useDriver() {
}
}

const all = defineOperation(() => Array.from(registry.values()).filter((driver) => driver.enabled))
const registered = defineOperation(() => Array.from(registry.values()).filter((driver) => driver.enabled))

const allInfo = defineOperation(() =>
Array.from(registry.values())
.filter((driver) => driver.enabled)
.map((d) => d.metadata)
)

const get = defineOperation((guid: string) => registry.get(guid) ?? null)

Expand Down Expand Up @@ -193,7 +199,8 @@ const useDrivers = memo(function useDriver() {
})

return {
all,
registered,
allInfo,
get,
activate,
powerOn,
Expand Down
Loading