]> git.codecow.com Git - nano25519.git/commitdiff
Use URL and random UUID to uniquely identify each async call and prevent crosstalk...
authorChris Duncan <chris@codecow.com>
Tue, 16 Jun 2026 22:53:11 +0000 (15:53 -0700)
committerChris Duncan <chris@codecow.com>
Tue, 16 Jun 2026 22:53:11 +0000 (15:53 -0700)
src/lib/nano25519.ts

index 42b869a686d9d641fbda9b7dd76aac8328ffd7cc..3aea89b339246d160ba2427fa9c936574a446700 100644 (file)
@@ -1,11 +1,13 @@
 //! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
-import { Worker as NodeWorker } from 'node:worker_threads'
+import { Worker as NodeWorker, threadId } from 'node:worker_threads'
 //@ts-expect-error
 import nano25519_wasm from '../../build/nano25519.wasm'
 
 type Data = {
+       url: string
+       id: string
        action: string
        message?: string | ArrayBuffer
        privateKey?: string
@@ -188,18 +190,30 @@ const nano25519_init = (bytes: number[]): { derive: typeof derive, sign: typeof
        function handleMessage (message: unknown): void {
                NODE: if (host == null) setTimeout(() => handleMessage(message), 0)
                let result: undefined | boolean | string | Uint8Array<ArrayBuffer>
+               let url: undefined | string
+               let id: undefined | string
                try {
                        if (message == null
                                || typeof message !== 'object'
                                || !('data' in message)
                                || message.data == null
                                || typeof message.data !== 'object'
+                               || !('url' in message.data)
+                               || typeof message.data.url !== 'string'
+                               || !('id' in message.data)
+                               || typeof message.data.id !== 'string'
                                || !('action' in message.data)
                                || typeof message.data.action !== 'string'
                        ) {
                                throw new TypeError('Invalid nano25519Worker request')
                        }
-                       const data: Data = message.data as object & Record<"action", string>
+                       const data: Data = message.data as object & { url: string, id: string, action: string }
+                       url = data.url
+                       id = data.id
+                       if (typeof url !== 'string' || typeof id !== 'string') return
+                       BROWSER: if (url !== location.href) return
+                       NODE: if (url !== threadId.toString()) return
+
                        if (data.action === 'start') {
                                isListening = true
                                result = 'started'
@@ -246,8 +260,8 @@ const nano25519_init = (bytes: number[]): { derive: typeof derive, sign: typeof
                                result = JSON.stringify(err)
                        }
                } finally {
-                       BROWSER: postMessage(result)
-                       NODE: host?.postMessage({ data: result })
+                       BROWSER: postMessage({ url, id, result })
+                       NODE: host?.postMessage({ data: { url, id, result } })
                }
        }
        BROWSER: addEventListener('message', handleMessage)
@@ -283,13 +297,14 @@ function isBytes (a: unknown): a is Uint8Array<ArrayBuffer> {
 // Create worker module
 function init (): void {
        try {
-               url = URL.createObjectURL(new Blob([nano25519_worker], { type: 'text/javascript' }))
+               BROWSER: url = URL.createObjectURL(new Blob([nano25519_worker], { type: 'text/javascript' }))
                BROWSER: worker = new Worker(url, { type: 'module' })
                NODE: worker = new NodeWorker(nano25519_worker, {
                        eval: true,
                        stderr: false,
                        stdout: false
                })
+               NODE: url = worker.threadId.toString()
                console.log(`nano25519 initialized.`)
                isWorkerReady = true
        } catch (err) {
@@ -311,14 +326,17 @@ async function start (): Promise<void> {
        if (!isWorkerReady) init()
        if (!isWorkerListening) {
                return new Promise(async (resolve, reject): Promise<void> => {
-                       const onstarted = (msg: any): void => {
-                               const result = msg.data
+                       const onstarted = (msg: { data: Record<string, unknown> }): void => {
+                               const { data } = msg
+                               if (data.url !== url) return
+                               const { result } = data
                                if (result === 'started') {
                                        console.log('worker started successfully')
                                        isWorkerListening = true
-                                       resolve(result)
+                                       resolve()
                                } else {
-                                       reject(result)
+                                       isWorkerListening = false
+                                       reject()
                                }
                        }
                        //@ts-expect-error
@@ -330,8 +348,9 @@ async function start (): Promise<void> {
                        //@ts-expect-error
                        NODE: worker.on('message', onstarted)
                        console.log(`starting worker`)
-                       BROWSER: worker.postMessage({ action: 'start' })
-                       NODE: worker.postMessage({ data: { action: 'start' } })
+                       const id = crypto.randomUUID()
+                       BROWSER: worker.postMessage({ url, id, action: 'start' })
+                       NODE: worker.postMessage({ data: { url, id, action: 'start' } })
                })
        }
 }
@@ -346,8 +365,10 @@ async function dispatch (data: { [key: string]: string | ArrayBuffer | Uint8Arra
                                transfer.push(data[k])
                        }
                }
-               const onresult = (msg: Record<"data", unknown>): void => {
-                       const result = msg.data
+               const onresult = (msg: { data: Record<string, unknown> }): void => {
+                       const { data } = msg
+                       if (data.url !== url) return
+                       const { result } = data
                        console.log(`received result from worker: `, result)
                        if (typeof result !== 'boolean' && typeof result !== 'string' && !isBytes(result)) {
                                return reject('Invalid return type')
@@ -363,6 +384,8 @@ async function dispatch (data: { [key: string]: string | ArrayBuffer | Uint8Arra
                //@ts-expect-error
                NODE: worker.on('message', onresult)
                console.log(`sending data to worker: `, data)
+               data.url = url
+               data.id = crypto.randomUUID()
                BROWSER: worker.postMessage(data, transfer)
                NODE: worker.postMessage({ data }, transfer)
        })
@@ -371,8 +394,10 @@ async function dispatch (data: { [key: string]: string | ArrayBuffer | Uint8Arra
 // Request that the worker stop listening without terminating
 async function stop (): Promise<void> {
        return new Promise((resolve, reject): void => {
-               const onstop = (msg: any): void => {
-                       const result = msg.data
+               const onstop = (msg: { data: Record<string, unknown> }): void => {
+                       const { data } = msg
+                       if (data.url !== url) return
+                       const { result } = data
                        if (result === 'stopped') {
                                console.log('worker stopped successfully')
                                isWorkerListening = false
@@ -390,8 +415,9 @@ async function stop (): Promise<void> {
                //@ts-expect-error
                NODE: worker.on('message', onstop)
                console.log(`stopping worker`)
-               BROWSER: worker.postMessage({ action: 'stop' })
-               NODE: worker.postMessage({ data: { action: 'stop' } })
+               const id = crypto.randomUUID()
+               BROWSER: worker.postMessage({ url, id, action: 'stop' })
+               NODE: worker.postMessage({ data: { url, id, action: 'stop' } })
        })
 }