sabcom 0.1.84 → 0.1.94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.cjs CHANGED
@@ -64,7 +64,7 @@ var Header = /*#__PURE__*/ function(Header) {
64
64
  Header[Header["CHUNK_SIZE"] = 3] = "CHUNK_SIZE";
65
65
  return Header;
66
66
  }({});
67
- const HEADER_VALUES = 1 + Math.max(Object.values(Handshake).length, Object.values(Header).length) / 2;
67
+ const HEADER_VALUES = 4;
68
68
  const HEADER_SIZE = Uint32Array.BYTES_PER_ELEMENT * HEADER_VALUES;
69
69
  function* writeGenerator(data, buffer, { timeout = 5000 } = {}) {
70
70
  if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export const SEMAPHORE = 0;\n\nexport enum Semaphore {\n READY,\n HANDSHAKE,\n PAYLOAD,\n}\n\nexport enum Handshake {\n TOTAL_SIZE = 1,\n TOTAL_CHUNKS,\n}\n\nexport enum Header {\n CHUNK_INDEX = 1,\n CHUNK_OFFSET,\n CHUNK_SIZE,\n}\n\nexport const HEADER_VALUES = 1 + Math.max(Object.values(Handshake).length, Object.values(Header).length) / 2;\nexport const HEADER_SIZE = Uint32Array.BYTES_PER_ELEMENT * HEADER_VALUES;\n\nexport interface Options {\n timeout?: number;\n}\n\nexport interface WaitRequest {\n target: Int32Array;\n index: number;\n value: number;\n timeout?: number;\n}\n\nexport type WaitResponse = ReturnType<typeof Atomics.wait>;\n\nexport function* writeGenerator(data: Uint8Array, buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, void, WaitResponse> {\n if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {\n throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');\n }\n const chunkSize = buffer.byteLength - HEADER_SIZE;\n if (chunkSize <= 0) {\n throw new Error('SharedArrayBuffer too small for header');\n }\n const totalSize = data.length;\n const totalChunks = Math.ceil(totalSize / chunkSize);\n const header = new Int32Array(buffer);\n\n header[Handshake.TOTAL_SIZE] = totalSize;\n header[Handshake.TOTAL_CHUNKS] = totalChunks;\n Atomics.store(header, SEMAPHORE, Semaphore.HANDSHAKE);\n Atomics.notify(header, SEMAPHORE);\n\n try {\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.HANDSHAKE,\n timeout,\n };\n if (handshakeResult === 'timed-out') {\n throw new Error('Reader handshake timeout');\n }\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const start = i * chunkSize;\n const end = Math.min(start + chunkSize, totalSize);\n const size = end - start;\n payload.set(data.subarray(start, end), 0);\n header[Header.CHUNK_INDEX] = i;\n header[Header.CHUNK_OFFSET] = start;\n header[Header.CHUNK_SIZE] = size;\n Atomics.store(header, SEMAPHORE, Semaphore.PAYLOAD);\n Atomics.notify(header, SEMAPHORE);\n\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.PAYLOAD,\n timeout,\n };\n if (chunkResult === 'timed-out') {\n throw new Error(`Reader timeout on chunk ${i}/${totalChunks - 1}`);\n }\n }\n } finally {\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n }\n}\n\nexport function* readGenerator(buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, Uint8Array, WaitResponse> {\n if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {\n throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');\n }\n const chunkSize = buffer.byteLength - HEADER_SIZE;\n if (chunkSize <= 0) {\n throw new Error('SharedArrayBuffer too small for header');\n }\n const header = new Int32Array(buffer);\n\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (handshakeResult === 'timed-out') {\n throw new Error('Handshake timeout');\n }\n if (header[SEMAPHORE] !== Semaphore.HANDSHAKE) {\n throw new Error('Invalid handshake state');\n }\n\n const totalSize = header[Handshake.TOTAL_SIZE];\n const totalChunks = header[Handshake.TOTAL_CHUNKS];\n if (totalSize < 0 || totalChunks < 0) {\n throw new Error('Invalid handshake values');\n }\n if (totalSize === 0 && totalChunks !== 0) {\n throw new Error('Invalid handshake values');\n }\n if (totalSize > totalChunks * chunkSize) {\n throw new Error('Invalid handshake values');\n }\n const data = new Uint8Array(totalSize);\n\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (chunkResult === 'timed-out') {\n throw new Error(`Writer timeout waiting for chunk ${i}`);\n }\n // @ts-expect-error does not infer number\n if (header[SEMAPHORE] !== Semaphore.PAYLOAD) {\n throw new Error(`Expected payload header, received ${Semaphore[header[SEMAPHORE]]}`);\n }\n const chunkIndex = header[Header.CHUNK_INDEX];\n if (i !== chunkIndex) {\n throw new Error(`Reader integrity failure for chunk ${chunkIndex} expected ${i}`);\n }\n const offset = header[Header.CHUNK_OFFSET];\n const size = header[Header.CHUNK_SIZE];\n if (offset < 0 || size <= 0 || size > chunkSize || offset + size > totalSize) {\n throw new Error(`Invalid chunk metadata for chunk ${chunkIndex}`);\n }\n data.set(payload.subarray(0, size), offset);\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n }\n return data;\n}\n\nexport const writeSync = (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options) => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n result = gen.next(waitResult);\n }\n};\n\nexport const write = async (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options) => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;\n result = gen.next(waitResult);\n }\n};\n\nexport const readSync = (buffer: SharedArrayBuffer, options?: Options): Uint8Array => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n result = gen.next(waitResult);\n }\n return result.value;\n};\n\nexport const read = async (buffer: SharedArrayBuffer, options?: Options): Promise<Uint8Array> => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;\n result = gen.next(waitResult);\n }\n return result.value;\n};\n"],"names":["HEADER_SIZE","HEADER_VALUES","Handshake","Header","SEMAPHORE","Semaphore","read","readGenerator","readSync","write","writeGenerator","writeSync","Math","max","Object","values","length","Uint32Array","BYTES_PER_ELEMENT","data","buffer","timeout","byteLength","Int32Array","Error","chunkSize","totalSize","totalChunks","ceil","header","Atomics","store","notify","handshakeResult","target","index","value","payload","Uint8Array","i","start","end","min","size","set","subarray","chunkResult","chunkIndex","offset","options","gen","result","next","done","waitResult","wait","request","waitAsync"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export const SEMAPHORE = 0;\n\nexport enum Semaphore {\n READY,\n HANDSHAKE,\n PAYLOAD,\n}\n\nexport enum Handshake {\n TOTAL_SIZE = 1,\n TOTAL_CHUNKS,\n}\n\nexport enum Header {\n CHUNK_INDEX = 1,\n CHUNK_OFFSET,\n CHUNK_SIZE,\n}\n\nexport const HEADER_VALUES = 4;\n\n/**\n * Size in bytes reserved for protocol header in the SharedArrayBuffer.\n * The usable payload size is `buffer.byteLength - HEADER_SIZE`.\n */\nexport const HEADER_SIZE = Uint32Array.BYTES_PER_ELEMENT * HEADER_VALUES;\n\nexport interface Options {\n /** Max wait time in milliseconds before timeout error. Default: 5000 */\n timeout?: number;\n}\n\nexport interface WaitRequest {\n target: Int32Array;\n index: number;\n value: number;\n timeout?: number;\n}\n\nexport type WaitResponse = ReturnType<typeof Atomics.wait>;\n\nexport function* writeGenerator(data: Uint8Array, buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, void, WaitResponse> {\n if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {\n throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');\n }\n const chunkSize = buffer.byteLength - HEADER_SIZE;\n if (chunkSize <= 0) {\n throw new Error('SharedArrayBuffer too small for header');\n }\n const totalSize = data.length;\n const totalChunks = Math.ceil(totalSize / chunkSize);\n const header = new Int32Array(buffer);\n\n header[Handshake.TOTAL_SIZE] = totalSize;\n header[Handshake.TOTAL_CHUNKS] = totalChunks;\n Atomics.store(header, SEMAPHORE, Semaphore.HANDSHAKE);\n Atomics.notify(header, SEMAPHORE);\n\n try {\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.HANDSHAKE,\n timeout,\n };\n if (handshakeResult === 'timed-out') {\n throw new Error('Reader handshake timeout');\n }\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const start = i * chunkSize;\n const end = Math.min(start + chunkSize, totalSize);\n const size = end - start;\n payload.set(data.subarray(start, end), 0);\n header[Header.CHUNK_INDEX] = i;\n header[Header.CHUNK_OFFSET] = start;\n header[Header.CHUNK_SIZE] = size;\n Atomics.store(header, SEMAPHORE, Semaphore.PAYLOAD);\n Atomics.notify(header, SEMAPHORE);\n\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.PAYLOAD,\n timeout,\n };\n if (chunkResult === 'timed-out') {\n throw new Error(`Reader timeout on chunk ${i}/${totalChunks - 1}`);\n }\n }\n } finally {\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n }\n}\n\nexport function* readGenerator(buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, Uint8Array, WaitResponse> {\n if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {\n throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');\n }\n const chunkSize = buffer.byteLength - HEADER_SIZE;\n if (chunkSize <= 0) {\n throw new Error('SharedArrayBuffer too small for header');\n }\n const header = new Int32Array(buffer);\n\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (handshakeResult === 'timed-out') {\n throw new Error('Handshake timeout');\n }\n if (header[SEMAPHORE] !== Semaphore.HANDSHAKE) {\n throw new Error('Invalid handshake state');\n }\n\n const totalSize = header[Handshake.TOTAL_SIZE];\n const totalChunks = header[Handshake.TOTAL_CHUNKS];\n if (totalSize < 0 || totalChunks < 0) {\n throw new Error('Invalid handshake values');\n }\n if (totalSize === 0 && totalChunks !== 0) {\n throw new Error('Invalid handshake values');\n }\n if (totalSize > totalChunks * chunkSize) {\n throw new Error('Invalid handshake values');\n }\n const data = new Uint8Array(totalSize);\n\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (chunkResult === 'timed-out') {\n throw new Error(`Writer timeout waiting for chunk ${i}`);\n }\n // @ts-expect-error does not infer number\n if (header[SEMAPHORE] !== Semaphore.PAYLOAD) {\n throw new Error(`Expected payload header, received ${Semaphore[header[SEMAPHORE]]}`);\n }\n const chunkIndex = header[Header.CHUNK_INDEX];\n if (i !== chunkIndex) {\n throw new Error(`Reader integrity failure for chunk ${chunkIndex} expected ${i}`);\n }\n const offset = header[Header.CHUNK_OFFSET];\n const size = header[Header.CHUNK_SIZE];\n if (offset < 0 || size <= 0 || size > chunkSize || offset + size > totalSize) {\n throw new Error(`Invalid chunk metadata for chunk ${chunkIndex}`);\n }\n data.set(payload.subarray(0, size), offset);\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n }\n return data;\n}\n\n/**\n * Synchronously writes data to a SharedArrayBuffer for cross-thread transfer.\n * Blocks until the reader receives all data. Use in workers where blocking is acceptable.\n * @param data - Bytes to send (can be larger than buffer, will be chunked automatically)\n * @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Reader handshake timeout\" - reader didn't respond in time\n * @throws {Error} \"Reader timeout on chunk N/M\" - reader stopped responding mid-transfer\n * @example\n * ```typescript\n * import { workerData } from 'worker_threads';\n * import { writeSync } from 'sabcom';\n *\n * const buffer = workerData as SharedArrayBuffer;\n * const data = new TextEncoder().encode('Hello from worker');\n * writeSync(data, buffer);\n * ```\n */\nexport const writeSync = (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options): void => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n result = gen.next(waitResult);\n }\n};\n\n/**\n * Asynchronously writes data to a SharedArrayBuffer for cross-thread transfer.\n * Non-blocking, suitable for main thread. Resolves when the reader receives all data.\n * @param data - Bytes to send (can be larger than buffer, will be chunked automatically)\n * @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Reader handshake timeout\" - reader didn't respond in time\n * @throws {Error} \"Reader timeout on chunk N/M\" - reader stopped responding mid-transfer\n * @example\n * ```typescript\n * import { Worker } from 'worker_threads';\n * import { write } from 'sabcom';\n *\n * const buffer = new SharedArrayBuffer(4096);\n * const worker = new Worker('./worker.js', { workerData: buffer });\n * const data = new TextEncoder().encode('Hello from main');\n * await write(data, buffer);\n * ```\n */\nexport const write = async (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options): Promise<void> => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;\n result = gen.next(waitResult);\n }\n};\n\n/**\n * Synchronously reads data from a SharedArrayBuffer written by another thread.\n * Blocks until all data is received. Use in workers where blocking is acceptable.\n * @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @returns Complete data as Uint8Array\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Handshake timeout\" - writer didn't send data in time\n * @throws {Error} \"Invalid handshake state\" - protocol error\n * @throws {Error} \"Writer timeout waiting for chunk N\" - writer stopped responding mid-transfer\n * @example\n * ```typescript\n * import { workerData } from 'worker_threads';\n * import { readSync } from 'sabcom';\n *\n * const buffer = workerData as SharedArrayBuffer;\n * const data = readSync(buffer);\n * const message = new TextDecoder().decode(data);\n * ```\n */\nexport const readSync = (buffer: SharedArrayBuffer, options?: Options): Uint8Array => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n result = gen.next(waitResult);\n }\n return result.value;\n};\n\n/**\n * Asynchronously reads data from a SharedArrayBuffer written by another thread.\n * Non-blocking, suitable for main thread. Resolves when all data is received.\n * @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @returns Complete data as Uint8Array\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Handshake timeout\" - writer didn't send data in time\n * @throws {Error} \"Invalid handshake state\" - protocol error\n * @throws {Error} \"Writer timeout waiting for chunk N\" - writer stopped responding mid-transfer\n * @example\n * ```typescript\n * import { Worker } from 'worker_threads';\n * import { read } from 'sabcom';\n *\n * const buffer = new SharedArrayBuffer(4096);\n * const worker = new Worker('./worker.js', { workerData: buffer });\n * // Worker writes data...\n * const data = await read(buffer);\n * const message = new TextDecoder().decode(data);\n * ```\n */\nexport const read = async (buffer: SharedArrayBuffer, options?: Options): Promise<Uint8Array> => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;\n result = gen.next(waitResult);\n }\n return result.value;\n};\n"],"names":["HEADER_SIZE","HEADER_VALUES","Handshake","Header","SEMAPHORE","Semaphore","read","readGenerator","readSync","write","writeGenerator","writeSync","Uint32Array","BYTES_PER_ELEMENT","data","buffer","timeout","byteLength","Int32Array","Error","chunkSize","totalSize","length","totalChunks","Math","ceil","header","Atomics","store","notify","handshakeResult","target","index","value","payload","Uint8Array","i","start","end","min","size","set","subarray","chunkResult","chunkIndex","offset","options","gen","result","next","done","waitResult","wait","request","waitAsync"],"mappings":"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"}
package/build/index.d.ts CHANGED
@@ -13,9 +13,14 @@ export declare enum Header {
13
13
  CHUNK_OFFSET = 2,
14
14
  CHUNK_SIZE = 3
15
15
  }
16
- export declare const HEADER_VALUES: number;
16
+ export declare const HEADER_VALUES = 4;
17
+ /**
18
+ * Size in bytes reserved for protocol header in the SharedArrayBuffer.
19
+ * The usable payload size is `buffer.byteLength - HEADER_SIZE`.
20
+ */
17
21
  export declare const HEADER_SIZE: number;
18
22
  export interface Options {
23
+ /** Max wait time in milliseconds before timeout error. Default: 5000 */
19
24
  timeout?: number;
20
25
  }
21
26
  export interface WaitRequest {
@@ -27,7 +32,92 @@ export interface WaitRequest {
27
32
  export type WaitResponse = ReturnType<typeof Atomics.wait>;
28
33
  export declare function writeGenerator(data: Uint8Array, buffer: SharedArrayBuffer, { timeout }?: Options): Generator<WaitRequest, void, WaitResponse>;
29
34
  export declare function readGenerator(buffer: SharedArrayBuffer, { timeout }?: Options): Generator<WaitRequest, Uint8Array, WaitResponse>;
35
+ /**
36
+ * Synchronously writes data to a SharedArrayBuffer for cross-thread transfer.
37
+ * Blocks until the reader receives all data. Use in workers where blocking is acceptable.
38
+ * @param data - Bytes to send (can be larger than buffer, will be chunked automatically)
39
+ * @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
40
+ * @param options - Optional configuration
41
+ * @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
42
+ * @throws {Error} "SharedArrayBuffer too small for header"
43
+ * @throws {Error} "Reader handshake timeout" - reader didn't respond in time
44
+ * @throws {Error} "Reader timeout on chunk N/M" - reader stopped responding mid-transfer
45
+ * @example
46
+ * ```typescript
47
+ * import { workerData } from 'worker_threads';
48
+ * import { writeSync } from 'sabcom';
49
+ *
50
+ * const buffer = workerData as SharedArrayBuffer;
51
+ * const data = new TextEncoder().encode('Hello from worker');
52
+ * writeSync(data, buffer);
53
+ * ```
54
+ */
30
55
  export declare const writeSync: (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options) => void;
56
+ /**
57
+ * Asynchronously writes data to a SharedArrayBuffer for cross-thread transfer.
58
+ * Non-blocking, suitable for main thread. Resolves when the reader receives all data.
59
+ * @param data - Bytes to send (can be larger than buffer, will be chunked automatically)
60
+ * @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
61
+ * @param options - Optional configuration
62
+ * @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
63
+ * @throws {Error} "SharedArrayBuffer too small for header"
64
+ * @throws {Error} "Reader handshake timeout" - reader didn't respond in time
65
+ * @throws {Error} "Reader timeout on chunk N/M" - reader stopped responding mid-transfer
66
+ * @example
67
+ * ```typescript
68
+ * import { Worker } from 'worker_threads';
69
+ * import { write } from 'sabcom';
70
+ *
71
+ * const buffer = new SharedArrayBuffer(4096);
72
+ * const worker = new Worker('./worker.js', { workerData: buffer });
73
+ * const data = new TextEncoder().encode('Hello from main');
74
+ * await write(data, buffer);
75
+ * ```
76
+ */
31
77
  export declare const write: (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options) => Promise<void>;
78
+ /**
79
+ * Synchronously reads data from a SharedArrayBuffer written by another thread.
80
+ * Blocks until all data is received. Use in workers where blocking is acceptable.
81
+ * @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
82
+ * @param options - Optional configuration
83
+ * @returns Complete data as Uint8Array
84
+ * @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
85
+ * @throws {Error} "SharedArrayBuffer too small for header"
86
+ * @throws {Error} "Handshake timeout" - writer didn't send data in time
87
+ * @throws {Error} "Invalid handshake state" - protocol error
88
+ * @throws {Error} "Writer timeout waiting for chunk N" - writer stopped responding mid-transfer
89
+ * @example
90
+ * ```typescript
91
+ * import { workerData } from 'worker_threads';
92
+ * import { readSync } from 'sabcom';
93
+ *
94
+ * const buffer = workerData as SharedArrayBuffer;
95
+ * const data = readSync(buffer);
96
+ * const message = new TextDecoder().decode(data);
97
+ * ```
98
+ */
32
99
  export declare const readSync: (buffer: SharedArrayBuffer, options?: Options) => Uint8Array;
100
+ /**
101
+ * Asynchronously reads data from a SharedArrayBuffer written by another thread.
102
+ * Non-blocking, suitable for main thread. Resolves when all data is received.
103
+ * @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
104
+ * @param options - Optional configuration
105
+ * @returns Complete data as Uint8Array
106
+ * @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
107
+ * @throws {Error} "SharedArrayBuffer too small for header"
108
+ * @throws {Error} "Handshake timeout" - writer didn't send data in time
109
+ * @throws {Error} "Invalid handshake state" - protocol error
110
+ * @throws {Error} "Writer timeout waiting for chunk N" - writer stopped responding mid-transfer
111
+ * @example
112
+ * ```typescript
113
+ * import { Worker } from 'worker_threads';
114
+ * import { read } from 'sabcom';
115
+ *
116
+ * const buffer = new SharedArrayBuffer(4096);
117
+ * const worker = new Worker('./worker.js', { workerData: buffer });
118
+ * // Worker writes data...
119
+ * const data = await read(buffer);
120
+ * const message = new TextDecoder().decode(data);
121
+ * ```
122
+ */
33
123
  export declare const read: (buffer: SharedArrayBuffer, options?: Options) => Promise<Uint8Array>;
package/build/index.js CHANGED
@@ -16,7 +16,7 @@ export var Header = /*#__PURE__*/ function(Header) {
16
16
  Header[Header["CHUNK_SIZE"] = 3] = "CHUNK_SIZE";
17
17
  return Header;
18
18
  }({});
19
- export const HEADER_VALUES = 1 + Math.max(Object.values(Handshake).length, Object.values(Header).length) / 2;
19
+ export const HEADER_VALUES = 4;
20
20
  export const HEADER_SIZE = Uint32Array.BYTES_PER_ELEMENT * HEADER_VALUES;
21
21
  export function* writeGenerator(data, buffer, { timeout = 5000 } = {}) {
22
22
  if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export const SEMAPHORE = 0;\n\nexport enum Semaphore {\n READY,\n HANDSHAKE,\n PAYLOAD,\n}\n\nexport enum Handshake {\n TOTAL_SIZE = 1,\n TOTAL_CHUNKS,\n}\n\nexport enum Header {\n CHUNK_INDEX = 1,\n CHUNK_OFFSET,\n CHUNK_SIZE,\n}\n\nexport const HEADER_VALUES = 1 + Math.max(Object.values(Handshake).length, Object.values(Header).length) / 2;\nexport const HEADER_SIZE = Uint32Array.BYTES_PER_ELEMENT * HEADER_VALUES;\n\nexport interface Options {\n timeout?: number;\n}\n\nexport interface WaitRequest {\n target: Int32Array;\n index: number;\n value: number;\n timeout?: number;\n}\n\nexport type WaitResponse = ReturnType<typeof Atomics.wait>;\n\nexport function* writeGenerator(data: Uint8Array, buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, void, WaitResponse> {\n if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {\n throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');\n }\n const chunkSize = buffer.byteLength - HEADER_SIZE;\n if (chunkSize <= 0) {\n throw new Error('SharedArrayBuffer too small for header');\n }\n const totalSize = data.length;\n const totalChunks = Math.ceil(totalSize / chunkSize);\n const header = new Int32Array(buffer);\n\n header[Handshake.TOTAL_SIZE] = totalSize;\n header[Handshake.TOTAL_CHUNKS] = totalChunks;\n Atomics.store(header, SEMAPHORE, Semaphore.HANDSHAKE);\n Atomics.notify(header, SEMAPHORE);\n\n try {\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.HANDSHAKE,\n timeout,\n };\n if (handshakeResult === 'timed-out') {\n throw new Error('Reader handshake timeout');\n }\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const start = i * chunkSize;\n const end = Math.min(start + chunkSize, totalSize);\n const size = end - start;\n payload.set(data.subarray(start, end), 0);\n header[Header.CHUNK_INDEX] = i;\n header[Header.CHUNK_OFFSET] = start;\n header[Header.CHUNK_SIZE] = size;\n Atomics.store(header, SEMAPHORE, Semaphore.PAYLOAD);\n Atomics.notify(header, SEMAPHORE);\n\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.PAYLOAD,\n timeout,\n };\n if (chunkResult === 'timed-out') {\n throw new Error(`Reader timeout on chunk ${i}/${totalChunks - 1}`);\n }\n }\n } finally {\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n }\n}\n\nexport function* readGenerator(buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, Uint8Array, WaitResponse> {\n if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {\n throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');\n }\n const chunkSize = buffer.byteLength - HEADER_SIZE;\n if (chunkSize <= 0) {\n throw new Error('SharedArrayBuffer too small for header');\n }\n const header = new Int32Array(buffer);\n\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (handshakeResult === 'timed-out') {\n throw new Error('Handshake timeout');\n }\n if (header[SEMAPHORE] !== Semaphore.HANDSHAKE) {\n throw new Error('Invalid handshake state');\n }\n\n const totalSize = header[Handshake.TOTAL_SIZE];\n const totalChunks = header[Handshake.TOTAL_CHUNKS];\n if (totalSize < 0 || totalChunks < 0) {\n throw new Error('Invalid handshake values');\n }\n if (totalSize === 0 && totalChunks !== 0) {\n throw new Error('Invalid handshake values');\n }\n if (totalSize > totalChunks * chunkSize) {\n throw new Error('Invalid handshake values');\n }\n const data = new Uint8Array(totalSize);\n\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (chunkResult === 'timed-out') {\n throw new Error(`Writer timeout waiting for chunk ${i}`);\n }\n // @ts-expect-error does not infer number\n if (header[SEMAPHORE] !== Semaphore.PAYLOAD) {\n throw new Error(`Expected payload header, received ${Semaphore[header[SEMAPHORE]]}`);\n }\n const chunkIndex = header[Header.CHUNK_INDEX];\n if (i !== chunkIndex) {\n throw new Error(`Reader integrity failure for chunk ${chunkIndex} expected ${i}`);\n }\n const offset = header[Header.CHUNK_OFFSET];\n const size = header[Header.CHUNK_SIZE];\n if (offset < 0 || size <= 0 || size > chunkSize || offset + size > totalSize) {\n throw new Error(`Invalid chunk metadata for chunk ${chunkIndex}`);\n }\n data.set(payload.subarray(0, size), offset);\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n }\n return data;\n}\n\nexport const writeSync = (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options) => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n result = gen.next(waitResult);\n }\n};\n\nexport const write = async (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options) => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;\n result = gen.next(waitResult);\n }\n};\n\nexport const readSync = (buffer: SharedArrayBuffer, options?: Options): Uint8Array => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n result = gen.next(waitResult);\n }\n return result.value;\n};\n\nexport const read = async (buffer: SharedArrayBuffer, options?: Options): Promise<Uint8Array> => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;\n result = gen.next(waitResult);\n }\n return result.value;\n};\n"],"names":["SEMAPHORE","Semaphore","Handshake","Header","HEADER_VALUES","Math","max","Object","values","length","HEADER_SIZE","Uint32Array","BYTES_PER_ELEMENT","writeGenerator","data","buffer","timeout","byteLength","Int32Array","Error","chunkSize","totalSize","totalChunks","ceil","header","Atomics","store","notify","handshakeResult","target","index","value","payload","Uint8Array","i","start","end","min","size","set","subarray","chunkResult","readGenerator","chunkIndex","offset","writeSync","options","gen","result","next","done","waitResult","wait","write","request","waitAsync","readSync","read"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export const SEMAPHORE = 0;\n\nexport enum Semaphore {\n READY,\n HANDSHAKE,\n PAYLOAD,\n}\n\nexport enum Handshake {\n TOTAL_SIZE = 1,\n TOTAL_CHUNKS,\n}\n\nexport enum Header {\n CHUNK_INDEX = 1,\n CHUNK_OFFSET,\n CHUNK_SIZE,\n}\n\nexport const HEADER_VALUES = 4;\n\n/**\n * Size in bytes reserved for protocol header in the SharedArrayBuffer.\n * The usable payload size is `buffer.byteLength - HEADER_SIZE`.\n */\nexport const HEADER_SIZE = Uint32Array.BYTES_PER_ELEMENT * HEADER_VALUES;\n\nexport interface Options {\n /** Max wait time in milliseconds before timeout error. Default: 5000 */\n timeout?: number;\n}\n\nexport interface WaitRequest {\n target: Int32Array;\n index: number;\n value: number;\n timeout?: number;\n}\n\nexport type WaitResponse = ReturnType<typeof Atomics.wait>;\n\nexport function* writeGenerator(data: Uint8Array, buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, void, WaitResponse> {\n if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {\n throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');\n }\n const chunkSize = buffer.byteLength - HEADER_SIZE;\n if (chunkSize <= 0) {\n throw new Error('SharedArrayBuffer too small for header');\n }\n const totalSize = data.length;\n const totalChunks = Math.ceil(totalSize / chunkSize);\n const header = new Int32Array(buffer);\n\n header[Handshake.TOTAL_SIZE] = totalSize;\n header[Handshake.TOTAL_CHUNKS] = totalChunks;\n Atomics.store(header, SEMAPHORE, Semaphore.HANDSHAKE);\n Atomics.notify(header, SEMAPHORE);\n\n try {\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.HANDSHAKE,\n timeout,\n };\n if (handshakeResult === 'timed-out') {\n throw new Error('Reader handshake timeout');\n }\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const start = i * chunkSize;\n const end = Math.min(start + chunkSize, totalSize);\n const size = end - start;\n payload.set(data.subarray(start, end), 0);\n header[Header.CHUNK_INDEX] = i;\n header[Header.CHUNK_OFFSET] = start;\n header[Header.CHUNK_SIZE] = size;\n Atomics.store(header, SEMAPHORE, Semaphore.PAYLOAD);\n Atomics.notify(header, SEMAPHORE);\n\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.PAYLOAD,\n timeout,\n };\n if (chunkResult === 'timed-out') {\n throw new Error(`Reader timeout on chunk ${i}/${totalChunks - 1}`);\n }\n }\n } finally {\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n }\n}\n\nexport function* readGenerator(buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, Uint8Array, WaitResponse> {\n if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {\n throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');\n }\n const chunkSize = buffer.byteLength - HEADER_SIZE;\n if (chunkSize <= 0) {\n throw new Error('SharedArrayBuffer too small for header');\n }\n const header = new Int32Array(buffer);\n\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (handshakeResult === 'timed-out') {\n throw new Error('Handshake timeout');\n }\n if (header[SEMAPHORE] !== Semaphore.HANDSHAKE) {\n throw new Error('Invalid handshake state');\n }\n\n const totalSize = header[Handshake.TOTAL_SIZE];\n const totalChunks = header[Handshake.TOTAL_CHUNKS];\n if (totalSize < 0 || totalChunks < 0) {\n throw new Error('Invalid handshake values');\n }\n if (totalSize === 0 && totalChunks !== 0) {\n throw new Error('Invalid handshake values');\n }\n if (totalSize > totalChunks * chunkSize) {\n throw new Error('Invalid handshake values');\n }\n const data = new Uint8Array(totalSize);\n\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (chunkResult === 'timed-out') {\n throw new Error(`Writer timeout waiting for chunk ${i}`);\n }\n // @ts-expect-error does not infer number\n if (header[SEMAPHORE] !== Semaphore.PAYLOAD) {\n throw new Error(`Expected payload header, received ${Semaphore[header[SEMAPHORE]]}`);\n }\n const chunkIndex = header[Header.CHUNK_INDEX];\n if (i !== chunkIndex) {\n throw new Error(`Reader integrity failure for chunk ${chunkIndex} expected ${i}`);\n }\n const offset = header[Header.CHUNK_OFFSET];\n const size = header[Header.CHUNK_SIZE];\n if (offset < 0 || size <= 0 || size > chunkSize || offset + size > totalSize) {\n throw new Error(`Invalid chunk metadata for chunk ${chunkIndex}`);\n }\n data.set(payload.subarray(0, size), offset);\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n }\n return data;\n}\n\n/**\n * Synchronously writes data to a SharedArrayBuffer for cross-thread transfer.\n * Blocks until the reader receives all data. Use in workers where blocking is acceptable.\n * @param data - Bytes to send (can be larger than buffer, will be chunked automatically)\n * @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Reader handshake timeout\" - reader didn't respond in time\n * @throws {Error} \"Reader timeout on chunk N/M\" - reader stopped responding mid-transfer\n * @example\n * ```typescript\n * import { workerData } from 'worker_threads';\n * import { writeSync } from 'sabcom';\n *\n * const buffer = workerData as SharedArrayBuffer;\n * const data = new TextEncoder().encode('Hello from worker');\n * writeSync(data, buffer);\n * ```\n */\nexport const writeSync = (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options): void => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n result = gen.next(waitResult);\n }\n};\n\n/**\n * Asynchronously writes data to a SharedArrayBuffer for cross-thread transfer.\n * Non-blocking, suitable for main thread. Resolves when the reader receives all data.\n * @param data - Bytes to send (can be larger than buffer, will be chunked automatically)\n * @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Reader handshake timeout\" - reader didn't respond in time\n * @throws {Error} \"Reader timeout on chunk N/M\" - reader stopped responding mid-transfer\n * @example\n * ```typescript\n * import { Worker } from 'worker_threads';\n * import { write } from 'sabcom';\n *\n * const buffer = new SharedArrayBuffer(4096);\n * const worker = new Worker('./worker.js', { workerData: buffer });\n * const data = new TextEncoder().encode('Hello from main');\n * await write(data, buffer);\n * ```\n */\nexport const write = async (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options): Promise<void> => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;\n result = gen.next(waitResult);\n }\n};\n\n/**\n * Synchronously reads data from a SharedArrayBuffer written by another thread.\n * Blocks until all data is received. Use in workers where blocking is acceptable.\n * @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @returns Complete data as Uint8Array\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Handshake timeout\" - writer didn't send data in time\n * @throws {Error} \"Invalid handshake state\" - protocol error\n * @throws {Error} \"Writer timeout waiting for chunk N\" - writer stopped responding mid-transfer\n * @example\n * ```typescript\n * import { workerData } from 'worker_threads';\n * import { readSync } from 'sabcom';\n *\n * const buffer = workerData as SharedArrayBuffer;\n * const data = readSync(buffer);\n * const message = new TextDecoder().decode(data);\n * ```\n */\nexport const readSync = (buffer: SharedArrayBuffer, options?: Options): Uint8Array => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n result = gen.next(waitResult);\n }\n return result.value;\n};\n\n/**\n * Asynchronously reads data from a SharedArrayBuffer written by another thread.\n * Non-blocking, suitable for main thread. Resolves when all data is received.\n * @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @returns Complete data as Uint8Array\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Handshake timeout\" - writer didn't send data in time\n * @throws {Error} \"Invalid handshake state\" - protocol error\n * @throws {Error} \"Writer timeout waiting for chunk N\" - writer stopped responding mid-transfer\n * @example\n * ```typescript\n * import { Worker } from 'worker_threads';\n * import { read } from 'sabcom';\n *\n * const buffer = new SharedArrayBuffer(4096);\n * const worker = new Worker('./worker.js', { workerData: buffer });\n * // Worker writes data...\n * const data = await read(buffer);\n * const message = new TextDecoder().decode(data);\n * ```\n */\nexport const read = async (buffer: SharedArrayBuffer, options?: Options): Promise<Uint8Array> => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;\n result = gen.next(waitResult);\n }\n return result.value;\n};\n"],"names":["SEMAPHORE","Semaphore","Handshake","Header","HEADER_VALUES","HEADER_SIZE","Uint32Array","BYTES_PER_ELEMENT","writeGenerator","data","buffer","timeout","byteLength","Int32Array","Error","chunkSize","totalSize","length","totalChunks","Math","ceil","header","Atomics","store","notify","handshakeResult","target","index","value","payload","Uint8Array","i","start","end","min","size","set","subarray","chunkResult","readGenerator","chunkIndex","offset","writeSync","options","gen","result","next","done","waitResult","wait","write","request","waitAsync","readSync","read"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sabcom",
3
- "version": "0.1.84",
3
+ "version": "0.1.94",
4
4
  "description": "A TypeScript/Node.js library for inter-thread communication using SharedArrayBuffer with atomic operations for raw buffer data transfer",
5
5
  "type": "module",
6
6
  "types": "build/index.d.ts",
@@ -14,12 +14,6 @@
14
14
  "build",
15
15
  "src/index.ts"
16
16
  ],
17
- "scripts": {
18
- "build": "rm -rf build && inop src build -i __tests__ -i *.tmp.ts && tsc --declaration --emitDeclarationOnly",
19
- "lint": "eslint src",
20
- "test": "vitest run",
21
- "test:coverage": "vitest run --coverage"
22
- },
23
17
  "repository": {
24
18
  "type": "git",
25
19
  "url": "git+https://github.com/3axap4eHko/sabcom.git"
@@ -48,19 +42,24 @@
48
42
  },
49
43
  "devDependencies": {
50
44
  "@eslint/js": "^9.39.2",
51
- "@types/node": "^25.0.3",
52
- "@typescript-eslint/eslint-plugin": "^8.52.0",
53
- "@typescript-eslint/parser": "^8.52.0",
54
- "@vitest/coverage-v8": "^4.0.16",
45
+ "@types/node": "^25.0.10",
46
+ "@typescript-eslint/eslint-plugin": "^8.53.1",
47
+ "@typescript-eslint/parser": "^8.53.1",
48
+ "@vitest/coverage-v8": "^4.0.17",
55
49
  "eslint": "^9.39.2",
56
50
  "eslint-config-prettier": "^10.1.8",
57
- "eslint-plugin-prettier": "^5.5.4",
51
+ "eslint-plugin-prettier": "^5.5.5",
58
52
  "husky": "^9.1.7",
59
53
  "inop": "^0.8.10",
60
- "prettier": "^3.7.4",
54
+ "prettier": "^3.8.1",
61
55
  "typescript": "^5.9.3",
62
- "typescript-eslint": "^8.52.0",
63
- "vitest": "^4.0.16"
56
+ "typescript-eslint": "^8.53.1",
57
+ "vitest": "^4.0.17"
64
58
  },
65
- "packageManager": "pnpm@10.27.0"
66
- }
59
+ "scripts": {
60
+ "build": "rm -rf build && inop src build -i __tests__ -i *.tmp.ts && tsc --declaration --emitDeclarationOnly",
61
+ "lint": "eslint src",
62
+ "test": "vitest run",
63
+ "test:coverage": "vitest run --coverage"
64
+ }
65
+ }
package/src/index.ts CHANGED
@@ -17,10 +17,16 @@ export enum Header {
17
17
  CHUNK_SIZE,
18
18
  }
19
19
 
20
- export const HEADER_VALUES = 1 + Math.max(Object.values(Handshake).length, Object.values(Header).length) / 2;
20
+ export const HEADER_VALUES = 4;
21
+
22
+ /**
23
+ * Size in bytes reserved for protocol header in the SharedArrayBuffer.
24
+ * The usable payload size is `buffer.byteLength - HEADER_SIZE`.
25
+ */
21
26
  export const HEADER_SIZE = Uint32Array.BYTES_PER_ELEMENT * HEADER_VALUES;
22
27
 
23
28
  export interface Options {
29
+ /** Max wait time in milliseconds before timeout error. Default: 5000 */
24
30
  timeout?: number;
25
31
  }
26
32
 
@@ -159,7 +165,27 @@ export function* readGenerator(buffer: SharedArrayBuffer, { timeout = 5000 }: Op
159
165
  return data;
160
166
  }
161
167
 
162
- export const writeSync = (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options) => {
168
+ /**
169
+ * Synchronously writes data to a SharedArrayBuffer for cross-thread transfer.
170
+ * Blocks until the reader receives all data. Use in workers where blocking is acceptable.
171
+ * @param data - Bytes to send (can be larger than buffer, will be chunked automatically)
172
+ * @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
173
+ * @param options - Optional configuration
174
+ * @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
175
+ * @throws {Error} "SharedArrayBuffer too small for header"
176
+ * @throws {Error} "Reader handshake timeout" - reader didn't respond in time
177
+ * @throws {Error} "Reader timeout on chunk N/M" - reader stopped responding mid-transfer
178
+ * @example
179
+ * ```typescript
180
+ * import { workerData } from 'worker_threads';
181
+ * import { writeSync } from 'sabcom';
182
+ *
183
+ * const buffer = workerData as SharedArrayBuffer;
184
+ * const data = new TextEncoder().encode('Hello from worker');
185
+ * writeSync(data, buffer);
186
+ * ```
187
+ */
188
+ export const writeSync = (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options): void => {
163
189
  const gen = writeGenerator(data, buffer, options);
164
190
  let result = gen.next();
165
191
  while (!result.done) {
@@ -168,7 +194,28 @@ export const writeSync = (data: Uint8Array, buffer: SharedArrayBuffer, options?:
168
194
  }
169
195
  };
170
196
 
171
- export const write = async (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options) => {
197
+ /**
198
+ * Asynchronously writes data to a SharedArrayBuffer for cross-thread transfer.
199
+ * Non-blocking, suitable for main thread. Resolves when the reader receives all data.
200
+ * @param data - Bytes to send (can be larger than buffer, will be chunked automatically)
201
+ * @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
202
+ * @param options - Optional configuration
203
+ * @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
204
+ * @throws {Error} "SharedArrayBuffer too small for header"
205
+ * @throws {Error} "Reader handshake timeout" - reader didn't respond in time
206
+ * @throws {Error} "Reader timeout on chunk N/M" - reader stopped responding mid-transfer
207
+ * @example
208
+ * ```typescript
209
+ * import { Worker } from 'worker_threads';
210
+ * import { write } from 'sabcom';
211
+ *
212
+ * const buffer = new SharedArrayBuffer(4096);
213
+ * const worker = new Worker('./worker.js', { workerData: buffer });
214
+ * const data = new TextEncoder().encode('Hello from main');
215
+ * await write(data, buffer);
216
+ * ```
217
+ */
218
+ export const write = async (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options): Promise<void> => {
172
219
  const gen = writeGenerator(data, buffer, options);
173
220
  let result = gen.next();
174
221
  while (!result.done) {
@@ -178,6 +225,27 @@ export const write = async (data: Uint8Array, buffer: SharedArrayBuffer, options
178
225
  }
179
226
  };
180
227
 
228
+ /**
229
+ * Synchronously reads data from a SharedArrayBuffer written by another thread.
230
+ * Blocks until all data is received. Use in workers where blocking is acceptable.
231
+ * @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
232
+ * @param options - Optional configuration
233
+ * @returns Complete data as Uint8Array
234
+ * @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
235
+ * @throws {Error} "SharedArrayBuffer too small for header"
236
+ * @throws {Error} "Handshake timeout" - writer didn't send data in time
237
+ * @throws {Error} "Invalid handshake state" - protocol error
238
+ * @throws {Error} "Writer timeout waiting for chunk N" - writer stopped responding mid-transfer
239
+ * @example
240
+ * ```typescript
241
+ * import { workerData } from 'worker_threads';
242
+ * import { readSync } from 'sabcom';
243
+ *
244
+ * const buffer = workerData as SharedArrayBuffer;
245
+ * const data = readSync(buffer);
246
+ * const message = new TextDecoder().decode(data);
247
+ * ```
248
+ */
181
249
  export const readSync = (buffer: SharedArrayBuffer, options?: Options): Uint8Array => {
182
250
  const gen = readGenerator(buffer, options);
183
251
  let result = gen.next();
@@ -188,6 +256,29 @@ export const readSync = (buffer: SharedArrayBuffer, options?: Options): Uint8Arr
188
256
  return result.value;
189
257
  };
190
258
 
259
+ /**
260
+ * Asynchronously reads data from a SharedArrayBuffer written by another thread.
261
+ * Non-blocking, suitable for main thread. Resolves when all data is received.
262
+ * @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
263
+ * @param options - Optional configuration
264
+ * @returns Complete data as Uint8Array
265
+ * @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
266
+ * @throws {Error} "SharedArrayBuffer too small for header"
267
+ * @throws {Error} "Handshake timeout" - writer didn't send data in time
268
+ * @throws {Error} "Invalid handshake state" - protocol error
269
+ * @throws {Error} "Writer timeout waiting for chunk N" - writer stopped responding mid-transfer
270
+ * @example
271
+ * ```typescript
272
+ * import { Worker } from 'worker_threads';
273
+ * import { read } from 'sabcom';
274
+ *
275
+ * const buffer = new SharedArrayBuffer(4096);
276
+ * const worker = new Worker('./worker.js', { workerData: buffer });
277
+ * // Worker writes data...
278
+ * const data = await read(buffer);
279
+ * const message = new TextDecoder().decode(data);
280
+ * ```
281
+ */
191
282
  export const read = async (buffer: SharedArrayBuffer, options?: Options): Promise<Uint8Array> => {
192
283
  const gen = readGenerator(buffer, options);
193
284
  let result = gen.next();