mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
chore(deps): update dependency @napi-rs/cli to v3.10.7 (#27347)
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---|---|---| | [@napi-rs/cli](https://napi.rs/) ([source](https://redirect.github.com/napi-rs/napi-rs)) | [`3.10.6` → `3.10.7`](https://renovatebot.com/diffs/npm/@napi-rs%2fcli/3.10.6/3.10.7) |  |  |  |  | --- ### Release Notes <details> <summary>napi-rs/napi-rs (@​napi-rs/cli)</summary> ### [`v3.10.7`](https://redirect.github.com/napi-rs/napi-rs/releases/tag/%40napi-rs/cli%403.10.7) [Compare Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.10.6...@napi-rs/cli@3.10.7) #### What's Changed - feat(cli, async-runtime): preload the threaded WASI worker pool from the loader by [@​Brooooooklyn](https://redirect.github.com/Brooooooklyn) in [#​3558](https://redirect.github.com/napi-rs/napi-rs/pull/3558) #### New Contributors - [@​colinhacks](https://redirect.github.com/colinhacks) made their first contribution in [#​3560](https://redirect.github.com/napi-rs/napi-rs/pull/3560) **Full Changelog**: <https://github.com/napi-rs/napi-rs/compare/napi-async-runtime-v0.2.4...@napi-rs/cli@3.10.7> </details> --- ### Configuration 📅 **Schedule**: (in timezone Asia/Shanghai) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/oxc-project/oxc). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMjUuMSIsInVwZGF0ZWRJblZlciI6IjQ0LjEzNC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119--> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Cameron Clark <cameron.clark@hey.com>
This commit is contained in:
1 parent
f9d08677da
commit
43212aa645
8 files changed
+984
-24
No files matched your search
+161
-1
@@ -222,6 +222,34 @@ function __hasWasiThreadCrashed() {
|
||||
}
|
||||
|
||||
let __wasiThreadCrashDisposePromise
|
||||
// Raised by the crash disposal right before it terminates the workers. See
|
||||
// `__wasiSetImmediate`.
|
||||
let __wasiReentryClosed = false
|
||||
|
||||
/**
|
||||
* emnapi's `features.setImmediate` for this binding's context. emnapi defers
|
||||
* its calls back into wasm through it: `_emnapi_set_immediate` (libuv handle
|
||||
* closes, threadsafe-function finalizers), threadsafe-function dispatch
|
||||
* (`async-send`) and the finalizer queue. One queued before a crash disposal
|
||||
* terminates the workers still runs after it, and a worker terminated while it
|
||||
* held a lock in the wasm heap (napi's heap-sync allocator lock spins and never
|
||||
* gives up) leaves that call spinning on this thread for good. The binding is
|
||||
* unusable after a crash disposal, so those calls are dropped.
|
||||
*
|
||||
* Known residual, still able to enter wasm after a crash disposal because emnapi
|
||||
* offers no hook for them: the `FinalizationRegistry` callbacks that free
|
||||
* external memory when GC collects a value (`_free`, the shared-buffer meta
|
||||
* release), threadsafe-function dispatch of `async-send` type 1 and
|
||||
* `_emnapi_next_tick` (both `Promise.resolve().then`), and every deferred
|
||||
* call under emnapi 1.x, whose `createContext` ignores `features`.
|
||||
*/
|
||||
function __wasiSetImmediate(callback) {
|
||||
return setImmediate(function () {
|
||||
if (!__wasiReentryClosed) {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the first error a pool worker reported, with the worker's id. Called
|
||||
@@ -371,6 +399,8 @@ function __disposeWasiBindingAfterThreadCrash() {
|
||||
return __wasiThreadCrashDisposePromise
|
||||
}
|
||||
__releaseEmnapiWaitingRequestHandle()
|
||||
// No call into wasm after this: see `__wasiSetImmediate`.
|
||||
__wasiReentryClosed = true
|
||||
let workerResult
|
||||
try {
|
||||
workerResult = __terminateWasiWorkers()
|
||||
@@ -455,6 +485,130 @@ function __rollbackWasiInitializationAfterThreadCrash() {
|
||||
return [crashError]
|
||||
}
|
||||
|
||||
const __wasiThreadPoolReconcileSymbol = Symbol.for('napi.rs.wasi.reconcileThreadPool')
|
||||
|
||||
/**
|
||||
* Takes a Worker out of emnapi's reuse pool, if it is still there. emnapi
|
||||
* terminates a pooled Worker that failed to load but (up to
|
||||
* @emnapi/wasi-threads 2.1.0) leaves it in the pool, where the next thread
|
||||
* spawn would pop it. A newer emnapi removes it itself, so this is a no-op
|
||||
* then.
|
||||
*/
|
||||
function __removeWasiPoolWorker(manager, worker) {
|
||||
const index = manager.unusedWorkers.indexOf(worker)
|
||||
if (index !== -1) {
|
||||
manager.unusedWorkers.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a Worker the thread manager just terminated out of `__wasiWorkers`
|
||||
* once it has exited, not before. `terminateWorker` only starts Node's
|
||||
* asynchronous `worker.terminate()` and drops its promise, so a disposal that
|
||||
* begins before the exit has to find this Worker in the set and wait for it
|
||||
* like any other. A second `terminate()` settles when the Worker has exited,
|
||||
* the way `__terminateWasiWorkers` waits.
|
||||
*/
|
||||
function __untrackWasiWorkerOnExit(worker) {
|
||||
const terminated = worker.terminate()
|
||||
if (__isThenable(terminated)) {
|
||||
Promise.resolve(terminated).then(
|
||||
() => {
|
||||
__wasiWorkers.delete(worker)
|
||||
},
|
||||
// Left tracked: disposal terminates it again and reports the error.
|
||||
() => {},
|
||||
)
|
||||
} else {
|
||||
__wasiWorkers.delete(worker)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches emnapi's idle reuse pool to the addon's configured MultiThread worker
|
||||
* count, which the addon exports as `napi_wasm_runtime_pool_workers`
|
||||
* (napi-async-runtime; 0 under CurrentThread). `reuseWorker: true` starts the
|
||||
* pool empty, so without this every pool thread the first async call spawns
|
||||
* boots a Worker and loads the wasm into it first. Here each missing Worker is
|
||||
* created and starts loading now; a spawn later pops one that is already
|
||||
* booting. Idle Workers above the count are terminated, last in first out,
|
||||
* the way a spawn takes them. A Worker a spawn already took is not in the pool
|
||||
* and is left alone.
|
||||
*
|
||||
* Runs once after a successful load and, when the loader wraps it, after every
|
||||
* successful `configureAsyncRuntime`. Also reachable as
|
||||
* binding[Symbol.for('napi.rs.wasi.reconcileThreadPool')]().
|
||||
* It never throws and never waits on a Worker: a Worker whose load fails is
|
||||
* dropped from the pool when its load rejects. Nothing at all happens after a
|
||||
* thread crash, once disposal started, or for an addon without the export.
|
||||
*/
|
||||
function __reconcileWasiThreadPool() {
|
||||
try {
|
||||
if (__wasiDisposed || __wasiDisposePromise || __hasWasiThreadCrashed()) {
|
||||
return
|
||||
}
|
||||
const read = __napiInstance?.exports?.napi_wasm_runtime_pool_workers
|
||||
if (typeof read !== 'function') {
|
||||
return
|
||||
}
|
||||
const count = read() >>> 0
|
||||
const manager = __getWasiThreadManager()
|
||||
if (
|
||||
!manager ||
|
||||
!Array.isArray(manager.unusedWorkers) ||
|
||||
typeof manager.allocateUnusedWorker !== 'function' ||
|
||||
typeof manager.loadWasmModuleToWorker !== 'function'
|
||||
) {
|
||||
return
|
||||
}
|
||||
// Both loops are bounded by the difference they start from, so a manager
|
||||
// that does not update `unusedWorkers` the way emnapi does cannot spin.
|
||||
for (let excess = manager.unusedWorkers.length - count; excess > 0; excess--) {
|
||||
const worker = manager.unusedWorkers[manager.unusedWorkers.length - 1]
|
||||
manager.terminateWorker(worker)
|
||||
__removeWasiPoolWorker(manager, worker)
|
||||
__untrackWasiWorkerOnExit(worker)
|
||||
// Compatibility with @emnapi/wasi-threads 2.1.0 and older:
|
||||
// `terminateWorker` installs a reporter that logs every emnapi message
|
||||
// still queued on the port, so a Worker that finished loading just
|
||||
// before it was terminated prints 'received "loaded" command from
|
||||
// terminated worker'. Nothing listens for that Worker any more, and a
|
||||
// newer emnapi ignores the late 'loaded' itself, so this is harmless
|
||||
// there. `__terminateWasiWorkers` does the same.
|
||||
worker.onmessage = undefined
|
||||
}
|
||||
for (let missing = count - manager.unusedWorkers.length; missing > 0; missing--) {
|
||||
let worker
|
||||
try {
|
||||
// Through `onCreateWorker`: tracked in `__wasiWorkers`, unref'd, and
|
||||
// handed the crash flags like any pool Worker.
|
||||
worker = manager.allocateUnusedWorker()
|
||||
manager
|
||||
.loadWasmModuleToWorker(worker)
|
||||
.then(undefined, () => __removeWasiPoolWorker(manager, worker))
|
||||
} catch {
|
||||
if (worker !== undefined) {
|
||||
__removeWasiPoolWorker(manager, worker)
|
||||
try {
|
||||
manager.terminateWorker(worker)
|
||||
__untrackWasiWorkerOnExit(worker)
|
||||
} catch {}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function __publishWasiThreadPoolReconcile(exports) {
|
||||
Object.defineProperty(exports, __wasiThreadPoolReconcileSymbol, {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
value: __reconcileWasiThreadPool,
|
||||
writable: false,
|
||||
})
|
||||
}
|
||||
|
||||
const __cwd = process.cwd()
|
||||
const __rootDir = __nodePath.parse(__cwd).root
|
||||
const __hostRoot =
|
||||
@@ -2011,7 +2165,7 @@ try {
|
||||
const __finishAutoDestroyCapture = __captureEmnapiAutoDestroyListener()
|
||||
try {
|
||||
__emnapiContext = __wrapEmnapiContextDestroyForSettlement(
|
||||
__emnapiCreateContext({ autoDestroy: false }),
|
||||
__emnapiCreateContext({ autoDestroy: false, features: { setImmediate: __wasiSetImmediate } }),
|
||||
__prepareWasmEnvCleanup,
|
||||
__isPreparingWasmEnvCleanup,
|
||||
)
|
||||
@@ -2108,6 +2262,7 @@ try {
|
||||
},
|
||||
}))
|
||||
__publishWasiDispose(__napiModule.exports)
|
||||
__publishWasiThreadPoolReconcile(__napiModule.exports)
|
||||
// The CommonJS tail below aliases `__napiModule.exports`; a named module
|
||||
// export does not travel with it, so carry the marker on the binding itself
|
||||
// too. Three things pin the stamp to exactly this spot:
|
||||
@@ -2136,6 +2291,11 @@ try {
|
||||
__runWasiInitializationRollback(rollback)
|
||||
throw rollback.error
|
||||
}
|
||||
// Preload the pool for the count the addon configured during registration.
|
||||
// See `__reconcileWasiThreadPool`.
|
||||
try {
|
||||
__reconcileWasiThreadPool()
|
||||
} catch {}
|
||||
module.exports = __napiModule.exports
|
||||
module.exports.LegalCommentsMode = __napiModule.exports.LegalCommentsMode
|
||||
module.exports.minify = __napiModule.exports.minify
|
||||
|
||||
@@ -222,6 +222,34 @@ function __hasWasiThreadCrashed() {
|
||||
}
|
||||
|
||||
let __wasiThreadCrashDisposePromise
|
||||
// Raised by the crash disposal right before it terminates the workers. See
|
||||
// `__wasiSetImmediate`.
|
||||
let __wasiReentryClosed = false
|
||||
|
||||
/**
|
||||
* emnapi's `features.setImmediate` for this binding's context. emnapi defers
|
||||
* its calls back into wasm through it: `_emnapi_set_immediate` (libuv handle
|
||||
* closes, threadsafe-function finalizers), threadsafe-function dispatch
|
||||
* (`async-send`) and the finalizer queue. One queued before a crash disposal
|
||||
* terminates the workers still runs after it, and a worker terminated while it
|
||||
* held a lock in the wasm heap (napi's heap-sync allocator lock spins and never
|
||||
* gives up) leaves that call spinning on this thread for good. The binding is
|
||||
* unusable after a crash disposal, so those calls are dropped.
|
||||
*
|
||||
* Known residual, still able to enter wasm after a crash disposal because emnapi
|
||||
* offers no hook for them: the `FinalizationRegistry` callbacks that free
|
||||
* external memory when GC collects a value (`_free`, the shared-buffer meta
|
||||
* release), threadsafe-function dispatch of `async-send` type 1 and
|
||||
* `_emnapi_next_tick` (both `Promise.resolve().then`), and every deferred
|
||||
* call under emnapi 1.x, whose `createContext` ignores `features`.
|
||||
*/
|
||||
function __wasiSetImmediate(callback) {
|
||||
return setImmediate(function () {
|
||||
if (!__wasiReentryClosed) {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the first error a pool worker reported, with the worker's id. Called
|
||||
@@ -371,6 +399,8 @@ function __disposeWasiBindingAfterThreadCrash() {
|
||||
return __wasiThreadCrashDisposePromise
|
||||
}
|
||||
__releaseEmnapiWaitingRequestHandle()
|
||||
// No call into wasm after this: see `__wasiSetImmediate`.
|
||||
__wasiReentryClosed = true
|
||||
let workerResult
|
||||
try {
|
||||
workerResult = __terminateWasiWorkers()
|
||||
@@ -455,6 +485,130 @@ function __rollbackWasiInitializationAfterThreadCrash() {
|
||||
return [crashError]
|
||||
}
|
||||
|
||||
const __wasiThreadPoolReconcileSymbol = Symbol.for('napi.rs.wasi.reconcileThreadPool')
|
||||
|
||||
/**
|
||||
* Takes a Worker out of emnapi's reuse pool, if it is still there. emnapi
|
||||
* terminates a pooled Worker that failed to load but (up to
|
||||
* @emnapi/wasi-threads 2.1.0) leaves it in the pool, where the next thread
|
||||
* spawn would pop it. A newer emnapi removes it itself, so this is a no-op
|
||||
* then.
|
||||
*/
|
||||
function __removeWasiPoolWorker(manager, worker) {
|
||||
const index = manager.unusedWorkers.indexOf(worker)
|
||||
if (index !== -1) {
|
||||
manager.unusedWorkers.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a Worker the thread manager just terminated out of `__wasiWorkers`
|
||||
* once it has exited, not before. `terminateWorker` only starts Node's
|
||||
* asynchronous `worker.terminate()` and drops its promise, so a disposal that
|
||||
* begins before the exit has to find this Worker in the set and wait for it
|
||||
* like any other. A second `terminate()` settles when the Worker has exited,
|
||||
* the way `__terminateWasiWorkers` waits.
|
||||
*/
|
||||
function __untrackWasiWorkerOnExit(worker) {
|
||||
const terminated = worker.terminate()
|
||||
if (__isThenable(terminated)) {
|
||||
Promise.resolve(terminated).then(
|
||||
() => {
|
||||
__wasiWorkers.delete(worker)
|
||||
},
|
||||
// Left tracked: disposal terminates it again and reports the error.
|
||||
() => {},
|
||||
)
|
||||
} else {
|
||||
__wasiWorkers.delete(worker)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches emnapi's idle reuse pool to the addon's configured MultiThread worker
|
||||
* count, which the addon exports as `napi_wasm_runtime_pool_workers`
|
||||
* (napi-async-runtime; 0 under CurrentThread). `reuseWorker: true` starts the
|
||||
* pool empty, so without this every pool thread the first async call spawns
|
||||
* boots a Worker and loads the wasm into it first. Here each missing Worker is
|
||||
* created and starts loading now; a spawn later pops one that is already
|
||||
* booting. Idle Workers above the count are terminated, last in first out,
|
||||
* the way a spawn takes them. A Worker a spawn already took is not in the pool
|
||||
* and is left alone.
|
||||
*
|
||||
* Runs once after a successful load and, when the loader wraps it, after every
|
||||
* successful `configureAsyncRuntime`. Also reachable as
|
||||
* binding[Symbol.for('napi.rs.wasi.reconcileThreadPool')]().
|
||||
* It never throws and never waits on a Worker: a Worker whose load fails is
|
||||
* dropped from the pool when its load rejects. Nothing at all happens after a
|
||||
* thread crash, once disposal started, or for an addon without the export.
|
||||
*/
|
||||
function __reconcileWasiThreadPool() {
|
||||
try {
|
||||
if (__wasiDisposed || __wasiDisposePromise || __hasWasiThreadCrashed()) {
|
||||
return
|
||||
}
|
||||
const read = __napiInstance?.exports?.napi_wasm_runtime_pool_workers
|
||||
if (typeof read !== 'function') {
|
||||
return
|
||||
}
|
||||
const count = read() >>> 0
|
||||
const manager = __getWasiThreadManager()
|
||||
if (
|
||||
!manager ||
|
||||
!Array.isArray(manager.unusedWorkers) ||
|
||||
typeof manager.allocateUnusedWorker !== 'function' ||
|
||||
typeof manager.loadWasmModuleToWorker !== 'function'
|
||||
) {
|
||||
return
|
||||
}
|
||||
// Both loops are bounded by the difference they start from, so a manager
|
||||
// that does not update `unusedWorkers` the way emnapi does cannot spin.
|
||||
for (let excess = manager.unusedWorkers.length - count; excess > 0; excess--) {
|
||||
const worker = manager.unusedWorkers[manager.unusedWorkers.length - 1]
|
||||
manager.terminateWorker(worker)
|
||||
__removeWasiPoolWorker(manager, worker)
|
||||
__untrackWasiWorkerOnExit(worker)
|
||||
// Compatibility with @emnapi/wasi-threads 2.1.0 and older:
|
||||
// `terminateWorker` installs a reporter that logs every emnapi message
|
||||
// still queued on the port, so a Worker that finished loading just
|
||||
// before it was terminated prints 'received "loaded" command from
|
||||
// terminated worker'. Nothing listens for that Worker any more, and a
|
||||
// newer emnapi ignores the late 'loaded' itself, so this is harmless
|
||||
// there. `__terminateWasiWorkers` does the same.
|
||||
worker.onmessage = undefined
|
||||
}
|
||||
for (let missing = count - manager.unusedWorkers.length; missing > 0; missing--) {
|
||||
let worker
|
||||
try {
|
||||
// Through `onCreateWorker`: tracked in `__wasiWorkers`, unref'd, and
|
||||
// handed the crash flags like any pool Worker.
|
||||
worker = manager.allocateUnusedWorker()
|
||||
manager
|
||||
.loadWasmModuleToWorker(worker)
|
||||
.then(undefined, () => __removeWasiPoolWorker(manager, worker))
|
||||
} catch {
|
||||
if (worker !== undefined) {
|
||||
__removeWasiPoolWorker(manager, worker)
|
||||
try {
|
||||
manager.terminateWorker(worker)
|
||||
__untrackWasiWorkerOnExit(worker)
|
||||
} catch {}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function __publishWasiThreadPoolReconcile(exports) {
|
||||
Object.defineProperty(exports, __wasiThreadPoolReconcileSymbol, {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
value: __reconcileWasiThreadPool,
|
||||
writable: false,
|
||||
})
|
||||
}
|
||||
|
||||
const __cwd = process.cwd()
|
||||
const __rootDir = __nodePath.parse(__cwd).root
|
||||
const __hostRoot =
|
||||
@@ -2011,7 +2165,7 @@ try {
|
||||
const __finishAutoDestroyCapture = __captureEmnapiAutoDestroyListener()
|
||||
try {
|
||||
__emnapiContext = __wrapEmnapiContextDestroyForSettlement(
|
||||
__emnapiCreateContext({ autoDestroy: false }),
|
||||
__emnapiCreateContext({ autoDestroy: false, features: { setImmediate: __wasiSetImmediate } }),
|
||||
__prepareWasmEnvCleanup,
|
||||
__isPreparingWasmEnvCleanup,
|
||||
)
|
||||
@@ -2108,6 +2262,7 @@ try {
|
||||
},
|
||||
}))
|
||||
__publishWasiDispose(__napiModule.exports)
|
||||
__publishWasiThreadPoolReconcile(__napiModule.exports)
|
||||
// The CommonJS tail below aliases `__napiModule.exports`; a named module
|
||||
// export does not travel with it, so carry the marker on the binding itself
|
||||
// too. Three things pin the stamp to exactly this spot:
|
||||
@@ -2136,6 +2291,11 @@ try {
|
||||
__runWasiInitializationRollback(rollback)
|
||||
throw rollback.error
|
||||
}
|
||||
// Preload the pool for the count the addon configured during registration.
|
||||
// See `__reconcileWasiThreadPool`.
|
||||
try {
|
||||
__reconcileWasiThreadPool()
|
||||
} catch {}
|
||||
module.exports = __napiModule.exports
|
||||
module.exports.Severity = __napiModule.exports.Severity
|
||||
module.exports.ParseResult = __napiModule.exports.ParseResult
|
||||
|
||||
@@ -222,6 +222,34 @@ function __hasWasiThreadCrashed() {
|
||||
}
|
||||
|
||||
let __wasiThreadCrashDisposePromise
|
||||
// Raised by the crash disposal right before it terminates the workers. See
|
||||
// `__wasiSetImmediate`.
|
||||
let __wasiReentryClosed = false
|
||||
|
||||
/**
|
||||
* emnapi's `features.setImmediate` for this binding's context. emnapi defers
|
||||
* its calls back into wasm through it: `_emnapi_set_immediate` (libuv handle
|
||||
* closes, threadsafe-function finalizers), threadsafe-function dispatch
|
||||
* (`async-send`) and the finalizer queue. One queued before a crash disposal
|
||||
* terminates the workers still runs after it, and a worker terminated while it
|
||||
* held a lock in the wasm heap (napi's heap-sync allocator lock spins and never
|
||||
* gives up) leaves that call spinning on this thread for good. The binding is
|
||||
* unusable after a crash disposal, so those calls are dropped.
|
||||
*
|
||||
* Known residual, still able to enter wasm after a crash disposal because emnapi
|
||||
* offers no hook for them: the `FinalizationRegistry` callbacks that free
|
||||
* external memory when GC collects a value (`_free`, the shared-buffer meta
|
||||
* release), threadsafe-function dispatch of `async-send` type 1 and
|
||||
* `_emnapi_next_tick` (both `Promise.resolve().then`), and every deferred
|
||||
* call under emnapi 1.x, whose `createContext` ignores `features`.
|
||||
*/
|
||||
function __wasiSetImmediate(callback) {
|
||||
return setImmediate(function () {
|
||||
if (!__wasiReentryClosed) {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the first error a pool worker reported, with the worker's id. Called
|
||||
@@ -371,6 +399,8 @@ function __disposeWasiBindingAfterThreadCrash() {
|
||||
return __wasiThreadCrashDisposePromise
|
||||
}
|
||||
__releaseEmnapiWaitingRequestHandle()
|
||||
// No call into wasm after this: see `__wasiSetImmediate`.
|
||||
__wasiReentryClosed = true
|
||||
let workerResult
|
||||
try {
|
||||
workerResult = __terminateWasiWorkers()
|
||||
@@ -455,6 +485,130 @@ function __rollbackWasiInitializationAfterThreadCrash() {
|
||||
return [crashError]
|
||||
}
|
||||
|
||||
const __wasiThreadPoolReconcileSymbol = Symbol.for('napi.rs.wasi.reconcileThreadPool')
|
||||
|
||||
/**
|
||||
* Takes a Worker out of emnapi's reuse pool, if it is still there. emnapi
|
||||
* terminates a pooled Worker that failed to load but (up to
|
||||
* @emnapi/wasi-threads 2.1.0) leaves it in the pool, where the next thread
|
||||
* spawn would pop it. A newer emnapi removes it itself, so this is a no-op
|
||||
* then.
|
||||
*/
|
||||
function __removeWasiPoolWorker(manager, worker) {
|
||||
const index = manager.unusedWorkers.indexOf(worker)
|
||||
if (index !== -1) {
|
||||
manager.unusedWorkers.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a Worker the thread manager just terminated out of `__wasiWorkers`
|
||||
* once it has exited, not before. `terminateWorker` only starts Node's
|
||||
* asynchronous `worker.terminate()` and drops its promise, so a disposal that
|
||||
* begins before the exit has to find this Worker in the set and wait for it
|
||||
* like any other. A second `terminate()` settles when the Worker has exited,
|
||||
* the way `__terminateWasiWorkers` waits.
|
||||
*/
|
||||
function __untrackWasiWorkerOnExit(worker) {
|
||||
const terminated = worker.terminate()
|
||||
if (__isThenable(terminated)) {
|
||||
Promise.resolve(terminated).then(
|
||||
() => {
|
||||
__wasiWorkers.delete(worker)
|
||||
},
|
||||
// Left tracked: disposal terminates it again and reports the error.
|
||||
() => {},
|
||||
)
|
||||
} else {
|
||||
__wasiWorkers.delete(worker)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches emnapi's idle reuse pool to the addon's configured MultiThread worker
|
||||
* count, which the addon exports as `napi_wasm_runtime_pool_workers`
|
||||
* (napi-async-runtime; 0 under CurrentThread). `reuseWorker: true` starts the
|
||||
* pool empty, so without this every pool thread the first async call spawns
|
||||
* boots a Worker and loads the wasm into it first. Here each missing Worker is
|
||||
* created and starts loading now; a spawn later pops one that is already
|
||||
* booting. Idle Workers above the count are terminated, last in first out,
|
||||
* the way a spawn takes them. A Worker a spawn already took is not in the pool
|
||||
* and is left alone.
|
||||
*
|
||||
* Runs once after a successful load and, when the loader wraps it, after every
|
||||
* successful `configureAsyncRuntime`. Also reachable as
|
||||
* binding[Symbol.for('napi.rs.wasi.reconcileThreadPool')]().
|
||||
* It never throws and never waits on a Worker: a Worker whose load fails is
|
||||
* dropped from the pool when its load rejects. Nothing at all happens after a
|
||||
* thread crash, once disposal started, or for an addon without the export.
|
||||
*/
|
||||
function __reconcileWasiThreadPool() {
|
||||
try {
|
||||
if (__wasiDisposed || __wasiDisposePromise || __hasWasiThreadCrashed()) {
|
||||
return
|
||||
}
|
||||
const read = __napiInstance?.exports?.napi_wasm_runtime_pool_workers
|
||||
if (typeof read !== 'function') {
|
||||
return
|
||||
}
|
||||
const count = read() >>> 0
|
||||
const manager = __getWasiThreadManager()
|
||||
if (
|
||||
!manager ||
|
||||
!Array.isArray(manager.unusedWorkers) ||
|
||||
typeof manager.allocateUnusedWorker !== 'function' ||
|
||||
typeof manager.loadWasmModuleToWorker !== 'function'
|
||||
) {
|
||||
return
|
||||
}
|
||||
// Both loops are bounded by the difference they start from, so a manager
|
||||
// that does not update `unusedWorkers` the way emnapi does cannot spin.
|
||||
for (let excess = manager.unusedWorkers.length - count; excess > 0; excess--) {
|
||||
const worker = manager.unusedWorkers[manager.unusedWorkers.length - 1]
|
||||
manager.terminateWorker(worker)
|
||||
__removeWasiPoolWorker(manager, worker)
|
||||
__untrackWasiWorkerOnExit(worker)
|
||||
// Compatibility with @emnapi/wasi-threads 2.1.0 and older:
|
||||
// `terminateWorker` installs a reporter that logs every emnapi message
|
||||
// still queued on the port, so a Worker that finished loading just
|
||||
// before it was terminated prints 'received "loaded" command from
|
||||
// terminated worker'. Nothing listens for that Worker any more, and a
|
||||
// newer emnapi ignores the late 'loaded' itself, so this is harmless
|
||||
// there. `__terminateWasiWorkers` does the same.
|
||||
worker.onmessage = undefined
|
||||
}
|
||||
for (let missing = count - manager.unusedWorkers.length; missing > 0; missing--) {
|
||||
let worker
|
||||
try {
|
||||
// Through `onCreateWorker`: tracked in `__wasiWorkers`, unref'd, and
|
||||
// handed the crash flags like any pool Worker.
|
||||
worker = manager.allocateUnusedWorker()
|
||||
manager
|
||||
.loadWasmModuleToWorker(worker)
|
||||
.then(undefined, () => __removeWasiPoolWorker(manager, worker))
|
||||
} catch {
|
||||
if (worker !== undefined) {
|
||||
__removeWasiPoolWorker(manager, worker)
|
||||
try {
|
||||
manager.terminateWorker(worker)
|
||||
__untrackWasiWorkerOnExit(worker)
|
||||
} catch {}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function __publishWasiThreadPoolReconcile(exports) {
|
||||
Object.defineProperty(exports, __wasiThreadPoolReconcileSymbol, {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
value: __reconcileWasiThreadPool,
|
||||
writable: false,
|
||||
})
|
||||
}
|
||||
|
||||
const __cwd = process.cwd()
|
||||
const __rootDir = __nodePath.parse(__cwd).root
|
||||
const __hostRoot =
|
||||
@@ -2011,7 +2165,7 @@ try {
|
||||
const __finishAutoDestroyCapture = __captureEmnapiAutoDestroyListener()
|
||||
try {
|
||||
__emnapiContext = __wrapEmnapiContextDestroyForSettlement(
|
||||
__emnapiCreateContext({ autoDestroy: false }),
|
||||
__emnapiCreateContext({ autoDestroy: false, features: { setImmediate: __wasiSetImmediate } }),
|
||||
__prepareWasmEnvCleanup,
|
||||
__isPreparingWasmEnvCleanup,
|
||||
)
|
||||
@@ -2108,6 +2262,7 @@ try {
|
||||
},
|
||||
}))
|
||||
__publishWasiDispose(__napiModule.exports)
|
||||
__publishWasiThreadPoolReconcile(__napiModule.exports)
|
||||
// The CommonJS tail below aliases `__napiModule.exports`; a named module
|
||||
// export does not travel with it, so carry the marker on the binding itself
|
||||
// too. Three things pin the stamp to exactly this spot:
|
||||
@@ -2136,6 +2291,11 @@ try {
|
||||
__runWasiInitializationRollback(rollback)
|
||||
throw rollback.error
|
||||
}
|
||||
// Preload the pool for the count the addon configured during registration.
|
||||
// See `__reconcileWasiThreadPool`.
|
||||
try {
|
||||
__reconcileWasiThreadPool()
|
||||
} catch {}
|
||||
module.exports = __napiModule.exports
|
||||
module.exports.Severity = __napiModule.exports.Severity
|
||||
module.exports.Oxc = __napiModule.exports.Oxc
|
||||
@@ -222,6 +222,34 @@ function __hasWasiThreadCrashed() {
|
||||
}
|
||||
|
||||
let __wasiThreadCrashDisposePromise
|
||||
// Raised by the crash disposal right before it terminates the workers. See
|
||||
// `__wasiSetImmediate`.
|
||||
let __wasiReentryClosed = false
|
||||
|
||||
/**
|
||||
* emnapi's `features.setImmediate` for this binding's context. emnapi defers
|
||||
* its calls back into wasm through it: `_emnapi_set_immediate` (libuv handle
|
||||
* closes, threadsafe-function finalizers), threadsafe-function dispatch
|
||||
* (`async-send`) and the finalizer queue. One queued before a crash disposal
|
||||
* terminates the workers still runs after it, and a worker terminated while it
|
||||
* held a lock in the wasm heap (napi's heap-sync allocator lock spins and never
|
||||
* gives up) leaves that call spinning on this thread for good. The binding is
|
||||
* unusable after a crash disposal, so those calls are dropped.
|
||||
*
|
||||
* Known residual, still able to enter wasm after a crash disposal because emnapi
|
||||
* offers no hook for them: the `FinalizationRegistry` callbacks that free
|
||||
* external memory when GC collects a value (`_free`, the shared-buffer meta
|
||||
* release), threadsafe-function dispatch of `async-send` type 1 and
|
||||
* `_emnapi_next_tick` (both `Promise.resolve().then`), and every deferred
|
||||
* call under emnapi 1.x, whose `createContext` ignores `features`.
|
||||
*/
|
||||
function __wasiSetImmediate(callback) {
|
||||
return setImmediate(function () {
|
||||
if (!__wasiReentryClosed) {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the first error a pool worker reported, with the worker's id. Called
|
||||
@@ -371,6 +399,8 @@ function __disposeWasiBindingAfterThreadCrash() {
|
||||
return __wasiThreadCrashDisposePromise
|
||||
}
|
||||
__releaseEmnapiWaitingRequestHandle()
|
||||
// No call into wasm after this: see `__wasiSetImmediate`.
|
||||
__wasiReentryClosed = true
|
||||
let workerResult
|
||||
try {
|
||||
workerResult = __terminateWasiWorkers()
|
||||
@@ -455,6 +485,130 @@ function __rollbackWasiInitializationAfterThreadCrash() {
|
||||
return [crashError]
|
||||
}
|
||||
|
||||
const __wasiThreadPoolReconcileSymbol = Symbol.for('napi.rs.wasi.reconcileThreadPool')
|
||||
|
||||
/**
|
||||
* Takes a Worker out of emnapi's reuse pool, if it is still there. emnapi
|
||||
* terminates a pooled Worker that failed to load but (up to
|
||||
* @emnapi/wasi-threads 2.1.0) leaves it in the pool, where the next thread
|
||||
* spawn would pop it. A newer emnapi removes it itself, so this is a no-op
|
||||
* then.
|
||||
*/
|
||||
function __removeWasiPoolWorker(manager, worker) {
|
||||
const index = manager.unusedWorkers.indexOf(worker)
|
||||
if (index !== -1) {
|
||||
manager.unusedWorkers.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a Worker the thread manager just terminated out of `__wasiWorkers`
|
||||
* once it has exited, not before. `terminateWorker` only starts Node's
|
||||
* asynchronous `worker.terminate()` and drops its promise, so a disposal that
|
||||
* begins before the exit has to find this Worker in the set and wait for it
|
||||
* like any other. A second `terminate()` settles when the Worker has exited,
|
||||
* the way `__terminateWasiWorkers` waits.
|
||||
*/
|
||||
function __untrackWasiWorkerOnExit(worker) {
|
||||
const terminated = worker.terminate()
|
||||
if (__isThenable(terminated)) {
|
||||
Promise.resolve(terminated).then(
|
||||
() => {
|
||||
__wasiWorkers.delete(worker)
|
||||
},
|
||||
// Left tracked: disposal terminates it again and reports the error.
|
||||
() => {},
|
||||
)
|
||||
} else {
|
||||
__wasiWorkers.delete(worker)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches emnapi's idle reuse pool to the addon's configured MultiThread worker
|
||||
* count, which the addon exports as `napi_wasm_runtime_pool_workers`
|
||||
* (napi-async-runtime; 0 under CurrentThread). `reuseWorker: true` starts the
|
||||
* pool empty, so without this every pool thread the first async call spawns
|
||||
* boots a Worker and loads the wasm into it first. Here each missing Worker is
|
||||
* created and starts loading now; a spawn later pops one that is already
|
||||
* booting. Idle Workers above the count are terminated, last in first out,
|
||||
* the way a spawn takes them. A Worker a spawn already took is not in the pool
|
||||
* and is left alone.
|
||||
*
|
||||
* Runs once after a successful load and, when the loader wraps it, after every
|
||||
* successful `configureAsyncRuntime`. Also reachable as
|
||||
* binding[Symbol.for('napi.rs.wasi.reconcileThreadPool')]().
|
||||
* It never throws and never waits on a Worker: a Worker whose load fails is
|
||||
* dropped from the pool when its load rejects. Nothing at all happens after a
|
||||
* thread crash, once disposal started, or for an addon without the export.
|
||||
*/
|
||||
function __reconcileWasiThreadPool() {
|
||||
try {
|
||||
if (__wasiDisposed || __wasiDisposePromise || __hasWasiThreadCrashed()) {
|
||||
return
|
||||
}
|
||||
const read = __napiInstance?.exports?.napi_wasm_runtime_pool_workers
|
||||
if (typeof read !== 'function') {
|
||||
return
|
||||
}
|
||||
const count = read() >>> 0
|
||||
const manager = __getWasiThreadManager()
|
||||
if (
|
||||
!manager ||
|
||||
!Array.isArray(manager.unusedWorkers) ||
|
||||
typeof manager.allocateUnusedWorker !== 'function' ||
|
||||
typeof manager.loadWasmModuleToWorker !== 'function'
|
||||
) {
|
||||
return
|
||||
}
|
||||
// Both loops are bounded by the difference they start from, so a manager
|
||||
// that does not update `unusedWorkers` the way emnapi does cannot spin.
|
||||
for (let excess = manager.unusedWorkers.length - count; excess > 0; excess--) {
|
||||
const worker = manager.unusedWorkers[manager.unusedWorkers.length - 1]
|
||||
manager.terminateWorker(worker)
|
||||
__removeWasiPoolWorker(manager, worker)
|
||||
__untrackWasiWorkerOnExit(worker)
|
||||
// Compatibility with @emnapi/wasi-threads 2.1.0 and older:
|
||||
// `terminateWorker` installs a reporter that logs every emnapi message
|
||||
// still queued on the port, so a Worker that finished loading just
|
||||
// before it was terminated prints 'received "loaded" command from
|
||||
// terminated worker'. Nothing listens for that Worker any more, and a
|
||||
// newer emnapi ignores the late 'loaded' itself, so this is harmless
|
||||
// there. `__terminateWasiWorkers` does the same.
|
||||
worker.onmessage = undefined
|
||||
}
|
||||
for (let missing = count - manager.unusedWorkers.length; missing > 0; missing--) {
|
||||
let worker
|
||||
try {
|
||||
// Through `onCreateWorker`: tracked in `__wasiWorkers`, unref'd, and
|
||||
// handed the crash flags like any pool Worker.
|
||||
worker = manager.allocateUnusedWorker()
|
||||
manager
|
||||
.loadWasmModuleToWorker(worker)
|
||||
.then(undefined, () => __removeWasiPoolWorker(manager, worker))
|
||||
} catch {
|
||||
if (worker !== undefined) {
|
||||
__removeWasiPoolWorker(manager, worker)
|
||||
try {
|
||||
manager.terminateWorker(worker)
|
||||
__untrackWasiWorkerOnExit(worker)
|
||||
} catch {}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function __publishWasiThreadPoolReconcile(exports) {
|
||||
Object.defineProperty(exports, __wasiThreadPoolReconcileSymbol, {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
value: __reconcileWasiThreadPool,
|
||||
writable: false,
|
||||
})
|
||||
}
|
||||
|
||||
const __cwd = process.cwd()
|
||||
const __rootDir = __nodePath.parse(__cwd).root
|
||||
const __hostRoot =
|
||||
@@ -2011,7 +2165,7 @@ try {
|
||||
const __finishAutoDestroyCapture = __captureEmnapiAutoDestroyListener()
|
||||
try {
|
||||
__emnapiContext = __wrapEmnapiContextDestroyForSettlement(
|
||||
__emnapiCreateContext({ autoDestroy: false }),
|
||||
__emnapiCreateContext({ autoDestroy: false, features: { setImmediate: __wasiSetImmediate } }),
|
||||
__prepareWasmEnvCleanup,
|
||||
__isPreparingWasmEnvCleanup,
|
||||
)
|
||||
@@ -2108,6 +2262,7 @@ try {
|
||||
},
|
||||
}))
|
||||
__publishWasiDispose(__napiModule.exports)
|
||||
__publishWasiThreadPoolReconcile(__napiModule.exports)
|
||||
// The CommonJS tail below aliases `__napiModule.exports`; a named module
|
||||
// export does not travel with it, so carry the marker on the binding itself
|
||||
// too. Three things pin the stamp to exactly this spot:
|
||||
@@ -2136,6 +2291,11 @@ try {
|
||||
__runWasiInitializationRollback(rollback)
|
||||
throw rollback.error
|
||||
}
|
||||
// Preload the pool for the count the addon configured during registration.
|
||||
// See `__reconcileWasiThreadPool`.
|
||||
try {
|
||||
__reconcileWasiThreadPool()
|
||||
} catch {}
|
||||
module.exports = __napiModule.exports
|
||||
module.exports.Severity = __napiModule.exports.Severity
|
||||
module.exports.transform = __napiModule.exports.transform
|
||||
|
||||
@@ -222,6 +222,34 @@ function __hasWasiThreadCrashed() {
|
||||
}
|
||||
|
||||
let __wasiThreadCrashDisposePromise
|
||||
// Raised by the crash disposal right before it terminates the workers. See
|
||||
// `__wasiSetImmediate`.
|
||||
let __wasiReentryClosed = false
|
||||
|
||||
/**
|
||||
* emnapi's `features.setImmediate` for this binding's context. emnapi defers
|
||||
* its calls back into wasm through it: `_emnapi_set_immediate` (libuv handle
|
||||
* closes, threadsafe-function finalizers), threadsafe-function dispatch
|
||||
* (`async-send`) and the finalizer queue. One queued before a crash disposal
|
||||
* terminates the workers still runs after it, and a worker terminated while it
|
||||
* held a lock in the wasm heap (napi's heap-sync allocator lock spins and never
|
||||
* gives up) leaves that call spinning on this thread for good. The binding is
|
||||
* unusable after a crash disposal, so those calls are dropped.
|
||||
*
|
||||
* Known residual, still able to enter wasm after a crash disposal because emnapi
|
||||
* offers no hook for them: the `FinalizationRegistry` callbacks that free
|
||||
* external memory when GC collects a value (`_free`, the shared-buffer meta
|
||||
* release), threadsafe-function dispatch of `async-send` type 1 and
|
||||
* `_emnapi_next_tick` (both `Promise.resolve().then`), and every deferred
|
||||
* call under emnapi 1.x, whose `createContext` ignores `features`.
|
||||
*/
|
||||
function __wasiSetImmediate(callback) {
|
||||
return setImmediate(function () {
|
||||
if (!__wasiReentryClosed) {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the first error a pool worker reported, with the worker's id. Called
|
||||
@@ -371,6 +399,8 @@ function __disposeWasiBindingAfterThreadCrash() {
|
||||
return __wasiThreadCrashDisposePromise
|
||||
}
|
||||
__releaseEmnapiWaitingRequestHandle()
|
||||
// No call into wasm after this: see `__wasiSetImmediate`.
|
||||
__wasiReentryClosed = true
|
||||
let workerResult
|
||||
try {
|
||||
workerResult = __terminateWasiWorkers()
|
||||
@@ -455,6 +485,130 @@ function __rollbackWasiInitializationAfterThreadCrash() {
|
||||
return [crashError]
|
||||
}
|
||||
|
||||
const __wasiThreadPoolReconcileSymbol = Symbol.for('napi.rs.wasi.reconcileThreadPool')
|
||||
|
||||
/**
|
||||
* Takes a Worker out of emnapi's reuse pool, if it is still there. emnapi
|
||||
* terminates a pooled Worker that failed to load but (up to
|
||||
* @emnapi/wasi-threads 2.1.0) leaves it in the pool, where the next thread
|
||||
* spawn would pop it. A newer emnapi removes it itself, so this is a no-op
|
||||
* then.
|
||||
*/
|
||||
function __removeWasiPoolWorker(manager, worker) {
|
||||
const index = manager.unusedWorkers.indexOf(worker)
|
||||
if (index !== -1) {
|
||||
manager.unusedWorkers.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a Worker the thread manager just terminated out of `__wasiWorkers`
|
||||
* once it has exited, not before. `terminateWorker` only starts Node's
|
||||
* asynchronous `worker.terminate()` and drops its promise, so a disposal that
|
||||
* begins before the exit has to find this Worker in the set and wait for it
|
||||
* like any other. A second `terminate()` settles when the Worker has exited,
|
||||
* the way `__terminateWasiWorkers` waits.
|
||||
*/
|
||||
function __untrackWasiWorkerOnExit(worker) {
|
||||
const terminated = worker.terminate()
|
||||
if (__isThenable(terminated)) {
|
||||
Promise.resolve(terminated).then(
|
||||
() => {
|
||||
__wasiWorkers.delete(worker)
|
||||
},
|
||||
// Left tracked: disposal terminates it again and reports the error.
|
||||
() => {},
|
||||
)
|
||||
} else {
|
||||
__wasiWorkers.delete(worker)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches emnapi's idle reuse pool to the addon's configured MultiThread worker
|
||||
* count, which the addon exports as `napi_wasm_runtime_pool_workers`
|
||||
* (napi-async-runtime; 0 under CurrentThread). `reuseWorker: true` starts the
|
||||
* pool empty, so without this every pool thread the first async call spawns
|
||||
* boots a Worker and loads the wasm into it first. Here each missing Worker is
|
||||
* created and starts loading now; a spawn later pops one that is already
|
||||
* booting. Idle Workers above the count are terminated, last in first out,
|
||||
* the way a spawn takes them. A Worker a spawn already took is not in the pool
|
||||
* and is left alone.
|
||||
*
|
||||
* Runs once after a successful load and, when the loader wraps it, after every
|
||||
* successful `configureAsyncRuntime`. Also reachable as
|
||||
* binding[Symbol.for('napi.rs.wasi.reconcileThreadPool')]().
|
||||
* It never throws and never waits on a Worker: a Worker whose load fails is
|
||||
* dropped from the pool when its load rejects. Nothing at all happens after a
|
||||
* thread crash, once disposal started, or for an addon without the export.
|
||||
*/
|
||||
function __reconcileWasiThreadPool() {
|
||||
try {
|
||||
if (__wasiDisposed || __wasiDisposePromise || __hasWasiThreadCrashed()) {
|
||||
return
|
||||
}
|
||||
const read = __napiInstance?.exports?.napi_wasm_runtime_pool_workers
|
||||
if (typeof read !== 'function') {
|
||||
return
|
||||
}
|
||||
const count = read() >>> 0
|
||||
const manager = __getWasiThreadManager()
|
||||
if (
|
||||
!manager ||
|
||||
!Array.isArray(manager.unusedWorkers) ||
|
||||
typeof manager.allocateUnusedWorker !== 'function' ||
|
||||
typeof manager.loadWasmModuleToWorker !== 'function'
|
||||
) {
|
||||
return
|
||||
}
|
||||
// Both loops are bounded by the difference they start from, so a manager
|
||||
// that does not update `unusedWorkers` the way emnapi does cannot spin.
|
||||
for (let excess = manager.unusedWorkers.length - count; excess > 0; excess--) {
|
||||
const worker = manager.unusedWorkers[manager.unusedWorkers.length - 1]
|
||||
manager.terminateWorker(worker)
|
||||
__removeWasiPoolWorker(manager, worker)
|
||||
__untrackWasiWorkerOnExit(worker)
|
||||
// Compatibility with @emnapi/wasi-threads 2.1.0 and older:
|
||||
// `terminateWorker` installs a reporter that logs every emnapi message
|
||||
// still queued on the port, so a Worker that finished loading just
|
||||
// before it was terminated prints 'received "loaded" command from
|
||||
// terminated worker'. Nothing listens for that Worker any more, and a
|
||||
// newer emnapi ignores the late 'loaded' itself, so this is harmless
|
||||
// there. `__terminateWasiWorkers` does the same.
|
||||
worker.onmessage = undefined
|
||||
}
|
||||
for (let missing = count - manager.unusedWorkers.length; missing > 0; missing--) {
|
||||
let worker
|
||||
try {
|
||||
// Through `onCreateWorker`: tracked in `__wasiWorkers`, unref'd, and
|
||||
// handed the crash flags like any pool Worker.
|
||||
worker = manager.allocateUnusedWorker()
|
||||
manager
|
||||
.loadWasmModuleToWorker(worker)
|
||||
.then(undefined, () => __removeWasiPoolWorker(manager, worker))
|
||||
} catch {
|
||||
if (worker !== undefined) {
|
||||
__removeWasiPoolWorker(manager, worker)
|
||||
try {
|
||||
manager.terminateWorker(worker)
|
||||
__untrackWasiWorkerOnExit(worker)
|
||||
} catch {}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function __publishWasiThreadPoolReconcile(exports) {
|
||||
Object.defineProperty(exports, __wasiThreadPoolReconcileSymbol, {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
value: __reconcileWasiThreadPool,
|
||||
writable: false,
|
||||
})
|
||||
}
|
||||
|
||||
const __cwd = process.cwd()
|
||||
const __rootDir = __nodePath.parse(__cwd).root
|
||||
const __hostRoot =
|
||||
@@ -2011,7 +2165,7 @@ try {
|
||||
const __finishAutoDestroyCapture = __captureEmnapiAutoDestroyListener()
|
||||
try {
|
||||
__emnapiContext = __wrapEmnapiContextDestroyForSettlement(
|
||||
__emnapiCreateContext({ autoDestroy: false }),
|
||||
__emnapiCreateContext({ autoDestroy: false, features: { setImmediate: __wasiSetImmediate } }),
|
||||
__prepareWasmEnvCleanup,
|
||||
__isPreparingWasmEnvCleanup,
|
||||
)
|
||||
@@ -2108,6 +2262,7 @@ try {
|
||||
},
|
||||
}))
|
||||
__publishWasiDispose(__napiModule.exports)
|
||||
__publishWasiThreadPoolReconcile(__napiModule.exports)
|
||||
// The CommonJS tail below aliases `__napiModule.exports`; a named module
|
||||
// export does not travel with it, so carry the marker on the binding itself
|
||||
// too. Three things pin the stamp to exactly this spot:
|
||||
@@ -2136,6 +2291,11 @@ try {
|
||||
__runWasiInitializationRollback(rollback)
|
||||
throw rollback.error
|
||||
}
|
||||
// Preload the pool for the count the addon configured during registration.
|
||||
// See `__reconcileWasiThreadPool`.
|
||||
try {
|
||||
__reconcileWasiThreadPool()
|
||||
} catch {}
|
||||
module.exports = __napiModule.exports
|
||||
module.exports.Severity = __napiModule.exports.Severity
|
||||
module.exports.transform = __napiModule.exports.transform
|
||||
|
||||
@@ -222,6 +222,34 @@ function __hasWasiThreadCrashed() {
|
||||
}
|
||||
|
||||
let __wasiThreadCrashDisposePromise
|
||||
// Raised by the crash disposal right before it terminates the workers. See
|
||||
// `__wasiSetImmediate`.
|
||||
let __wasiReentryClosed = false
|
||||
|
||||
/**
|
||||
* emnapi's `features.setImmediate` for this binding's context. emnapi defers
|
||||
* its calls back into wasm through it: `_emnapi_set_immediate` (libuv handle
|
||||
* closes, threadsafe-function finalizers), threadsafe-function dispatch
|
||||
* (`async-send`) and the finalizer queue. One queued before a crash disposal
|
||||
* terminates the workers still runs after it, and a worker terminated while it
|
||||
* held a lock in the wasm heap (napi's heap-sync allocator lock spins and never
|
||||
* gives up) leaves that call spinning on this thread for good. The binding is
|
||||
* unusable after a crash disposal, so those calls are dropped.
|
||||
*
|
||||
* Known residual, still able to enter wasm after a crash disposal because emnapi
|
||||
* offers no hook for them: the `FinalizationRegistry` callbacks that free
|
||||
* external memory when GC collects a value (`_free`, the shared-buffer meta
|
||||
* release), threadsafe-function dispatch of `async-send` type 1 and
|
||||
* `_emnapi_next_tick` (both `Promise.resolve().then`), and every deferred
|
||||
* call under emnapi 1.x, whose `createContext` ignores `features`.
|
||||
*/
|
||||
function __wasiSetImmediate(callback) {
|
||||
return setImmediate(function () {
|
||||
if (!__wasiReentryClosed) {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the first error a pool worker reported, with the worker's id. Called
|
||||
@@ -371,6 +399,8 @@ function __disposeWasiBindingAfterThreadCrash() {
|
||||
return __wasiThreadCrashDisposePromise
|
||||
}
|
||||
__releaseEmnapiWaitingRequestHandle()
|
||||
// No call into wasm after this: see `__wasiSetImmediate`.
|
||||
__wasiReentryClosed = true
|
||||
let workerResult
|
||||
try {
|
||||
workerResult = __terminateWasiWorkers()
|
||||
@@ -455,6 +485,130 @@ function __rollbackWasiInitializationAfterThreadCrash() {
|
||||
return [crashError]
|
||||
}
|
||||
|
||||
const __wasiThreadPoolReconcileSymbol = Symbol.for('napi.rs.wasi.reconcileThreadPool')
|
||||
|
||||
/**
|
||||
* Takes a Worker out of emnapi's reuse pool, if it is still there. emnapi
|
||||
* terminates a pooled Worker that failed to load but (up to
|
||||
* @emnapi/wasi-threads 2.1.0) leaves it in the pool, where the next thread
|
||||
* spawn would pop it. A newer emnapi removes it itself, so this is a no-op
|
||||
* then.
|
||||
*/
|
||||
function __removeWasiPoolWorker(manager, worker) {
|
||||
const index = manager.unusedWorkers.indexOf(worker)
|
||||
if (index !== -1) {
|
||||
manager.unusedWorkers.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a Worker the thread manager just terminated out of `__wasiWorkers`
|
||||
* once it has exited, not before. `terminateWorker` only starts Node's
|
||||
* asynchronous `worker.terminate()` and drops its promise, so a disposal that
|
||||
* begins before the exit has to find this Worker in the set and wait for it
|
||||
* like any other. A second `terminate()` settles when the Worker has exited,
|
||||
* the way `__terminateWasiWorkers` waits.
|
||||
*/
|
||||
function __untrackWasiWorkerOnExit(worker) {
|
||||
const terminated = worker.terminate()
|
||||
if (__isThenable(terminated)) {
|
||||
Promise.resolve(terminated).then(
|
||||
() => {
|
||||
__wasiWorkers.delete(worker)
|
||||
},
|
||||
// Left tracked: disposal terminates it again and reports the error.
|
||||
() => {},
|
||||
)
|
||||
} else {
|
||||
__wasiWorkers.delete(worker)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches emnapi's idle reuse pool to the addon's configured MultiThread worker
|
||||
* count, which the addon exports as `napi_wasm_runtime_pool_workers`
|
||||
* (napi-async-runtime; 0 under CurrentThread). `reuseWorker: true` starts the
|
||||
* pool empty, so without this every pool thread the first async call spawns
|
||||
* boots a Worker and loads the wasm into it first. Here each missing Worker is
|
||||
* created and starts loading now; a spawn later pops one that is already
|
||||
* booting. Idle Workers above the count are terminated, last in first out,
|
||||
* the way a spawn takes them. A Worker a spawn already took is not in the pool
|
||||
* and is left alone.
|
||||
*
|
||||
* Runs once after a successful load and, when the loader wraps it, after every
|
||||
* successful `configureAsyncRuntime`. Also reachable as
|
||||
* binding[Symbol.for('napi.rs.wasi.reconcileThreadPool')]().
|
||||
* It never throws and never waits on a Worker: a Worker whose load fails is
|
||||
* dropped from the pool when its load rejects. Nothing at all happens after a
|
||||
* thread crash, once disposal started, or for an addon without the export.
|
||||
*/
|
||||
function __reconcileWasiThreadPool() {
|
||||
try {
|
||||
if (__wasiDisposed || __wasiDisposePromise || __hasWasiThreadCrashed()) {
|
||||
return
|
||||
}
|
||||
const read = __napiInstance?.exports?.napi_wasm_runtime_pool_workers
|
||||
if (typeof read !== 'function') {
|
||||
return
|
||||
}
|
||||
const count = read() >>> 0
|
||||
const manager = __getWasiThreadManager()
|
||||
if (
|
||||
!manager ||
|
||||
!Array.isArray(manager.unusedWorkers) ||
|
||||
typeof manager.allocateUnusedWorker !== 'function' ||
|
||||
typeof manager.loadWasmModuleToWorker !== 'function'
|
||||
) {
|
||||
return
|
||||
}
|
||||
// Both loops are bounded by the difference they start from, so a manager
|
||||
// that does not update `unusedWorkers` the way emnapi does cannot spin.
|
||||
for (let excess = manager.unusedWorkers.length - count; excess > 0; excess--) {
|
||||
const worker = manager.unusedWorkers[manager.unusedWorkers.length - 1]
|
||||
manager.terminateWorker(worker)
|
||||
__removeWasiPoolWorker(manager, worker)
|
||||
__untrackWasiWorkerOnExit(worker)
|
||||
// Compatibility with @emnapi/wasi-threads 2.1.0 and older:
|
||||
// `terminateWorker` installs a reporter that logs every emnapi message
|
||||
// still queued on the port, so a Worker that finished loading just
|
||||
// before it was terminated prints 'received "loaded" command from
|
||||
// terminated worker'. Nothing listens for that Worker any more, and a
|
||||
// newer emnapi ignores the late 'loaded' itself, so this is harmless
|
||||
// there. `__terminateWasiWorkers` does the same.
|
||||
worker.onmessage = undefined
|
||||
}
|
||||
for (let missing = count - manager.unusedWorkers.length; missing > 0; missing--) {
|
||||
let worker
|
||||
try {
|
||||
// Through `onCreateWorker`: tracked in `__wasiWorkers`, unref'd, and
|
||||
// handed the crash flags like any pool Worker.
|
||||
worker = manager.allocateUnusedWorker()
|
||||
manager
|
||||
.loadWasmModuleToWorker(worker)
|
||||
.then(undefined, () => __removeWasiPoolWorker(manager, worker))
|
||||
} catch {
|
||||
if (worker !== undefined) {
|
||||
__removeWasiPoolWorker(manager, worker)
|
||||
try {
|
||||
manager.terminateWorker(worker)
|
||||
__untrackWasiWorkerOnExit(worker)
|
||||
} catch {}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function __publishWasiThreadPoolReconcile(exports) {
|
||||
Object.defineProperty(exports, __wasiThreadPoolReconcileSymbol, {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
value: __reconcileWasiThreadPool,
|
||||
writable: false,
|
||||
})
|
||||
}
|
||||
|
||||
const __cwd = process.cwd()
|
||||
const __rootDir = __nodePath.parse(__cwd).root
|
||||
const __hostRoot =
|
||||
@@ -2011,7 +2165,7 @@ try {
|
||||
const __finishAutoDestroyCapture = __captureEmnapiAutoDestroyListener()
|
||||
try {
|
||||
__emnapiContext = __wrapEmnapiContextDestroyForSettlement(
|
||||
__emnapiCreateContext({ autoDestroy: false }),
|
||||
__emnapiCreateContext({ autoDestroy: false, features: { setImmediate: __wasiSetImmediate } }),
|
||||
__prepareWasmEnvCleanup,
|
||||
__isPreparingWasmEnvCleanup,
|
||||
)
|
||||
@@ -2108,6 +2262,7 @@ try {
|
||||
},
|
||||
}))
|
||||
__publishWasiDispose(__napiModule.exports)
|
||||
__publishWasiThreadPoolReconcile(__napiModule.exports)
|
||||
// The CommonJS tail below aliases `__napiModule.exports`; a named module
|
||||
// export does not travel with it, so carry the marker on the binding itself
|
||||
// too. Three things pin the stamp to exactly this spot:
|
||||
@@ -2136,6 +2291,11 @@ try {
|
||||
__runWasiInitializationRollback(rollback)
|
||||
throw rollback.error
|
||||
}
|
||||
// Preload the pool for the count the addon configured during registration.
|
||||
// See `__reconcileWasiThreadPool`.
|
||||
try {
|
||||
__reconcileWasiThreadPool()
|
||||
} catch {}
|
||||
module.exports = __napiModule.exports
|
||||
module.exports.Severity = __napiModule.exports.Severity
|
||||
module.exports.HelperMode = __napiModule.exports.HelperMode
|
||||
|
||||
Generated
+17
-17
@@ -174,8 +174,8 @@ catalogs:
|
||||
specifier: 2.0.0-alpha.5
|
||||
version: 2.0.0-alpha.5
|
||||
'@napi-rs/cli':
|
||||
specifier: 3.10.6
|
||||
version: 3.10.6
|
||||
specifier: 3.10.7
|
||||
version: 3.10.7
|
||||
'@napi-rs/wasm-runtime':
|
||||
specifier: 1.2.4
|
||||
version: 1.2.4
|
||||
@@ -218,7 +218,7 @@ importers:
|
||||
devDependencies:
|
||||
'@napi-rs/cli':
|
||||
specifier: 'catalog:'
|
||||
version: 3.10.6(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@25.0.2)(emnapi@2.0.0-alpha.5)
|
||||
version: 3.10.7(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@25.0.2)(emnapi@2.0.0-alpha.5)
|
||||
emnapi:
|
||||
specifier: 2.0.0-alpha.5
|
||||
version: 2.0.0-alpha.5
|
||||
@@ -252,7 +252,7 @@ importers:
|
||||
version: 0.18.5
|
||||
'@napi-rs/cli':
|
||||
specifier: 'catalog:'
|
||||
version: 3.10.6(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)
|
||||
version: 3.10.7(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)
|
||||
'@oxapps/shared':
|
||||
specifier: workspace:*
|
||||
version: link:../shared
|
||||
@@ -297,7 +297,7 @@ importers:
|
||||
version: 8.0.6
|
||||
'@napi-rs/cli':
|
||||
specifier: 'catalog:'
|
||||
version: 3.10.6(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)
|
||||
version: 3.10.7(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)
|
||||
'@oxapps/shared':
|
||||
specifier: workspace:*
|
||||
version: link:../shared
|
||||
@@ -411,7 +411,7 @@ importers:
|
||||
version: 2.0.0-alpha.5
|
||||
'@napi-rs/cli':
|
||||
specifier: 'catalog:'
|
||||
version: 3.10.6(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)
|
||||
version: 3.10.7(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)
|
||||
'@types/node':
|
||||
specifier: 'catalog:'
|
||||
version: 24.1.0
|
||||
@@ -439,7 +439,7 @@ importers:
|
||||
version: 2.0.0-alpha.5
|
||||
'@napi-rs/cli':
|
||||
specifier: 'catalog:'
|
||||
version: 3.10.6(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)
|
||||
version: 3.10.7(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)
|
||||
'@napi-rs/wasm-runtime':
|
||||
specifier: 'catalog:'
|
||||
version: 1.2.4(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)
|
||||
@@ -485,7 +485,7 @@ importers:
|
||||
version: 2.0.0-alpha.5
|
||||
'@napi-rs/cli':
|
||||
specifier: 'catalog:'
|
||||
version: 3.10.6(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@25.0.2)(emnapi@2.0.0-alpha.5)
|
||||
version: 3.10.7(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@25.0.2)(emnapi@2.0.0-alpha.5)
|
||||
publint:
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.25
|
||||
@@ -500,7 +500,7 @@ importers:
|
||||
version: 2.0.0-alpha.5
|
||||
'@napi-rs/cli':
|
||||
specifier: 'catalog:'
|
||||
version: 3.10.6(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)
|
||||
version: 3.10.7(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)
|
||||
'@types/node':
|
||||
specifier: 'catalog:'
|
||||
version: 24.1.0
|
||||
@@ -521,7 +521,7 @@ importers:
|
||||
version: 2.0.0-alpha.5
|
||||
'@napi-rs/cli':
|
||||
specifier: 'catalog:'
|
||||
version: 3.10.6(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)
|
||||
version: 3.10.7(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)
|
||||
'@types/node':
|
||||
specifier: 'catalog:'
|
||||
version: 24.1.0
|
||||
@@ -542,7 +542,7 @@ importers:
|
||||
version: 2.0.0-alpha.5
|
||||
'@napi-rs/cli':
|
||||
specifier: 'catalog:'
|
||||
version: 3.10.6(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)
|
||||
version: 3.10.7(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)
|
||||
'@types/node':
|
||||
specifier: 'catalog:'
|
||||
version: 24.1.0
|
||||
@@ -610,7 +610,7 @@ importers:
|
||||
devDependencies:
|
||||
'@napi-rs/cli':
|
||||
specifier: 'catalog:'
|
||||
version: 3.10.6(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@25.0.2)(emnapi@2.0.0-alpha.5)
|
||||
version: 3.10.7(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@25.0.2)(emnapi@2.0.0-alpha.5)
|
||||
|
||||
tasks/compat_data:
|
||||
devDependencies:
|
||||
@@ -1537,8 +1537,8 @@ packages:
|
||||
resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
'@napi-rs/cli@3.10.6':
|
||||
resolution: {integrity: sha512-8Vj+XgPHtHWiPjyj+RXKKdAiC/rSpSrzT7OabrS4xGKu6yEnj6sVJ2Bqzmg3xHYqopXCE6P3ylcuek7wV4zFWA==}
|
||||
'@napi-rs/cli@3.10.7':
|
||||
resolution: {integrity: sha512-wTocvxyQeqqvKVOzocHMUs2X27Nc2EcRnpH0ojK8uSnzUAYSehfGSYgRheZNcU1ltY2yqJ/R18Zt2YbKXqYMmw==}
|
||||
engines: {node: ^20.17.0 || ^22.13.0 || >= 23.5.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -7143,7 +7143,7 @@ snapshots:
|
||||
|
||||
'@lukeed/csprng@1.1.0': {}
|
||||
|
||||
'@napi-rs/cli@3.10.6(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)':
|
||||
'@napi-rs/cli@3.10.7(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)':
|
||||
dependencies:
|
||||
'@inquirer/prompts': 8.5.2(@types/node@24.1.0)
|
||||
'@napi-rs/cross-toolchain': 1.0.3(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)
|
||||
@@ -7175,7 +7175,7 @@ snapshots:
|
||||
- '@types/node'
|
||||
- supports-color
|
||||
|
||||
'@napi-rs/cli@3.10.6(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)':
|
||||
'@napi-rs/cli@3.10.7(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@24.1.0)(emnapi@2.0.0-alpha.5)':
|
||||
dependencies:
|
||||
'@inquirer/prompts': 8.5.2(@types/node@24.1.0)
|
||||
'@napi-rs/cross-toolchain': 1.0.3(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)
|
||||
@@ -7207,7 +7207,7 @@ snapshots:
|
||||
- '@types/node'
|
||||
- supports-color
|
||||
|
||||
'@napi-rs/cli@3.10.6(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@25.0.2)(emnapi@2.0.0-alpha.5)':
|
||||
'@napi-rs/cli@3.10.7(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)(@types/node@25.0.2)(emnapi@2.0.0-alpha.5)':
|
||||
dependencies:
|
||||
'@inquirer/prompts': 8.5.2(@types/node@25.0.2)
|
||||
'@napi-rs/cross-toolchain': 1.0.3(@emnapi/core@2.0.0-alpha.5)(@emnapi/runtime@2.0.0-alpha.5)
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ catalog:
|
||||
"@emnapi/runtime": 2.0.0-alpha.5
|
||||
emnapi: 2.0.0-alpha.5
|
||||
"@arethetypeswrong/core": 0.18.5
|
||||
"@napi-rs/cli": 3.10.6
|
||||
"@napi-rs/cli": 3.10.7
|
||||
"@napi-rs/wasm-runtime": 1.2.4
|
||||
"@types/node": 24.1.0
|
||||
cross-env: ^10.1.0
|
||||
|
||||
Reference in new issue
Block a user