@napi-rs/snappy-wasm32-wasi 7.4.2 → 7.4.4

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/snappy.wasi.d.cts CHANGED
@@ -1,10 +1,57 @@
1
1
  /* auto-generated by NAPI-RS */
2
2
  /* eslint-disable */
3
- export declare function compress(
4
- input: string | Uint8Array,
5
- options?: EncOptions | undefined | null,
6
- signal?: AbortSignal | undefined | null,
7
- ): Promise<Buffer>
3
+
4
+ /** The WASI flavor this loader instantiates. */
5
+ export declare const __napiBindingTarget: 'wasm32-wasi'
6
+
7
+ /**
8
+ * Incremental Snappy **frame-format** compressor.
9
+ *
10
+ * Feed plaintext with [`update`](Compressor::update); call
11
+ * [`finish`](Compressor::finish) for the trailing framed bytes. The valid
12
+ * stream is the concatenation of every `update()` output plus the `finish()`
13
+ * tail. This is **not** the same format as one-shot [`crate::compress`].
14
+ */
15
+ export declare class Compressor {
16
+ /** Create a streaming framed-Snappy compressor. */
17
+ constructor()
18
+ /**
19
+ * Feed one chunk. A `string` is UTF-8 encoded (matching the one-shot
20
+ * `compress` convention); a `Uint8Array` is fed verbatim. Returns the
21
+ * framed bytes produced so far (possibly empty). Never flushes the encoder
22
+ * (byte-identity invariant across chunkings).
23
+ */
24
+ update(chunk: string | Uint8Array): Buffer
25
+ /**
26
+ * Flush remaining buffered plaintext into frames off the JS thread.
27
+ * Resolves to the tail bytes. Idempotency-guarded: a second call rejects.
28
+ */
29
+ finish(): Promise<Buffer>
30
+ }
31
+
32
+ /**
33
+ * Incremental Snappy **frame-format** decompressor.
34
+ *
35
+ * Feed framed compressed chunks with [`update`](Decompressor::update); call
36
+ * [`finish`](Decompressor::finish) for the decoded tail. Not compatible with
37
+ * one-shot raw [`crate::compress`] output.
38
+ */
39
+ export declare class Decompressor {
40
+ /** Create a streaming framed-Snappy decompressor and start its worker thread. */
41
+ constructor()
42
+ /**
43
+ * Feed one compressed chunk; returns the bytes decoded so far (possibly
44
+ * empty). Deadlock-free under backpressure.
45
+ */
46
+ update(chunk: Uint8Array): Buffer
47
+ /**
48
+ * Signal EOF and resolve to the decoded tail off the JS thread.
49
+ * Idempotency-guarded: a second call rejects cleanly.
50
+ */
51
+ finish(): Promise<Buffer>
52
+ }
53
+
54
+ export declare function compress(input: string | Uint8Array, options?: EncOptions | undefined | null, signal?: AbortSignal | undefined | null): Promise<Buffer>
8
55
 
9
56
  export declare function compressSync(input: string | Uint8Array, options?: EncOptions | undefined | null): Buffer
10
57
 
@@ -29,13 +76,6 @@ export interface EncOptions {
29
76
  copyOutputData?: boolean
30
77
  }
31
78
 
32
- export declare function uncompress(
33
- input: string | Uint8Array,
34
- options?: DecOptions | undefined | null,
35
- signal?: AbortSignal | undefined | null,
36
- ): Promise<string | Buffer>
79
+ export declare function uncompress(input: string | Uint8Array, options?: DecOptions | undefined | null, signal?: AbortSignal | undefined | null): Promise<string | Buffer>
37
80
 
38
- export declare function uncompressSync(
39
- input: string | Uint8Array,
40
- options?: DecOptions | undefined | null,
41
- ): string | Buffer
81
+ export declare function uncompressSync(input: string | Uint8Array, options?: DecOptions | undefined | null): string | Buffer
Binary file
@@ -13,10 +13,9 @@ const handler = new MessageHandler({
13
13
  // eslint-disable-next-line no-console
14
14
  console.log.apply(console, arguments)
15
15
  },
16
- printErr: function() {
16
+ printErr: function () {
17
17
  // eslint-disable-next-line no-console
18
18
  console.error.apply(console, arguments)
19
-
20
19
  },
21
20
  })
