]> git.codecow.com Git - nano25519.git/commitdiff
Refactor async worker to avoid overwriting message handler, simplify dispatch, and...
authorChris Duncan <chris@zoso.dev>
Thu, 13 Aug 2026 06:02:22 +0000 (23:02 -0700)
committerChris Duncan <chris@zoso.dev>
Thu, 13 Aug 2026 06:02:22 +0000 (23:02 -0700)
src/lib/nano25519.ts

index 4c8e2a4967bebda0a2913bec60fa17f585e9571f..ff2fc6090b984063565a8a11d7eeb3080acbf5f5 100644 (file)
@@ -315,6 +315,7 @@ const nano25519_worker = `;(${nano25519_worker_init})((${nano25519_init})([${nan
  */
 let isWorkerReady: boolean = false
 let isWorkerListening: boolean = false
+let tasks: Map<string, Parameters<ConstructorParameters<PromiseConstructor>[0]>> = new Map()
 let worker: Worker | NodeWorker
 let url: string
 
@@ -353,17 +354,35 @@ function reset (): void {
        init()
 }
 
+function onresult (msg: { data: Record<string, unknown> }): void {
+       const { data } = msg
+       if (data.url !== url) return
+       if (!('id' in data) || typeof data.id !== 'string') return
+
+       const executor = tasks.get(data.id)
+       if (executor == null) return
+       const [ok, err] = executor
+
+       const { result } = data
+       console.log('received result from worker')
+       if (typeof result !== 'boolean' && typeof result !== 'string' && !isBytes(result)) {
+               err('Invalid return type')
+       }
+       ok(result)
+}
+
 // Check that the worker is running and listening before sending messages
 async function start (): Promise<void> {
        if (!isWorkerReady) init()
        if (!isWorkerListening) {
                return new Promise(async (resolve, reject): Promise<void> => {
                        const onstarted = (msg: { data: Record<string, unknown> }): void => {
-                               const { data } = msg
-                               if (data.url !== url) return
-                               const { result } = data
+                               if (msg.data.url !== url) return
+                               const { result } = msg.data
                                if (result === 'started') {
                                        console.log('worker started successfully')
+                                       BROWSER: { (worker as Worker).onmessage = onresult }
+                                       NODE: { (worker as NodeWorker).on('message', onresult) }
                                        isWorkerListening = true
                                        resolve()
                                } else {
@@ -373,59 +392,41 @@ async function start (): Promise<void> {
                        }
                        console.log(`starting worker`)
                        const id = crypto.randomUUID()
+                       const data = { url, id, action: 'start' }
                        BROWSER: {
                                worker = worker as Worker
                                worker.onerror = reject
                                worker.onmessage = onstarted
-                               worker.postMessage({ url, id, action: 'start' })
+                               worker.postMessage(data)
                        }
                        NODE: {
                                worker = worker as unknown as NodeWorker
                                worker.on('error', reject)
                                worker.on('message', onstarted)
-                               worker.postMessage({ data: { url, id, action: 'start' } })
+                               worker.postMessage({ data })
                        }
                })
        }
 }
 
 // Send command and relevant data to nano25519 worker
-async function dispatch (data: { [key: string]: string | ArrayBuffer | Uint8Array<ArrayBuffer> }): Promise<boolean | string | Uint8Array<ArrayBuffer>> {
-       return new Promise((resolve, reject) => {
-               const id = crypto.randomUUID()
-               const transfer: ArrayBuffer[] = []
-               for (let k of Object.keys(data)) {
-                       if (isBytes(data[k])) {
-                               data[k] = data[k].buffer.slice()
-                               transfer.push(data[k])
-                       }
-               }
-               const onresult = (msg: { data: Record<string, unknown> }): void => {
-                       const { data } = msg
-                       if (data.url !== url) return
-                       if (data.id !== id) return
-                       const { result } = data
-                       console.log('received result from worker')
-                       if (typeof result !== 'boolean' && typeof result !== 'string' && !isBytes(result)) {
-                               return reject('Invalid return type')
-                       }
-                       resolve(result)
-               }
-               console.log('sending data to worker')
-               data.url = url
-               data.id = id
-               BROWSER: {
-                       worker = worker as Worker
-                       worker.onerror = reject
-                       worker.onmessage = onresult
-                       worker.postMessage(data, transfer)
-               }
-               NODE: {
-                       worker = worker as unknown as NodeWorker
-                       worker.on('error', reject)
-                       worker.on('message', onresult)
-                       worker.postMessage({ data }, transfer)
+async function dispatch (data: Record<string, string | ArrayBuffer | Uint8Array<ArrayBuffer>>): Promise<unknown> {
+       const id = crypto.randomUUID()
+       const transfer: ArrayBuffer[] = []
+       for (let k of Object.keys(data)) {
+               if (isBytes(data[k])) {
+                       data[k] = data[k].buffer.slice()
+                       transfer.push(data[k])
                }
+       }
+       console.log('sending data to worker')
+       data.url = url
+       data.id = id
+       return new Promise((resolve, reject) => {
+               tasks.set(id, [resolve, reject])
+               console.log('tasks', tasks)
+               BROWSER: { (worker as Worker).postMessage(data, transfer) }
+               NODE: { (worker as NodeWorker).postMessage({ data }, transfer) }
        })
 }
 
@@ -461,10 +462,10 @@ async function stop (): Promise<void> {
        })
 }
 
-export async function run (data: Record<"action", "derive"> & Record<string, string | Uint8Array<ArrayBuffer>>): Promise<string | Uint8Array<ArrayBuffer>>
-export async function run (data: Record<"action", "sign"> & Record<string, string | Uint8Array<ArrayBuffer>>): Promise<string | Uint8Array<ArrayBuffer>>
-export async function run (data: Record<"action", "verify"> & Record<string, string | Uint8Array<ArrayBuffer>>): Promise<boolean>
-export async function run (data: Record<string, string | Uint8Array<ArrayBuffer>>): Promise<boolean | string | Uint8Array<ArrayBuffer>> {
+export async function run (data: Record<'action', 'derive'> & Record<string, string | Uint8Array<ArrayBuffer>>): Promise<string | Uint8Array<ArrayBuffer>>
+export async function run (data: Record<'action', 'sign'> & Record<string, string | Uint8Array<ArrayBuffer>>): Promise<string | Uint8Array<ArrayBuffer>>
+export async function run (data: Record<'action', 'verify'> & Record<string, string | Uint8Array<ArrayBuffer>>): Promise<boolean>
+export async function run (data: Record<'action', 'derive' | 'sign' | 'verify'> & Record<string, string | Uint8Array<ArrayBuffer>>): Promise<boolean | string | Uint8Array<ArrayBuffer>> {
        try {
                await start()
        } catch (err: any) {
@@ -472,10 +473,29 @@ export async function run (data: Record<string, string | Uint8Array<ArrayBuffer>
        }
        try {
                const result = await dispatch(data)
-               if (typeof result === 'string' && !/^([0-9a-f]{64}|[0-9a-f]{128})$/i.test(result)) {
-                       throw new Error('Invalid result')
+               switch (data.action) {
+                       case 'derive': {
+                               if ((isBytes(result) && result.byteLength === 32) || (typeof result === 'string' && /^[0-9a-f]{64}$/i.test(result))) {
+                                       return result
+                               } else {
+                                       throw new Error('derive result invalid')
+                               }
+                       }
+                       case 'sign': {
+                               if ((isBytes(result) && result.byteLength === 64) || (typeof result === 'string' && /^[0-9a-f]{128}$/i.test(result))) {
+                                       return result
+                               } else {
+                                       throw new Error('sign result invalid')
+                               }
+                       }
+                       case 'verify': {
+                               if (typeof result === 'boolean') {
+                                       return result
+                               } else {
+                                       throw new Error('verify result invalid')
+                               }
+                       }
                }
-               return result
        } catch (err: any) {
                try {
                        await stop()