From def57079324f1e50823d2eca128d15c82ef5623f Mon Sep 17 00:00:00 2001 From: Alex Kern Date: Mon, 9 Sep 2024 17:48:30 -0700 Subject: [PATCH] checkpoint --- package.json | 5 +- pnpm-lock.yaml | 7 +++ src/components/Downloader.tsx | 14 ++--- src/hooks/useUploaderConnections.ts | 9 ++- src/messages.ts | 96 +++++++++++++---------------- 5 files changed, 62 insertions(+), 69 deletions(-) diff --git a/package.json b/package.json index 31f9afb..bcc2e79 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,6 @@ "autoprefixer": "^10.4.20", "debug": "^4.3.6", "express": "^4.19.2", - "fp-ts": "^2.16.9", - "io-ts": "^2.2.21", "ioredis": "^4.28.5", "next": "^14.2.8", "nodemon": "^1.19.4", @@ -42,7 +40,8 @@ "twilio": "^2.11.1", "use-http": "^1.0.28", "web-streams-polyfill": "^3.3.3", - "webrtcsupport": "^2.2.0" + "webrtcsupport": "^2.2.0", + "zod": "^3.23.8" }, "devDependencies": { "@types/debug": "^4.1.12", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4f8035c..181ea9b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,9 @@ dependencies: webrtcsupport: specifier: ^2.2.0 version: 2.2.0 + zod: + specifier: ^3.23.8 + version: 3.23.8 devDependencies: '@types/debug': @@ -9401,3 +9404,7 @@ packages: /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + + /zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + dev: false diff --git a/src/components/Downloader.tsx b/src/components/Downloader.tsx index 0983f36..b121972 100644 --- a/src/components/Downloader.tsx +++ b/src/components/Downloader.tsx @@ -10,7 +10,7 @@ import { mobileVendor, mobileModel, } from 'react-device-detect' -import * as t from 'io-ts' +import { z } from 'zod' // Add this import import { ChunkMessage, decodeMessage, Message, MessageType } from '../messages' import { createZipStream } from '../zip-stream' import { DataConnection } from 'peerjs' @@ -110,7 +110,7 @@ export default function Downloader({ type: string }> | null>(null) const processChunk = useRef< - ((message: t.TypeOf) => void) | null + ((message: z.infer) => void) | null >(null) const [shouldAttemptConnection, setShouldAttemptConnection] = useState(false) const [open, setOpen] = useState(false) @@ -133,7 +133,7 @@ export default function Downloader({ const handleOpen = () => { setOpen(true) - const request: t.TypeOf = { + const request: z.infer = { type: MessageType.RequestInfo, browserName: browserName, browserVersion: browserVersion, @@ -149,7 +149,7 @@ export default function Downloader({ const handleData = (data: unknown) => { try { - const message = decodeMessage(data) + const message = Message.parse(data) // Use Zod's parse method switch (message.type) { case MessageType.Info: setFilesInfo(message.files) @@ -248,7 +248,7 @@ export default function Downloader({ return } - const request: t.TypeOf = { + const request: z.infer = { type: MessageType.Start, fileName: filesInfo[nextFileIndex].fileName, offset: 0, @@ -257,7 +257,7 @@ export default function Downloader({ nextFileIndex++ } - const processChunkFunc = (message: t.TypeOf): void => { + const processChunkFunc = (message: z.infer): void => { const fileStream = fileStreamByPath[message.fileName] if (!fileStream) { console.error('no stream found for ' + message.fileName) @@ -291,7 +291,7 @@ export default function Downloader({ downloadPromise .then(() => { - const request: t.TypeOf = { + const request: z.infer = { type: MessageType.Done, } dataConnection.send(request) diff --git a/src/hooks/useUploaderConnections.ts b/src/hooks/useUploaderConnections.ts index a84ab72..8abed65 100644 --- a/src/hooks/useUploaderConnections.ts +++ b/src/hooks/useUploaderConnections.ts @@ -6,7 +6,6 @@ import { UploaderConnectionStatus, } from '../types' import { decodeMessage, Message, MessageType } from '../messages' -import * as t from 'io-ts' import { getFileName } from '../fs' // TODO(@kern): Test for better values @@ -59,7 +58,7 @@ export function useUploaderConnections( switch (message.type) { case MessageType.RequestInfo: { if (message.password !== password) { - const request: t.TypeOf = { + const request: Message = { type: MessageType.Error, error: 'Invalid password', } @@ -105,13 +104,13 @@ export function useUploaderConnections( const fileInfo = files.map((f) => { return { - fileName: f.fileName ?? f.name ?? '', + fileName: getFileName(f), size: f.size, type: f.type, } }) - const request: t.TypeOf = { + const request: Message = { type: MessageType.Info, files: fileInfo, } @@ -142,7 +141,7 @@ export function useUploaderConnections( const end = Math.min(file.size, offset + MAX_CHUNK_SIZE) const chunkSize = end - offset const final = chunkSize < MAX_CHUNK_SIZE - const request: t.TypeOf = { + const request: Message = { type: MessageType.Chunk, fileName, offset, diff --git a/src/messages.ts b/src/messages.ts index dfb2bf6..4c24852 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -1,82 +1,70 @@ -import * as t from 'io-ts' -import { pipe } from 'fp-ts/function' -import { fold } from 'fp-ts/Either' +import { z } from 'zod' export enum MessageType { - RequestInfo = 'REQUEST_INFO', - Info = 'INFO', - Pause = 'PAUSE', - Start = 'START', - Chunk = 'CHUNK', - Done = 'DONE', - Error = 'ERROR', + RequestInfo = 'RequestInfo', + Info = 'Info', + Start = 'Start', + Chunk = 'Chunk', + Done = 'Done', + Error = 'Error', } -export const RequestInfoMessage = t.type({ - type: t.literal(MessageType.RequestInfo), - browserName: t.string, - browserVersion: t.string, - osName: t.string, - osVersion: t.string, - mobileVendor: t.string, - mobileModel: t.string, - password: t.string, +export const RequestInfoMessage = z.object({ + type: z.literal(MessageType.RequestInfo), + browserName: z.string(), + browserVersion: z.string(), + osName: z.string(), + osVersion: z.string(), + mobileVendor: z.string(), + mobileModel: z.string(), + password: z.string(), }) -export const InfoMessage = t.type({ - type: t.literal(MessageType.Info), - files: t.array( - t.type({ - fileName: t.string, - size: t.number, - type: t.string, +export const InfoMessage = z.object({ + type: z.literal(MessageType.Info), + files: z.array( + z.object({ + fileName: z.string(), + size: z.number(), + type: z.string(), }), ), }) -export const StartMessage = t.type({ - type: t.literal(MessageType.Start), - fileName: t.string, - offset: t.number, +export const StartMessage = z.object({ + type: z.literal(MessageType.Start), + fileName: z.string(), + offset: z.number(), }) -export const ChunkMessage = t.type({ - type: t.literal(MessageType.Chunk), - fileName: t.string, - offset: t.number, - bytes: t.unknown, - final: t.boolean, +export const ChunkMessage = z.object({ + type: z.literal(MessageType.Chunk), + fileName: z.string(), + offset: z.number(), + bytes: z.unknown(), + final: z.boolean(), }) -export const PauseMessage = t.type({ - type: t.literal(MessageType.Pause), +export const DoneMessage = z.object({ + type: z.literal(MessageType.Done), }) -export const DoneMessage = t.type({ - type: t.literal(MessageType.Done), +export const ErrorMessage = z.object({ + type: z.literal(MessageType.Error), + error: z.string(), }) -export const ErrorMessage = t.type({ - type: t.literal(MessageType.Error), - error: t.string, -}) - -export const Message = t.union([ +export const Message = z.discriminatedUnion('type', [ RequestInfoMessage, InfoMessage, - PauseMessage, StartMessage, ChunkMessage, DoneMessage, ErrorMessage, ]) -export function decodeMessage(data: any): t.TypeOf { - const onFailure = (errors: t.Errors): t.TypeOf => { - throw new Error(`${errors.length} error(s) found`) - } - - const onSuccess = (mesg: t.TypeOf) => mesg +export type Message = z.infer - return pipe(Message.decode(data), fold(onFailure, onSuccess)) +export function decodeMessage(data: unknown): Message { + return Message.parse(data) }