-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor switch to device internally (#96)
- Renamed most switch references to device where possible - Added migration system - Added migration testing and fixes - Removed Base64, since it started failing. - Reduced logger noisiness
- Loading branch information
Showing
61 changed files
with
1,251 additions
and
406 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,18 @@ | ||
{ | ||
"i18n-ally.localesPaths": ["src/renderer/locales"], | ||
"vitest.disableWorkspaceWarning": true, | ||
"i18n-ally.keystyle": "nested" | ||
"i18n-ally.keystyle": "nested", | ||
"cSpell.words": [ | ||
"bridgecmdr", | ||
"extron", | ||
"fgpa", | ||
"pinia", | ||
"radash", | ||
"shinybow", | ||
"sindresorhus", | ||
"sixxgate", | ||
"snes", | ||
"vuelidate", | ||
"vuetify" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/** Converts a byte array into a Base64 string. */ | ||
export function toBase64(data: Uint8Array) { | ||
return btoa(String.fromCodePoint(...data)) | ||
} | ||
|
||
function* codePointsOf(data: string) { | ||
for (let i = 0, cp = data.codePointAt(i); cp != null; ++i, cp = data.codePointAt(i)) { | ||
yield cp | ||
} | ||
} | ||
|
||
/** Coverts a Base64 string into a byte array. */ | ||
export function fromBase64(data: string) { | ||
return new Uint8Array([...codePointsOf(atob(data))]) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,78 @@ | ||
import { Base64 } from 'js-base64' | ||
import { SuperJSON } from 'superjson' | ||
import { Attachment } from '../attachments' | ||
import { raiseError } from '../error-handling' | ||
import { fromBase64, toBase64 } from '@/base64' | ||
|
||
export default function useSuperJson() { | ||
SuperJSON.registerCustom( | ||
function useSuperJsonCommon() { | ||
const superJson = new SuperJSON() | ||
|
||
// HACK: tRPC and SuperJSON won't serialize functions; but | ||
// doesn't filter them out, so the IPC throws an error | ||
// if they are passed. They are also not passable via | ||
// HTTP JSON serialization. This will ensure any | ||
// types with functions won't be usable with | ||
// routes, as functions will trigger an | ||
// Error with this transformation. | ||
superJson.registerCustom( | ||
{ | ||
isApplicable: (v): v is () => undefined => typeof v === 'function', | ||
serialize: (_: () => undefined) => raiseError(() => new TypeError('Functions may not be serialized')), | ||
deserialize: (_: never) => raiseError(() => new TypeError('Functions may not be serialized')) | ||
}, | ||
'Function' | ||
) | ||
|
||
return superJson | ||
} | ||
|
||
/** | ||
* Sets up SuperJSON for use over Electron IPC. | ||
* | ||
* This will attempt to translate data into a form compatible with the limited | ||
* structuralClone ability of Electron's IPC channels. This means most | ||
* builtin objects and a very limited set of host objects may be | ||
* passed directly through without translation. | ||
*/ | ||
export function useIpcJson() { | ||
const superJson = useSuperJsonCommon() | ||
superJson.registerCustom( | ||
{ | ||
isApplicable: (v) => v instanceof Attachment, | ||
serialize: (attachment) => ({ | ||
name: attachment.name, | ||
type: attachment.type, | ||
data: Base64.fromUint8Array(attachment) | ||
data: new Uint8Array(attachment.buffer) as never | ||
}), | ||
deserialize: (attachment) => | ||
new Attachment(attachment.name, attachment.type, Base64.toUint8Array(attachment.data)) | ||
deserialize: (attachment) => new Attachment(attachment.name, attachment.type, 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( | ||
return superJson | ||
} | ||
|
||
/** | ||
* Sets up SuperJSON for use over HTTP. | ||
* | ||
* This will attempt to translate data into a form compatible with normal JSON. | ||
* This means only data allowed in regular JSON may be passed, other types | ||
* of data must be translated into the best possible representation. | ||
* Binary data will need to be Base64 encoded. | ||
*/ | ||
export function useWebJson() { | ||
const superJson = useSuperJsonCommon() | ||
superJson.registerCustom( | ||
{ | ||
isApplicable: (v): v is () => undefined => typeof v === 'function', | ||
serialize: (_: () => undefined) => raiseError(() => new TypeError('Functions may not be serialized')), | ||
deserialize: (_: never) => raiseError(() => new TypeError('Functions may not be serialized')) | ||
isApplicable: (v) => v instanceof Attachment, | ||
serialize: (attachment) => ({ | ||
name: attachment.name, | ||
type: attachment.type, | ||
data: toBase64(attachment) | ||
}), | ||
deserialize: (attachment) => new Attachment(attachment.name, attachment.type, fromBase64(attachment.data)) | ||
}, | ||
'Function' | ||
'Attachment' | ||
) | ||
|
||
return SuperJSON | ||
return superJson | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { memo } from 'radash' | ||
import { z } from 'zod' | ||
import { | ||
Database, | ||
DocumentId, | ||
inferDocumentOf, | ||
inferNewDocumentOf, | ||
inferUpdatesOf, | ||
inferUpsertOf | ||
} from '../services/database' | ||
import useTiesDatabase from './ties' | ||
import type { RevisionId } from '../services/database' | ||
|
||
export const DeviceModel = z.object({ | ||
driverId: DocumentId, | ||
title: z.string().min(1), | ||
path: z.string().min(1) | ||
}) | ||
|
||
const useDevicesDatabase = memo( | ||
() => | ||
new (class extends Database.of('devices', DeviceModel) { | ||
readonly #ties = useTiesDatabase() | ||
|
||
override async remove(id: DocumentId, rev?: RevisionId) { | ||
await super.remove(id, rev) | ||
|
||
const related = await this.#ties.forDevice(id) | ||
await Promise.all( | ||
related.map(async ({ _id, _rev }) => { | ||
await this.#ties.remove(_id, _rev) | ||
}) | ||
) | ||
} | ||
})() | ||
) | ||
|
||
export type Device = inferDocumentOf<typeof DeviceModel> | ||
export const Device = inferDocumentOf(DeviceModel) | ||
export type NewDevice = inferNewDocumentOf<typeof DeviceModel> | ||
export const NewDevice = inferNewDocumentOf(DeviceModel) | ||
export type DeviceUpdate = inferUpdatesOf<typeof DeviceModel> | ||
export const DeviceUpdate = inferUpdatesOf(DeviceModel) | ||
export type DeviceUpsert = inferUpsertOf<typeof DeviceModel> | ||
export const DeviceUpsert = inferUpsertOf(DeviceModel) | ||
|
||
export default useDevicesDatabase |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { z } from 'zod' | ||
import { Database, DocumentId } from '../services/database' | ||
|
||
export async function migrate() { | ||
const Model = z.object({ | ||
driverId: DocumentId, | ||
title: z.string().min(1), | ||
path: z.string().min(1) | ||
}) | ||
|
||
const switches = new Database('switches', Model) | ||
const devices = new Database('devices', Model) | ||
|
||
for (const device of await switches.all()) { | ||
// eslint-disable-next-line no-await-in-loop | ||
await devices.upsert(device) | ||
} | ||
|
||
await switches.destroy() | ||
await devices.close() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { z } from 'zod' | ||
import { Database, DocumentId } from '../services/database' | ||
|
||
export async function migrate() { | ||
const Model = z.object({}).catchall(z.unknown()) | ||
|
||
const OldModel = z | ||
.object({ | ||
_id: DocumentId, | ||
sourceId: DocumentId, | ||
switchId: DocumentId, | ||
inputChannel: z.number().int().nonnegative(), | ||
outputChannels: z.object({ | ||
video: z.number().int().nonnegative().optional(), | ||
audio: z.number().int().nonnegative().optional() | ||
}) | ||
}) | ||
.passthrough() | ||
|
||
const ties = new Database('ties', Model) | ||
|
||
for (const tie of await ties.all()) { | ||
const { switchId, ...old } = OldModel.parse(tie) | ||
|
||
// eslint-disable-next-line no-await-in-loop | ||
await ties.replace({ ...old, deviceId: switchId }) | ||
} | ||
} |
Oops, something went wrong.