22
21
  return instantiateNapiModuleSync(wasmModule, {
@@ -37,9 +36,32 @@ const handler = new MessageHandler({
37
36
  },
38
37
  })
39
38
  },
40
-
41
39
  })
42
40
 
41
+ // When this wasm thread dies, raise the addon's crash flag before emnapi reports
42
+ // it. The loader thread may be inside wasm, in a cleanup call that waits on this
43
+ // thread; the flag is what those waits check between short slices, and once it
44
+ // is up they trap instead of waiting for good. It is a word in the shared wasm
45
+ // memory, and the loader posts a view of it (see
46
+ // napi_wasm_thread_crash_flag_address) after it instantiated the wasm, or right
47
+ // after it created this worker. No view, no flag: an addon built with an older
48
+ // napi, or a failure before the view arrived.
49
+ let __addonCrashFlag
50
+ const __beforeReportError = handler.beforeReportError
51
+ handler.beforeReportError = function (...args) {
52
+ if (__addonCrashFlag !== undefined) {
53
+ try {
54
+ Atomics.store(__addonCrashFlag, 0, 1)
55
+ } catch {}
56
+ }
57
+ return __beforeReportError.apply(this, args)
58
+ }
59
+
43
60
  globalThis.onmessage = function (e) {
61
+ const data = e && e.data
62
+ if (data && data.__napiRsAddonCrashFlag instanceof Int32Array) {
63
+ __addonCrashFlag = data.__napiRsAddonCrashFlag
64
+ return
65
+ }
44
66
  handler.handle(e)
45
67
  }
package/wasi-worker.mjs CHANGED
@@ -1,74 +1,186 @@
1
- import fs from "node:fs";
2
- import { createRequire } from "node:module";
3
- import { parse } from "node:path";
4
- import { WASI } from "node:wasi";
5
- import { parentPort, Worker } from "node:worker_threads";
1
+ import fs from 'node:fs'
2
+ import { createRequire } from 'node:module'
3
+ import { parse } from 'node:path'
4
+ import { WASI } from 'node:wasi'
5
+ import { parentPort, threadId, Worker, workerData } from 'node:worker_threads'
6
6
 
7
- const require = createRequire(import.meta.url);
7
+ const require = createRequire(import.meta.url)
8
8
 
