--- /dev/null
+// SPDX-FileCopyrightText: 2024 Chris Duncan <chris@zoso.dev>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import { isDataView } from 'node:util/types'
+
+if (globalThis.Worker == null) {
+ const { Worker } = await import('node:worker_threads')
+ //@ts-expect-error
+ Worker.prototype.addEventListener = Worker.prototype.addListener
+ //@ts-expect-error
+ globalThis.Worker = Worker
+}
+
+type Thread = {
+ isBusy: boolean
+ worker: Worker
+}
+
+/**
+* Processes an array of tasks using Web Workers.
+*/
+export class Pool {
+ #queue: object[] = []
+ #resolve: Function = (value: unknown) => { }
+ #results: object[] = []
+ #threads: Thread[] = []
+
+ get isDone () {
+ for (const thread of this.#threads) {
+ if (thread.isBusy) {
+ return false
+ }
+ return true
+ }
+ }
+
+ constructor (url: string | URL) {
+ for (let i = navigator.hardwareConcurrency - 1; i > 0; i--) {
+ const thread = {
+ isBusy: false,
+ worker: new Worker(new URL(url, import.meta.url), { type: 'module' })
+ }
+ thread.worker.addEventListener('message', (message) => {
+ thread.isBusy = false
+ this.#report(thread, message.data ?? message)
+ })
+ this.#threads.push(thread)
+ }
+ }
+
+ #assign (thread: Thread) {
+ const next = this.#queue.shift()
+ if (next != null) {
+ thread.isBusy = true
+ thread.worker.postMessage(next)
+ }
+ }
+
+ #report (thread: Thread, result: any) {
+ this.#results.push(result)
+ if (this.#queue.length > 0) {
+ this.#assign(thread)
+ } else if (this.isDone) {
+ this.#resolve(this.#results)
+ }
+ }
+
+ async work (data: object[]): Promise<any> {
+ if (!Array.isArray(data)) data = [data]
+ return new Promise(resolve => {
+ this.#queue = data
+ this.#resolve = resolve
+ for (const thread of this.#threads) {
+ this.#assign(thread)
+ }
+ })
+ }
+}
import { buffer, hex, utf8 } from './convert.js'
import { Entropy } from './entropy.js'
-import { workerUrl } from './passkey.js'
-import { Thread } from './thread.js'
const { subtle } = globalThis.crypto
const ERR_MSG = 'Failed to store item in Safe'
export class Safe {
#storage: Storage
- #thread: Thread
constructor () {
this.#storage = globalThis.sessionStorage
- this.#thread = new Thread(workerUrl)
}
/**
const iv = new Entropy()
if (typeof passkey === 'string') {
try {
- const keyBuffer = await this.#thread.work({ password: passkey, iv: iv.bytes })
- passkey = await subtle.importKey('raw', keyBuffer, 'AES-GCM', false, ['encrypt'])
- // passkey = await subtle.importKey('raw', utf8.toBytes(passkey), 'PBKDF2', false, ['deriveBits', 'deriveKey'])
- // passkey = await subtle.deriveKey({ name: 'PBKDF2', hash: 'SHA-512', salt: iv.bytes, iterations: 210000 }, passkey, { name: 'AES-GCM', length: 256 }, false, ['encrypt'])
+ passkey = await subtle.importKey('raw', utf8.toBytes(passkey), 'PBKDF2', false, ['deriveBits', 'deriveKey'])
+ passkey = await subtle.deriveKey({ name: 'PBKDF2', hash: 'SHA-512', salt: iv.bytes, iterations: 210000 }, passkey, { name: 'AES-GCM', length: 256 }, false, ['encrypt'])
} catch (err) {
throw new Error(ERR_MSG)
}
+++ /dev/null
-// SPDX-FileCopyrightText: 2024 Chris Duncan <chris@zoso.dev>
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-if (globalThis.Worker == null) {
- const { Worker } = await import('node:worker_threads')
- //@ts-expect-error
- Worker.prototype.addEventListener = Worker.prototype.addListener
- //@ts-expect-error
- globalThis.Worker = Worker
-}
-
-type Task = {
- data: object,
- resolve: Function
-}
-
-/**
-* Processes tasks from a queue using a Web Worker.
-*/
-export class Thread {
- #isAvailable: boolean = true
- #queue: Task[] = []
- #task?: Task
- #worker: Worker
-
- #post (next: Task) {
- this.#isAvailable = false
- this.#task = next
- this.#worker.postMessage(next.data)
- }
-
- constructor (url: string | URL) {
- this.#worker = new Worker(new URL(url, import.meta.url), { type: 'module' })
- this.#worker.addEventListener('message', (message) => {
- const result = message.data ?? message
- if (this.#task == null) {
- throw new ReferenceError(`Error resolving Worker result: ${result}`)
- }
- const resolve = this.#task.resolve
- const next = this.#queue.shift()
- if (next == null) {
- this.#isAvailable = true
- } else {
- this.#post(next)
- }
- resolve(result)
- })
- }
-
- async work (data: object): Promise<any> {
- return new Promise(resolve => {
- if (this.#isAvailable) {
- this.#post({ data, resolve })
- } else {
- this.#queue.push({ data, resolve })
- }
- })
- }
-}