@medicomind/rolldown-compression-wasm32-wasi 2.1.3 → 2.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medicomind/rolldown-compression-wasm32-wasi",
3
- "version": "2.1.3",
3
+ "version": "2.1.6",
4
4
  "cpu": [
5
5
  "wasm32"
6
6
  ],
@@ -37,7 +37,7 @@
37
37
  "browser": "rolldown-compression.wasi-browser.js",
38
38
  "dependencies": {
39
39
  "@napi-rs/wasm-runtime": "^1.1.6",
40
- "@emnapi/core": "1.11.2",
41
- "@emnapi/runtime": "1.11.2"
40
+ "@emnapi/core": "^1.11.2",
41
+ "@emnapi/runtime": "^1.11.2"
42
42
  }
43
43
  }
@@ -1,9 +1,11 @@
1
1
  import {
2
+ emnapiAsyncWorkPlugin as __emnapiAsyncWorkPlugin,
3
+ emnapiTSFNPlugin as __emnapiTSFNPlugin,
2
4
  createOnMessage as __wasmCreateOnMessageForFsProxy,
3
- getDefaultContext as __emnapiGetDefaultContext,
4
5
  instantiateNapiModuleSync as __emnapiInstantiateNapiModuleSync,
5
6
  WASI as __WASI,
6
7
  } from '@napi-rs/wasm-runtime'
8
+ import { createContext as __emnapiCreateContext } from '@emnapi/runtime'
7
9
 
8
10
 
9
11
 
@@ -12,8 +14,18 @@ const __wasi = new __WASI({
12
14
  })
13
15
 
14
16
  const __wasmUrl = new URL('./rolldown-compression.wasm32-wasi.wasm', import.meta.url).href
15
- const __emnapiContext = __emnapiGetDefaultContext()
16
-
17
+ const __wasmResponse = await globalThis.fetch(__wasmUrl)
18
+ if (!__wasmResponse.ok) {
19
+ throw new Error(
20
+ 'Failed to fetch WASI module ' +
21
+ __wasmUrl +
22
+ ': ' +
23
+ __wasmResponse.status +
24
+ ' ' +
25
+ (__wasmResponse.statusText || 'Unknown Status'),
26
+ )
27
+ }
28
+ const __wasmFile = await __wasmResponse.arrayBuffer()
17
29
 
