Skip to content

Commit

Permalink
Moved from WebSocket to IPC for tRPC
Browse files Browse the repository at this point in the history
  • Loading branch information
6XGate committed Nov 10, 2024
1 parent dac8916 commit add0614
Show file tree
Hide file tree
Showing 49 changed files with 456 additions and 229 deletions.
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

0 comments on commit add0614

Please sign in to comment.