9
- const {
10
- instantiateNapiModuleSync,
11
- MessageHandler,
12
- getDefaultContext,
13
- emnapiAsyncWorkPlugin,
14
- emnapiTSFNPlugin,
15
- } = require("@napi-rs/wasm-runtime");
9
+ // The thread spawn that created this worker has already returned, so the loader
10
+ // thread may be waiting on this thread. A failure before the crash hook below is
11
+ // in place (a missing '@napi-rs/wasm-runtime', say) has to raise the crash flags
12
+ // too, then fail the worker as before.
13
+ let handler
14
+ try {
15
+ const {
16
+ instantiateNapiModuleSync,
17
+ MessageHandler,
18
+ getDefaultContext,
19
+ emnapiAsyncWorkPlugin,
20
+ emnapiTSFNPlugin,
21
+ } = require('@napi-rs/wasm-runtime')
16
22
 
17
- if (parentPort) {
18
- parentPort.on("message", (data) => {
19
- globalThis.onmessage({ data });
20
- });
21
- }
23
+ if (parentPort) {
24
+ parentPort.on('message', (data) => {
25
+ globalThis.onmessage({ data })
26
+ })
27
+ }
22
28
 
23
- Object.assign(globalThis, {
24
- self: globalThis,
25
- require,
26
- Worker,
27
- importScripts: function (f) {
28
- ;(0, eval)(fs.readFileSync(f, "utf8") + "//# sourceURL=" + f);
29
- },
30
- postMessage: function (msg) {
31
- if (parentPort) {
32
- parentPort.postMessage(msg);
33
- }
34
- },
35
- });
29
+ Object.assign(globalThis, {
30
+ self: globalThis,
31
+ require,
32
+ Worker,
33
+ importScripts: function (f) {
34
+ // oxlint-disable-next-line no-eval -- WASI importScripts polyfill
35
+ ;(0, eval)(fs.readFileSync(f, 'utf8') + '//# sourceURL=' + f)
36
+ },
37
+ postMessage: function (msg) {
38
+ if (parentPort) {
39
+ parentPort.postMessage(msg)
40
+ }
41
+ },
42
+ })
36
43
 
37
- const emnapiContext = getDefaultContext();
44
+ const emnapiContext = getDefaultContext()
38
45
 
39
- const __rootDir = parse(process.cwd()).root;
46
+ const __cwd = process.cwd()
47
+ const __rootDir =
48
+ (workerData && typeof workerData.rootDir === 'string' && workerData.rootDir) ||
49
+ parse(__cwd).root
50
+ const __hostRoot =
51
+ (workerData && typeof workerData.hostRoot === 'string' && workerData.hostRoot) ||
52
+ (process.platform === 'android' ? __cwd : __rootDir)
40
53
 
41
- const handler = new MessageHandler({
42
- onLoad({ wasmModule, wasmMemory }) {
43
- const wasi = new WASI({
44
- version: 'preview1',
45
- env: process.env,
46
- preopens: {
47
- [__rootDir]: __rootDir,
48
- },
49
- });
54
+ handler = new MessageHandler({
55
+ onLoad({ wasmModule, wasmMemory }) {
56
+ const wasi = new WASI({
57
+ version: 'preview1',
58
+ env: process.env,
59
+ preopens: {
60
+ [__rootDir]: __hostRoot,
61
+ [__hostRoot]: __hostRoot,
62
+ },
63
+ })
50
64
 
51
- return instantiateNapiModuleSync(wasmModule, {
52
- childThread: true,
53
- wasi,
54
- context: emnapiContext,
55
- // The wasm links a "basic" emnapi archive (no C async-work /
56
- // threadsafe-function implementations), so every thread that
57
- // instantiates it must provide the JavaScript implementations
58
- // through the emnapi plugins.
59
- plugins: [emnapiAsyncWorkPlugin, emnapiTSFNPlugin],
60
- overwriteImports(importObject) {
61
- importObject.env = {
62
- ...importObject.env,
63
- ...importObject.napi,
64
- ...importObject.emnapi,
65
- memory: wasmMemory
66
- };
67
- },
68
- });
69
- },
70
- });
65
+ return instantiateNapiModuleSync(wasmModule, {
66
+ childThread: true,
67
+ wasi,
68
+ context: emnapiContext,
69
+ // The wasm links a "basic" emnapi archive (no C async-work /
70
+ // threadsafe-function implementations), so every thread that
71
+ // instantiates it must provide the JavaScript implementations
72
+ // through the emnapi plugins.
73
+ plugins: [emnapiAsyncWorkPlugin, emnapiTSFNPlugin],
74
+ overwriteImports(importObject) {
75
+ importObject.env = {
76
+ ...importObject.env,
77
+ ...importObject.napi,
78
+ ...importObject.emnapi,
79
+ memory: wasmMemory,
80
+ }
81
+ },
82
+ })
83
+ },
84
+ })
85
+ } catch (error) {
86
+ if (workerData && workerData.crashFlag instanceof Int32Array) {
87
+ __raiseWasiThreadCrashFlags(error)
88
+ }
89
+ throw error
90
+ }
91
+
92
+ // Tell the loader thread synchronously that this wasm thread died, so its 'exit'
93
+ // teardown does not re-enter wasm and wait on this thread forever. emnapi calls
94
+ // beforeReportError (which runs emnapi_thread_crashed, so the main thread may
95
+ // throw 'unwind' and start exiting) before the 'error' event is even posted.
96
+ // A loader that predates the flag passes none.
97
+ if (workerData && workerData.crashFlag instanceof Int32Array) {
98
+ const __beforeReportError = handler.beforeReportError
99
+ handler.beforeReportError = function (...args) {
100
+ if (!__raiseWasiThreadCrashFlags(args[0])) {
101
+ // No view (the thread was spawned before the loader read the address,
102
+ // say): the export stores into the same word, but only a worker that
103
+ // loaded has an instance to call it on.
104
+ try {
105
+ const __napiThreadCrashed = this.instance?.exports?.napi_wasm_thread_crashed
106
+ if (typeof __napiThreadCrashed === 'function') {
107
+ __napiThreadCrashed()
108
+ }
109
+ } catch {}
110
+ }
111
+ return __beforeReportError.apply(this, args)
112
+ }
113
+ }
114
+
115
+ // The error goes into the shared crash report first. The 'error' event that
116
+ // would carry it is dropped when the loader terminates this worker before the
117
+ // event is sent, so the report is the only copy the loader can count on.
118
+ // Layout: three Int32 words — state (0 empty, 1 writing, 2 written), byte
119
+ // length, threadId — then the UTF-8 JSON of { name, message, stack }. Only the
120
+ // first worker to crash writes it, and it is complete before the flag is raised.
121
+ //
122
+ // Then the loader's flag, then the addon's. The loader thread may already be
123
+ // inside wasm, in a cleanup call that waits on this thread; the addon's flag is
124
+ // what those waits check between short slices. It is a word in the shared wasm
125
+ // memory (the loader passes a view of it, see napi_wasm_thread_crash_flag_address),
126
+ // so no instance is needed: a worker that failed while loading raises it too.
127
+ // Once it is up the waits trap, and the loader, whose flag is already up, turns
128
+ // the throw into its crash rejection. Returns whether the addon's flag was raised.
129
+ function __raiseWasiThreadCrashFlags(error) {
130
+ try {
131
+ __writeCrashReport(workerData.crashReport, error)
132
+ } catch {}
133
+ try {
134
+ Atomics.store(workerData.crashFlag, 0, 1)
135
+ } catch {}
136
+ const addonCrashFlag = workerData.addonCrashFlag
137
+ if (!(addonCrashFlag instanceof Int32Array)) {
138
+ return false
139
+ }
140
+ try {
141
+ Atomics.store(addonCrashFlag, 0, 1)
142
+ return true
143
+ } catch {
144
+ return false
145
+ }
146
+ }
147
+
148
+ function __writeCrashReport(report, error) {
149
+ if (!(report instanceof SharedArrayBuffer) || report.byteLength <= 12) {
150
+ return
151
+ }
152
+ const header = new Int32Array(report, 0, 3)
153
+ if (Atomics.compareExchange(header, 0, 0, 1) !== 0) {
154
+ return
155
+ }
156
+ let length = 0
157
+ try {
158
+ const body = new Uint8Array(report, 12)
159
+ const isObject =
160
+ error !== null && (typeof error === 'object' || typeof error === 'function')
161
+ const name = isObject && typeof error.name === 'string' ? error.name : 'Error'
162
+ const message = isObject && typeof error.message === 'string'
163
+ ? error.message
164
+ : String(error)
165
+ const stack = isObject && typeof error.stack === 'string' ? error.stack : undefined
166
+ const encoder = new TextEncoder()
167
+ let bytes = encoder.encode(JSON.stringify({ name, message, stack }))
168
+ if (bytes.length > body.length) {
169
+ bytes = encoder.encode(
170
+ JSON.stringify({ name, message: message.slice(0, body.length >> 3) }),
171
+ )
172
+ }
173
+ if (bytes.length <= body.length) {
174
+ body.set(bytes)
175
+ length = bytes.length
176
+ }
177
+ Atomics.store(header, 2, threadId)
178
+ } finally {
179
+ Atomics.store(header, 1, length)
180
+ Atomics.store(header, 0, 2)
181
+ }
182
+ }
71
183
 
72
184
  globalThis.onmessage = function (e) {
73
- handler.handle(e);
74
- };
185
+ handler.handle(e)
186
+ }