18
30
  const __sharedMemory = new WebAssembly.Memory({
19
31
  initial: 4000,
@@ -21,40 +33,313 @@ const __sharedMemory = new WebAssembly.Memory({
21
33
  shared: true,
22
34
  })
23
35
 
24
- const __wasmFile = await fetch(__wasmUrl).then((res) => res.arrayBuffer())
25
-
26
- const {
27
- instance: __napiInstance,
28
- module: __wasiModule,
29
- napiModule: __napiModule,
30
- } = __emnapiInstantiateNapiModuleSync(__wasmFile, {
31
- context: __emnapiContext,
32
- asyncWorkPoolSize: 4,
33
- wasi: __wasi,
34
- onCreateWorker() {
35
- const worker = new Worker(new URL('@medicomind/rolldown-compression-wasm32-wasi/wasi-worker-browser.mjs', import.meta.url), {
36
- type: 'module',
37
- })
38
-
39
-
40
- return worker
41
- },
42
- overwriteImports(importObject) {
43
- importObject.env = {
44
- ...importObject.env,
45
- ...importObject.napi,
46
- ...importObject.emnapi,
47
- memory: __sharedMemory,
48
- }
49
- return importObject
50
- },
51
- beforeInit({ instance }) {
52
- for (const name of Object.keys(instance.exports)) {
53
- if (name.startsWith('__napi_register__')) {
54
- instance.exports[name]()
36
+ let __emnapiContext
37
+
38
+ const __wasiDisposeSymbol = Symbol.for('napi.rs.wasi.dispose')
39
+ const __wasiWorkers = new Set()
40
+ let __napiInstance
41
+ let __emnapiContextDestroyed = false
42
+ let __emnapiContextDestroyPromise
43
+ let __emnapiWasmEnvCleanupPrepared = false
44
+ let __wasiDisposed = false
45
+ let __wasiDisposePromise
46
+ let __completeWasiDisposal = function() {}
47
+
48
+ function __isThenable(value) {
49
+ return (
50
+ value !== null &&
51
+ (typeof value === 'object' || typeof value === 'function') &&
52
+ typeof value.then === 'function'
53
+ )
54
+ }
55
+
56
+ function __createCleanupError(errors, message) {
57
+ if (errors.length === 1) {
58
+ return errors[0]
59
+ }
60
+ const __AggregateError = globalThis.AggregateError
61
+ if (typeof __AggregateError === 'function') {
62
+ return new __AggregateError(errors, message)
63
+ }
64
+ const error = new Error(message)
65
+ error.errors = errors
66
+ return error
67
+ }
68
+
69
+ function __attachCleanupErrors(error, cleanupErrors) {
70
+ if (cleanupErrors.length === 0) {
71
+ return error
72
+ }
73
+ const cleanupError = __createCleanupError(
74
+ cleanupErrors,
75
+ 'WASI binding cleanup failed',
76
+ )
77
+ try {
78
+ if (
79
+ error &&
80
+ (typeof error === 'object' || typeof error === 'function')
81
+ ) {
82
+ if (error.cause === undefined) {
83
+ error.cause = cleanupError
84
+ if (error.cause === cleanupError) {
85
+ return error
86
+ }
87
+ }
88
+ if (Array.isArray(error.cleanupErrors)) {
89
+ error.cleanupErrors.push(cleanupError)
90
+ return error
91
+ } else {
92
+ const attachedCleanupErrors = [cleanupError]
93
+ error.cleanupErrors = attachedCleanupErrors
94
+ if (error.cleanupErrors === attachedCleanupErrors) {
95
+ return error
96
+ }
55
97
  }
56
98
  }
57
- },
58
- })
99
+ } catch {}
100
+ const aggregate = __createCleanupError(
101
+ [error, cleanupError],
102
+ 'WASI binding initialization and cleanup failed',
103
+ )
104
+ try {
105
+ aggregate.cause = error
106
+ } catch {}
107
+ return aggregate
108
+ }
109
+
110
+ function __prepareWasmEnvCleanup() {
111
+ if (__emnapiWasmEnvCleanupPrepared) {
112
+ return
113
+ }
114
+ const prepare = __napiInstance?.exports?.napi_prepare_wasm_env_cleanup
115
+ if (typeof prepare === 'function') {
116
+ prepare()
117
+ }
118
+ __emnapiWasmEnvCleanupPrepared = true
119
+ }
120
+
121
+ function __destroyEmnapiContext() {
122
+ if (__emnapiContextDestroyed || __emnapiContext === undefined) {
123
+ __emnapiContextDestroyed = true
124
+ return
125
+ }
126
+ if (__emnapiContextDestroyPromise) {
127
+ return __emnapiContextDestroyPromise
128
+ }
129
+
130
+ __prepareWasmEnvCleanup()
131
+ const result = __emnapiContext.destroy()
132
+ if (!__isThenable(result)) {
133
+ __emnapiContextDestroyed = true
134
+ return
135
+ }
136
+
137
+ const destroyPromise = Promise.resolve(result).then(
138
+ (value) => {
139
+ __emnapiContextDestroyed = true
140
+ return value
141
+ },
142
+ (error) => {
143
+ __emnapiContextDestroyPromise = undefined
144
+ throw error
145
+ },
146
+ )
147
+ __emnapiContextDestroyPromise = destroyPromise
148
+ return destroyPromise
149
+ }
150
+
151
+ function __terminateWasiWorkers() {
152
+ const cleanupErrors = []
153
+ const pending = []
154
+
155
+ for (const worker of __wasiWorkers) {
156
+ let result
157
+ try {
158
+ result = worker.terminate()
159
+ } catch (error) {
160
+ cleanupErrors.push(error)
161
+ continue
162
+ }
163
+ if (__isThenable(result)) {
164
+ pending.push(
165
+ Promise.resolve(result).then(
166
+ () => {
167
+ __wasiWorkers.delete(worker)
168
+ },
169
+ (error) => {
170
+ cleanupErrors.push(error)
171
+ },
172
+ ),
173
+ )
174
+ } else {
175
+ __wasiWorkers.delete(worker)
176
+ }
177
+ }
178
+
179
+ const finish = () => {
180
+ if (cleanupErrors.length > 0) {
181
+ throw __createCleanupError(
182
+ cleanupErrors,
183
+ 'Failed to terminate WASI workers',
184
+ )
185
+ }
186
+ }
187
+ return pending.length > 0 ? Promise.all(pending).then(finish) : finish()
188
+ }
189
+
190
+ function __finishWasiDisposal() {
191
+ const workerResult = __terminateWasiWorkers()
192
+ if (__isThenable(workerResult)) {
193
+ return Promise.resolve(workerResult).then(__completeWasiDisposal)
194
+ }
195
+ return __completeWasiDisposal()
196
+ }
197
+
198
+ function __startWasiDisposal() {
199
+ const destroyResult = __destroyEmnapiContext()
200
+ if (__isThenable(destroyResult)) {
201
+ return Promise.resolve(destroyResult).then(__finishWasiDisposal)
202
+ }
203
+ return __finishWasiDisposal()
204
+ }
205
+
206
+ /**
207
+ * Disposes this generated WASI binding.
208
+ *
209
+ * Access this function with:
210
+ * binding[Symbol.for('napi.rs.wasi.dispose')]()
211
+ */
212
+ function __disposeWasiBinding() {
213
+ if (__wasiDisposePromise) {
214
+ return __wasiDisposePromise
215
+ }
216
+ if (__wasiDisposed) {
217
+ return Promise.resolve()
218
+ }
219
+
220
+ let resolveDispose
221
+ let rejectDispose
222
+ const disposePromise = new Promise((resolve, reject) => {
223
+ resolveDispose = resolve
224
+ rejectDispose = reject
225
+ })
226
+ __wasiDisposePromise = disposePromise
227
+
228
+ let result
229
+ try {
230
+ result = __startWasiDisposal()
231
+ } catch (error) {
232
+ __wasiDisposePromise = undefined
233
+ rejectDispose(error)
234
+ return disposePromise
235
+ }
236
+
237
+ Promise.resolve(result).then(
238
+ (value) => {
239
+ __wasiDisposed = true
240
+ resolveDispose(value)
241
+ },
242
+ (error) => {
243
+ __wasiDisposePromise = undefined
244
+ rejectDispose(error)
245
+ },
246
+ )
247
+ return disposePromise
248
+ }
249
+
250
+ function __publishWasiDispose(exports) {
251
+ Object.defineProperty(exports, __wasiDisposeSymbol, {
252
+ configurable: false,
253
+ enumerable: false,
254
+ value: __disposeWasiBinding,
255
+ writable: false,
256
+ })
257
+ }
258
+
259
+ function __finishWasiInitializationRollback(cleanupErrors) {
260
+ let workerResult
261
+ try {
262
+ workerResult = __terminateWasiWorkers()
263
+ } catch (cleanupError) {
264
+ cleanupErrors.push(cleanupError)
265
+ return cleanupErrors
266
+ }
267
+ if (__isThenable(workerResult)) {
268
+ return Promise.resolve(workerResult)
269
+ .catch((cleanupError) => {
270
+ cleanupErrors.push(cleanupError)
271
+ })
272
+ .then(() => cleanupErrors)
273
+ }
274
+ return cleanupErrors
275
+ }
276
+
277
+ function __rollbackWasiInitialization() {
278
+ const cleanupErrors = []
279
+ let destroyResult
280
+ try {
281
+ destroyResult = __destroyEmnapiContext()
282
+ } catch (cleanupError) {
283
+ cleanupErrors.push(cleanupError)
284
+ return __finishWasiInitializationRollback(cleanupErrors)
285
+ }
286
+ if (__isThenable(destroyResult)) {
287
+ return Promise.resolve(destroyResult)
288
+ .catch((cleanupError) => {
289
+ cleanupErrors.push(cleanupError)
290
+ })
291
+ .then(() => __finishWasiInitializationRollback(cleanupErrors))
292
+ }
293
+ return __finishWasiInitializationRollback(cleanupErrors)
294
+ }
295
+
296
+ let __wasiModule
297
+ let __napiModule
298
+
299
+ try {
300
+ __emnapiContext = __emnapiCreateContext({ autoDestroy: false })
301
+ __emnapiContext.suppressDestroy()
302
+
303
+ ;({
304
+ instance: __napiInstance,
305
+ module: __wasiModule,
306
+ napiModule: __napiModule,
307
+ } = __emnapiInstantiateNapiModuleSync(__wasmFile, {
308
+ context: __emnapiContext,
309
+ asyncWorkPoolSize: 4,
310
+ plugins: [__emnapiAsyncWorkPlugin, __emnapiTSFNPlugin],
311
+ wasi: __wasi,
312
+ onCreateWorker() {
313
+ const worker = new Worker(new URL('@medicomind/rolldown-compression-wasm32-wasi/wasi-worker-browser.mjs', import.meta.url), {
314
+ type: 'module',
315
+ })
316
+ __wasiWorkers.add(worker)
317
+
318
+
319
+ return worker
320
+ },
321
+ overwriteImports(importObject) {
322
+ importObject.env = {
323
+ ...importObject.env,
324
+ ...importObject.napi,
325
+ ...importObject.emnapi,
326
+ memory: __sharedMemory,
327
+ }
328
+ return importObject
329
+ },
330
+ beforeInit({ instance }) {
331
+ __napiInstance = instance
332
+ for (const name of Object.keys(instance.exports)) {
333
+ if (name.startsWith('__napi_register__')) {
334
+ instance.exports[name]()
335
+ }
336
+ }
337
+ },
338
+ }))
339
+ __publishWasiDispose(__napiModule.exports)
340
+ } catch (error) {
341
+ const cleanupErrors = await __rollbackWasiInitialization()
342
+ throw __attachCleanupErrors(error, cleanupErrors)
343
+ }
59
344
  export default __napiModule.exports
60
345
  export const compressBuffers = __napiModule.exports.compressBuffers
@@ -1,3 +1,4 @@
1
+ // napi-rs-artifact-metadata:{"version":2,"rootEntry":"index.js","exports":["compressBuffers"],"managedRootEntries":["browser.js","index.js","rolldown-compression.wasm","rolldown-compression.debug.wasm"]}
1
2
  /* eslint-disable */
2
3
  /* prettier-ignore */
3
4
 
@@ -9,10 +10,101 @@ const { WASI: __nodeWASI } = require('node:wasi')
9
10
  const { Worker } = require('node:worker_threads')
10
11
 
11
12
  const {
13
+ emnapiAsyncWorkPlugin: __emnapiAsyncWorkPlugin,
14
+ emnapiTSFNPlugin: __emnapiTSFNPlugin,
12
15
  createOnMessage: __wasmCreateOnMessageForFsProxy,
13
- getDefaultContext: __emnapiGetDefaultContext,
14
16
  instantiateNapiModuleSync: __emnapiInstantiateNapiModuleSync,
15
17
  } = require('@napi-rs/wasm-runtime')
18
+ const { createContext: __emnapiCreateContext } = require('@emnapi/runtime')
19
+
20
+ function __getWasiWorkerExecArgv() {
21
+ const __workerExecArgv = []
22
+ for (let __index = 0; __index < process.execArgv.length; __index += 1) {
23
+ const __arg = process.execArgv[__index]
24
+ if (
25
+ __arg === '--input-type' ||
26
+ __arg === '--eval' ||
27
+ __arg === '-e' ||
28
+ __arg === '--print' ||
29
+ __arg === '-p'
30
+ ) {
31
+ __index += 1
32
+ continue
33
+ }
34
+ if (
35
+ __arg.startsWith('--input-type=') ||
36
+ __arg.startsWith('--eval=') ||
37
+ __arg.startsWith('--print=')
38
+ ) {
39
+ continue
40
+ }
41
+ __workerExecArgv.push(__arg)
42
+ }
43
+ return __workerExecArgv
44
+ }
45
+
46
+ function __isInvalidWasiWorkerExecArgv(errorMessage, argument) {
47
+ const __equalsIndex = argument.indexOf('=')
48
+ const __argumentName =
49
+ __equalsIndex === -1 ? argument : argument.slice(0, __equalsIndex)
50
+ return (
51
+ errorMessage.includes(': ' + __argumentName + ',') ||
52
+ errorMessage.includes(': ' + __argumentName + '=') ||
53
+ errorMessage.endsWith(': ' + __argumentName) ||
54
+ errorMessage.includes(', ' + __argumentName + ',') ||
55
+ errorMessage.includes(', ' + __argumentName + '=') ||
56
+ errorMessage.endsWith(', ' + __argumentName)
57
+ )
58
+ }
59
+
60
+ function __removeInvalidWasiWorkerExecArgv(execArgv, error) {
61
+ if (typeof error.message !== 'string') {
62
+ return
63
+ }
64
+ const __workerExecArgv = []
65
+ let __removed = false
66
+ for (let __index = 0; __index < execArgv.length; __index += 1) {
67
+ const __arg = execArgv[__index]
68
+ if (
69
+ __arg.startsWith('-') &&
70
+ __isInvalidWasiWorkerExecArgv(error.message, __arg)
71
+ ) {
72
+ __removed = true
73
+ if (
74
+ !__arg.includes('=') &&
75
+ __index + 1 < execArgv.length &&
76
+ !execArgv[__index + 1].startsWith('-')
77
+ ) {
78
+ __index += 1
79
+ }
80
+ continue
81
+ }
82
+ __workerExecArgv.push(__arg)
83
+ }
84
+ return __removed ? __workerExecArgv : undefined
85
+ }
86
+
87
+ function __createWasiWorker(filename) {
88
+ let __workerExecArgv = __getWasiWorkerExecArgv()
89
+ while (true) {
90
+ try {
91
+ return new Worker(filename, {
92
+ env: process.env,
93
+ execArgv: __workerExecArgv,
94
+ })
95
+ } catch (error) {
96
+ if (!error || error.code !== 'ERR_WORKER_INVALID_EXEC_ARGV') {
97
+ throw error
98
+ }
99
+ const __nextWorkerExecArgv =
100
+ __removeInvalidWasiWorkerExecArgv(__workerExecArgv, error)
101
+ if (!__nextWorkerExecArgv) {
102
+ throw error
103
+ }
104
+ __workerExecArgv = __nextWorkerExecArgv
105
+ }
106
+ }
107
+ }
16
108
 
17
109
  const __rootDir = __nodePath.parse(process.cwd()).root
18
110
 
@@ -24,8 +116,6 @@ const __wasi = new __nodeWASI({
24
116
  }
25
117
  })
26
118
 
27
- const __emnapiContext = __emnapiGetDefaultContext()
28
-
29
119
  const __sharedMemory = new WebAssembly.Memory({
30
120
  initial: 4000,
31
121
  maximum: 65536,
@@ -38,74 +128,529 @@ const __wasmDebugFilePath = __nodePath.join(__dirname, 'rolldown-compression.was
38
128
  if (__nodeFs.existsSync(__wasmDebugFilePath)) {
39
129
  __wasmFilePath = __wasmDebugFilePath
40
130
  } else if (!__nodeFs.existsSync(__wasmFilePath)) {
131
+ const __wasiPackageEntry = require.resolve('@medicomind/rolldown-compression-wasm32-wasi')
132
+ const __packagedWasmFilePath = __nodePath.join(
133
+ __nodePath.dirname(__wasiPackageEntry),
134
+ 'rolldown-compression.wasm32-wasi.wasm',
135
+ )
136
+ if (!__nodeFs.existsSync(__packagedWasmFilePath)) {
137
+ throw new Error(
138
+ '@medicomind/rolldown-compression-wasm32-wasi is installed but is missing rolldown-compression.wasm32-wasi.wasm.',
139
+ )
140
+ }
141
+ __wasmFilePath = __packagedWasmFilePath
142
+ }
143
+
144
+ const __wasmFile = __nodeFs.readFileSync(__wasmFilePath)
145
+ let __emnapiContext
146
+
147
+ const __wasiDisposeSymbol = Symbol.for('napi.rs.wasi.dispose')
148
+ const __wasiWorkers = new Set()
149
+ let __napiInstance
150
+ let __emnapiContextDestroyed = false
151
+ let __emnapiContextDestroyPromise
152
+ let __emnapiWasmEnvCleanupPrepared = false
153
+ let __wasiDisposed = false
154
+ let __wasiDisposePromise
155
+ let __completeWasiDisposal = function() {}
156
+
157
+ function __isThenable(value) {
158
+ return (
159
+ value !== null &&
160
+ (typeof value === 'object' || typeof value === 'function') &&
161
+ typeof value.then === 'function'
162
+ )
163
+ }
164
+
165
+ function __createCleanupError(errors, message) {
166
+ if (errors.length === 1) {
167
+ return errors[0]
168
+ }
169
+ const __AggregateError = globalThis.AggregateError
170
+ if (typeof __AggregateError === 'function') {
171
+ return new __AggregateError(errors, message)
172
+ }
173
+ const error = new Error(message)
174
+ error.errors = errors
175
+ return error
176
+ }
177
+
178
+ function __attachCleanupErrors(error, cleanupErrors) {
179
+ if (cleanupErrors.length === 0) {
180
+ return error
181
+ }
182
+ const cleanupError = __createCleanupError(
183
+ cleanupErrors,
184
+ 'WASI binding cleanup failed',
185
+ )
41
186
  try {
42
- __wasmFilePath = require.resolve('@medicomind/rolldown-compression-wasm32-wasi/rolldown-compression.wasm32-wasi.wasm')
43
- } catch {
44
- throw new Error('Cannot find rolldown-compression.wasm32-wasi.wasm file, and @medicomind/rolldown-compression-wasm32-wasi package is not installed.')
187
+ if (
188
+ error &&
189
+ (typeof error === 'object' || typeof error === 'function')
190
+ ) {
191
+ if (error.cause === undefined) {
192
+ error.cause = cleanupError
193
+ if (error.cause === cleanupError) {
194
+ return error
195
+ }
196
+ }
197
+ if (Array.isArray(error.cleanupErrors)) {
198
+ error.cleanupErrors.push(cleanupError)
199
+ return error
200
+ } else {
201
+ const attachedCleanupErrors = [cleanupError]
202
+ error.cleanupErrors = attachedCleanupErrors
203
+ if (error.cleanupErrors === attachedCleanupErrors) {
204
+ return error
205
+ }
206
+ }
207
+ }
208
+ } catch {}
209
+ const aggregate = __createCleanupError(
210
+ [error, cleanupError],
211
+ 'WASI binding initialization and cleanup failed',
212
+ )
213
+ try {
214
+ aggregate.cause = error
215
+ } catch {}
216
+ return aggregate
217
+ }
218
+
219
+ function __prepareWasmEnvCleanup() {
220
+ if (__emnapiWasmEnvCleanupPrepared) {
221
+ return
45
222
  }
223
+ const prepare = __napiInstance?.exports?.napi_prepare_wasm_env_cleanup
224
+ if (typeof prepare === 'function') {
225
+ prepare()
226
+ }
227
+ __emnapiWasmEnvCleanupPrepared = true
46
228
  }
47
229
 
48
- const { instance: __napiInstance, module: __wasiModule, napiModule: __napiModule } = __emnapiInstantiateNapiModuleSync(__nodeFs.readFileSync(__wasmFilePath), {
49
- context: __emnapiContext,
50
- asyncWorkPoolSize: (function() {
51
- const threadsSizeFromEnv = Number(process.env.NAPI_RS_ASYNC_WORK_POOL_SIZE ?? process.env.UV_THREADPOOL_SIZE)
52
- // NaN > 0 is false
53
- if (threadsSizeFromEnv > 0) {
54
- return threadsSizeFromEnv
230
+ function __destroyEmnapiContext() {
231
+ if (__emnapiContextDestroyed || __emnapiContext === undefined) {
232
+ __emnapiContextDestroyed = true
233
+ return
234
+ }
235
+ if (__emnapiContextDestroyPromise) {
236
+ return __emnapiContextDestroyPromise
237
+ }
238
+
239
+ __prepareWasmEnvCleanup()
240
+ const result = __emnapiContext.destroy()
241
+ if (!__isThenable(result)) {
242
+ __emnapiContextDestroyed = true
243
+ return
244
+ }
245
+
246
+ const destroyPromise = Promise.resolve(result).then(
247
+ (value) => {
248
+ __emnapiContextDestroyed = true
249
+ return value
250
+ },
251
+ (error) => {
252
+ __emnapiContextDestroyPromise = undefined
253
+ throw error
254
+ },
255
+ )
256
+ __emnapiContextDestroyPromise = destroyPromise
257
+ return destroyPromise
258
+ }
259
+
260
+ function __terminateWasiWorkers() {
261
+ const cleanupErrors = []
262
+ const pending = []
263
+
264
+ for (const worker of __wasiWorkers) {
265
+ let result
266
+ try {
267
+ result = worker.terminate()
268
+ } catch (error) {
269
+ cleanupErrors.push(error)
270
+ continue
271
+ }
272
+ if (__isThenable(result)) {
273
+ pending.push(
274
+ Promise.resolve(result).then(
275
+ () => {
276
+ __wasiWorkers.delete(worker)
277
+ },
278
+ (error) => {
279
+ cleanupErrors.push(error)
280
+ },
281
+ ),
282
+ )
55
283
  } else {
56
- return 4
284
+ __wasiWorkers.delete(worker)
57
285
  }
58
- })(),
59
- reuseWorker: true,
60
- wasi: __wasi,
61
- onCreateWorker() {
62
- const worker = new Worker(__nodePath.join(__dirname, 'wasi-worker.mjs'), {
63
- env: process.env,
64
- })
65
- worker.onmessage = ({ data }) => {
66
- __wasmCreateOnMessageForFsProxy(__nodeFs)(data)
286
+ }
287
+
288
+ const finish = () => {
289
+ if (cleanupErrors.length > 0) {
290
+ throw __createCleanupError(
291
+ cleanupErrors,
292
+ 'Failed to terminate WASI workers',
293
+ )
67
294
  }
295
+ }
296
+ return pending.length > 0 ? Promise.all(pending).then(finish) : finish()
297
+ }
68
298
 
69
- // The main thread of Node.js waits for all the active handles before exiting.
70
- // But Rust threads are never waited without `thread::join`.
71
- // So here we hack the code of Node.js to prevent the workers from being referenced (active).
72
- // According to https://github.com/nodejs/node/blob/19e0d472728c79d418b74bddff588bea70a403d0/lib/internal/worker.js#L415,
73
- // a worker is consist of two handles: kPublicPort and kHandle.
74
- {
75
- const kPublicPort = Object.getOwnPropertySymbols(worker).find(s =>
76
- s.toString().includes("kPublicPort")
77
- );
78
- if (kPublicPort) {
79
- worker[kPublicPort].ref = () => {};
80
- }
299
+ function __finishWasiDisposal() {
300
+ const workerResult = __terminateWasiWorkers()
301
+ if (__isThenable(workerResult)) {
302
+ return Promise.resolve(workerResult).then(__completeWasiDisposal)
303
+ }
304
+ return __completeWasiDisposal()
305
+ }
306
+
307
+ function __startWasiDisposal() {
308
+ const destroyResult = __destroyEmnapiContext()
309
+ if (__isThenable(destroyResult)) {
310
+ return Promise.resolve(destroyResult).then(__finishWasiDisposal)
311
+ }
312
+ return __finishWasiDisposal()
313
+ }
314
+
315
+ /**
316
+ * Disposes this generated WASI binding.
317
+ *
318
+ * Access this function with:
319
+ * binding[Symbol.for('napi.rs.wasi.dispose')]()
320
+ */
321
+ function __disposeWasiBinding() {
322
+ if (__wasiDisposePromise) {
323
+ return __wasiDisposePromise
324
+ }
325
+ if (__wasiDisposed) {
326
+ return Promise.resolve()
327
+ }
328
+
329
+ let resolveDispose
330
+ let rejectDispose
331
+ const disposePromise = new Promise((resolve, reject) => {
332
+ resolveDispose = resolve
333
+ rejectDispose = reject
334
+ })
335
+ __wasiDisposePromise = disposePromise
336
+
337
+ let result
338
+ try {
339
+ result = __startWasiDisposal()
340
+ } catch (error) {
341
+ __wasiDisposePromise = undefined
342
+ rejectDispose(error)
343
+ return disposePromise
344
+ }
345
+
346
+ Promise.resolve(result).then(
347
+ (value) => {
348
+ __wasiDisposed = true
349
+ resolveDispose(value)
350
+ },
351
+ (error) => {
352
+ __wasiDisposePromise = undefined
353
+ rejectDispose(error)
354
+ },
355
+ )
356
+ return disposePromise
357
+ }
81
358
 
82
- const kHandle = Object.getOwnPropertySymbols(worker).find(s =>
83
- s.toString().includes("kHandle")
84
- );
85
- if (kHandle) {
86
- worker[kHandle].ref = () => {};
359
+ function __publishWasiDispose(exports) {
360
+ Object.defineProperty(exports, __wasiDisposeSymbol, {
361
+ configurable: false,
362
+ enumerable: false,
363
+ value: __disposeWasiBinding,
364
+ writable: false,
365
+ })
366
+ }
367
+
368
+ function __finishWasiInitializationRollback(cleanupErrors) {
369
+ let workerResult
370
+ try {
371
+ workerResult = __terminateWasiWorkers()
372
+ } catch (cleanupError) {
373
+ cleanupErrors.push(cleanupError)
374
+ return cleanupErrors
375
+ }
376
+ if (__isThenable(workerResult)) {
377
+ return Promise.resolve(workerResult)
378
+ .catch((cleanupError) => {
379
+ cleanupErrors.push(cleanupError)
380
+ })
381
+ .then(() => cleanupErrors)
382
+ }
383
+ return cleanupErrors
384
+ }
385
+
386
+ function __rollbackWasiInitialization() {
387
+ const cleanupErrors = []
388
+ let destroyResult
389
+ try {
390
+ destroyResult = __destroyEmnapiContext()
391
+ } catch (cleanupError) {
392
+ cleanupErrors.push(cleanupError)
393
+ return __finishWasiInitializationRollback(cleanupErrors)
394
+ }
395
+ if (__isThenable(destroyResult)) {
396
+ return Promise.resolve(destroyResult)
397
+ .catch((cleanupError) => {
398
+ cleanupErrors.push(cleanupError)
399
+ })
400
+ .then(() => __finishWasiInitializationRollback(cleanupErrors))
401
+ }
402
+ return __finishWasiInitializationRollback(cleanupErrors)
403
+ }
404
+
405
+ const __wasiRollbackRegistrySymbol = Symbol.for('napi.rs.wasi.rollback.registry.v1')
406
+ const __wasiRollbackRegistryKey =
407
+ typeof __filename === 'string' ? __filename : __wasmFilePath
408
+
409
+ function __getWasiRollbackRegistry() {
410
+ const existing = process[__wasiRollbackRegistrySymbol]
411
+ if (existing !== undefined) {
412
+ if (!(existing instanceof Map)) {
413
+ throw new TypeError(
414
+ 'The process-wide NAPI-RS WASI rollback registry is invalid',
415
+ )
416
+ }
417
+ return existing
418
+ }
419
+ const registry = new Map()
420
+ Object.defineProperty(process, __wasiRollbackRegistrySymbol, {
421
+ configurable: false,
422
+ enumerable: false,
423
+ value: registry,
424
+ writable: false,
425
+ })
426
+ return registry
427
+ }
428
+
429
+ const __wasiRollbackRegistry = __getWasiRollbackRegistry()
430
+
431
+ function __completeWasiInitializationRollback(record, cleanupErrors) {
432
+ try {
433
+ if (cleanupErrors.length === 0) {
434
+ if (
435
+ __wasiRollbackRegistry.get(__wasiRollbackRegistryKey) === record
436
+ ) {
437
+ __wasiRollbackRegistry.delete(__wasiRollbackRegistryKey)
87
438
  }
439
+ return
440
+ }
441
+ record.error = __attachCleanupErrors(record.error, cleanupErrors)
442
+ } catch (cleanupError) {
443
+ try {
444
+ record.error = __createCleanupError(
445
+ [record.error, cleanupError],
446
+ 'WASI binding initialization and cleanup failed',
447
+ )
448
+ } catch {}
449
+ } finally {
450
+ record.active = false
451
+ record.promise = undefined
452
+ }
453
+ }
454
+
455
+ function __runWasiInitializationRollback(record) {
456
+ if (record.active) {
457
+ return
458
+ }
459
+ record.active = true
460
+
461
+ let rollbackResult
462
+ try {
463
+ rollbackResult = record.rollback()
464
+ } catch (cleanupError) {
465
+ __completeWasiInitializationRollback(record, [cleanupError])
466
+ return
467
+ }
468
+
469
+ if (!__isThenable(rollbackResult)) {
470
+ __completeWasiInitializationRollback(record, rollbackResult)
471
+ return
472
+ }
473
+
474
+ record.promise = Promise.resolve(rollbackResult).then(
475
+ (cleanupErrors) => {
476
+ __completeWasiInitializationRollback(record, cleanupErrors)
477
+ },
478
+ (cleanupError) => {
479
+ __completeWasiInitializationRollback(record, [cleanupError])
480
+ },
481
+ )
482
+ }
483
+
484
+ const __pendingWasiRollback = __wasiRollbackRegistry.get(
485
+ __wasiRollbackRegistryKey,
486
+ )
487
+ if (__pendingWasiRollback !== undefined) {
488
+ __runWasiInitializationRollback(__pendingWasiRollback)
489
+ throw __pendingWasiRollback.error
490
+ }
491
+
492
+ let __wasiModule
493
+ let __napiModule
494
+ let __wasiExitListenerRegistered = false
88
495
 
89
- worker.unref();
496
+ function __removeWasiExitListener() {
497
+ if (
498
+ __wasiExitListenerRegistered &&
499
+ typeof process.removeListener === 'function'
500
+ ) {
501
+ process.removeListener('exit', __disposeWasiBindingAtExit)
502
+ }
503
+ __wasiExitListenerRegistered = false
504
+ }
505
+
506
+ function __disposeWasiBindingAtExit() {
507
+ __wasiExitListenerRegistered = false
508
+ try {
509
+ const result = __disposeWasiBinding()
510
+ if (__isThenable(result)) {
511
+ void Promise.resolve(result).catch(() => {})
90
512
  }
91
- return worker
92
- },
93
- overwriteImports(importObject) {
94
- importObject.env = {
95
- ...importObject.env,
96
- ...importObject.napi,
97
- ...importObject.emnapi,
98
- memory: __sharedMemory,
513
+ } catch {}
514
+ }
515
+
516
+ function __registerWasiExitListener() {
517
+ if (
518
+ !__wasiExitListenerRegistered &&
519
+ typeof process.once === 'function'
520
+ ) {
521
+ process.once('exit', __disposeWasiBindingAtExit)
522
+ __wasiExitListenerRegistered = true
523
+ }
524
+ }
525
+
526
+ __completeWasiDisposal = __removeWasiExitListener
527
+
528
+ function __captureEmnapiAutoDestroyListener() {
529
+ if (
530
+ typeof process.prependListener !== 'function' ||
531
+ typeof process.removeListener !== 'function'
532
+ ) {
533
+ return
534
+ }
535
+ let __autoDestroyListener
536
+ const __captureListener = (__event, __listener) => {
537
+ if (__event === 'beforeExit' && __autoDestroyListener === undefined) {
538
+ __autoDestroyListener = __listener
99
539
  }
100
- return importObject
101
- },
102
- beforeInit({ instance }) {
103
- for (const name of Object.keys(instance.exports)) {
104
- if (name.startsWith('__napi_register__')) {
105
- instance.exports[name]()
106
- }
540
+ }
541
+ try {
542
+ // Run before existing newListener hooks so a hook that registers its own
543
+ // beforeExit listener cannot be mistaken for emnapi's registration.
544
+ process.prependListener('newListener', __captureListener)
545
+ } catch {
546
+ return
547
+ }
548
+ return () => {
549
+ try {
550
+ process.removeListener('newListener', __captureListener)
551
+ } catch {}
552
+ if (__autoDestroyListener !== undefined) {
553
+ try {
554
+ process.removeListener('beforeExit', __autoDestroyListener)
555
+ } catch {}
107
556
  }
108
- },
109
- })
557
+ }
558
+ }
559
+
560
+ try {
561
+ const __finishAutoDestroyCapture = __captureEmnapiAutoDestroyListener()
562
+ try {
563
+ __emnapiContext = __emnapiCreateContext({ autoDestroy: false })
564
+ // emnapi 2.x still registers an unconditional once-listener for
565
+ // beforeExit that auto-destroys the context, and suppressDestroy() only
566
+ // neutralizes its callback without removing it. This loader owns cleanup
567
+ // through its 'exit' listener, so emnapi's listener is captured and
568
+ // removed; suppressDestroy() remains the safety net when removal fails.
569
+ __emnapiContext.suppressDestroy()
570
+ } finally {
571
+ // Remove only the exact emnapi callback captured above.
572
+ __finishAutoDestroyCapture?.()
573
+ }
574
+
575
+ ;({
576
+ instance: __napiInstance,
577
+ module: __wasiModule,
578
+ napiModule: __napiModule,
579
+ } = __emnapiInstantiateNapiModuleSync(__wasmFile, {
580
+ context: __emnapiContext,
581
+ asyncWorkPoolSize: (function() {
582
+ const threadsSizeFromEnv = Number(process.env.NAPI_RS_ASYNC_WORK_POOL_SIZE ?? process.env.UV_THREADPOOL_SIZE)
583
+ // NaN > 0 is false
584
+ if (threadsSizeFromEnv > 0) {
585
+ return threadsSizeFromEnv
586
+ } else {
587
+ return 4
588
+ }
589
+ })(),
590
+ reuseWorker: true,
591
+ plugins: [__emnapiAsyncWorkPlugin, __emnapiTSFNPlugin],
592
+ wasi: __wasi,
593
+ onCreateWorker() {
594
+ const worker = __createWasiWorker(__nodePath.join(__dirname, 'wasi-worker.mjs'))
595
+ __wasiWorkers.add(worker)
596
+ worker.onmessage = ({ data }) => {
597
+ __wasmCreateOnMessageForFsProxy(__nodeFs)(data)
598
+ }
599
+
600
+ // The main thread of Node.js waits for all the active handles before exiting.
601
+ // But Rust threads are never waited without `thread::join`.
602
+ // So here we hack the code of Node.js to prevent the workers from being referenced (active).
603
+ // According to https://github.com/nodejs/node/blob/19e0d472728c79d418b74bddff588bea70a403d0/lib/internal/worker.js#L415,
604
+ // a worker is consist of two handles: kPublicPort and kHandle.
605
+ {
606
+ const kPublicPort = Object.getOwnPropertySymbols(worker).find(s =>
607
+ s.toString().includes("kPublicPort")
608
+ );
609
+ if (kPublicPort) {
610
+ worker[kPublicPort].ref = () => {};
611
+ }
612
+
613
+ const kHandle = Object.getOwnPropertySymbols(worker).find(s =>
614
+ s.toString().includes("kHandle")
615
+ );
616
+ if (kHandle) {
617
+ worker[kHandle].ref = () => {};
618
+ }
619
+
620
+ worker.unref();
621
+ }
622
+ return worker
623
+ },
624
+ overwriteImports(importObject) {
625
+ importObject.env = {
626
+ ...importObject.env,
627
+ ...importObject.napi,
628
+ ...importObject.emnapi,
629
+ memory: __sharedMemory,
630
+ }
631
+ return importObject
632
+ },
633
+ beforeInit({ instance }) {
634
+ __napiInstance = instance
635
+ for (const name of Object.keys(instance.exports)) {
636
+ if (name.startsWith('__napi_register__')) {
637
+ instance.exports[name]()
638
+ }
639
+ }
640
+ },
641
+ }))
642
+ __publishWasiDispose(__napiModule.exports)
643
+ __registerWasiExitListener()
644
+ } catch (error) {
645
+ const rollback = {
646
+ active: false,
647
+ error,
648
+ promise: undefined,
649
+ rollback: __rollbackWasiInitialization,
650
+ }
651
+ __wasiRollbackRegistry.set(__wasiRollbackRegistryKey, rollback)
652
+ __runWasiInitializationRollback(rollback)
653
+ throw rollback.error
654
+ }
110
655
  module.exports = __napiModule.exports
111
656
  module.exports.compressBuffers = __napiModule.exports.compressBuffers
@@ -1,4 +1,10 @@
1
- import { instantiateNapiModuleSync, MessageHandler, WASI } from '@napi-rs/wasm-runtime'
1
+ import {
2
+ instantiateNapiModuleSync,
3
+ MessageHandler,
4
+ WASI,
5
+ emnapiAsyncWorkPlugin,
6
+ emnapiTSFNPlugin,
7
+ } from '@napi-rs/wasm-runtime'
2
8
 
3
9
  const handler = new MessageHandler({
4
10
  onLoad({ wasmModule, wasmMemory }) {
@@ -16,6 +22,11 @@ const handler = new MessageHandler({
16
22
  return instantiateNapiModuleSync(wasmModule, {
17
23
  childThread: true,
18
24
  wasi,
25
+ // The wasm links a "basic" emnapi archive (no C async-work /
26
+ // threadsafe-function implementations), so every thread that
27
+ // instantiates it must provide the JavaScript implementations
28
+ // through the emnapi plugins.
29
+ plugins: [emnapiAsyncWorkPlugin, emnapiTSFNPlugin],
19
30
  overwriteImports(importObject) {
20
31
  importObject.env = {
21
32
  ...importObject.env,
package/wasi-worker.mjs CHANGED
@@ -6,7 +6,13 @@ import { parentPort, Worker } from "node:worker_threads";
6
6
 
7
7
  const require = createRequire(import.meta.url);
8
8
 
9
- const { instantiateNapiModuleSync, MessageHandler, getDefaultContext } = require("@napi-rs/wasm-runtime");
9
+ const {
10
+ instantiateNapiModuleSync,
11
+ MessageHandler,
12
+ getDefaultContext,
13
+ emnapiAsyncWorkPlugin,
14
+ emnapiTSFNPlugin,
15
+ } = require("@napi-rs/wasm-runtime");
10
16
 
11
17
  if (parentPort) {
12
18
  parentPort.on("message", (data) => {
@@ -46,6 +52,11 @@ const handler = new MessageHandler({
46
52
  childThread: true,
47
53
  wasi,
48
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],
49
60
  overwriteImports(importObject) {
50
61
  importObject.env = {
51
62
  ...importObject.env,