@phron/image-wasm32-wasip1 0.0.0-stage → 0.1.0

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.
@@ -0,0 +1,1865 @@
1
+ import {
2
+ emnapiAsyncWorkPlugin as __emnapiAsyncWorkPlugin,
3
+ emnapiTSFNPlugin as __emnapiTSFNPlugin,
4
+ instantiateNapiModule as __emnapiInstantiateNapiModule,
5
+ WASI as __WASI,
6
+ } from '@napi-rs/wasm-runtime'
7
+ import { createContext as __emnapiCreateContext } from '@emnapi/runtime'
8
+
9
+
10
+ export const WASM_MEMORY = Object.freeze({
11
+ initialPages: 1024,
12
+ maximumPages: 65536,
13
+ pageBytes: 65536,
14
+ initialBytes: 1024 * 65536,
15
+ maximumBytes: 65536 * 65536,
16
+ })
17
+
18
+ let __createdInstances = 0
19
+ let __liveInstances = 0
20
+
21
+ /**
22
+ * Counters for instances created by THIS module evaluation, not process-wide:
23
+ * a second bundled copy of this loader keeps its own. Only successfully
24
+ * created instances are counted, and `liveInstances` drops when an instance's
25
+ * `dispose()` resolves.
26
+ *
27
+ * `declaredInitialMemoryBytes` is declared address space, not a host's
28
+ * committed-memory metric; pair it with host telemetry rather than treating it
29
+ * as a quota.
30
+ */
31
+ export function getDeferredRuntimeStats() {
32
+ return Object.freeze({
33
+ createdInstances: __createdInstances,
34
+ liveInstances: __liveInstances,
35
+ declaredInitialMemoryBytes: WASM_MEMORY.initialBytes,
36
+ })
37
+ }
38
+
39
+ const __arrayBufferByteLengthGetter = Object.getOwnPropertyDescriptor(
40
+ ArrayBuffer.prototype,
41
+ 'byteLength',
42
+ ).get
43
+ const __memoryBufferGetter = Object.getOwnPropertyDescriptor(
44
+ WebAssembly.Memory.prototype,
45
+ 'buffer',
46
+ ).get
47
+ // One managed initialization per Memory, success or failure: an attempt that
48
+ // throws may already have written into linear memory, so the bytes are not a
49
+ // clean slate for a second instance. Module-local, like the counters above.
50
+ const __claimedMemories = new WeakSet()
51
+
52
+ function __resolveInstanceMemory(__options) {
53
+ const __provided = __options == null ? undefined : __options.memory
54
+ if (__provided === undefined || __provided === null) {
55
+ // Page counts are handed to the engine unvalidated: it already rejects a
56
+ // negative, over-4GiB or below-maximum value with a precise message, and a
57
+ // second set of bounds here would only drift from it.
58
+ const __allocated = new WebAssembly.Memory({
59
+ initial:
60
+ __options != null && __options.initialMemoryPages !== undefined
61
+ ? __options.initialMemoryPages
62
+ : WASM_MEMORY.initialPages,
63
+ maximum:
64
+ __options != null && __options.maximumMemoryPages !== undefined
65
+ ? __options.maximumMemoryPages
66
+ : WASM_MEMORY.maximumPages,
67
+ })
68
+ // Claimed like a caller-provided one. The handle publishes it as
69
+ // `instance.memory`, so handing it back to `createInstance()` is as easy
70
+ // as passing your own twice, and it would put two live instances on one
71
+ // linear memory: each initialization rewrites the emnapi/WASI state the
72
+ // other is still running on.
73
+ __claimedMemories.add(__allocated)
74
+ return __allocated
75
+ }
76
+ if (
77
+ __options.initialMemoryPages !== undefined ||
78
+ __options.maximumMemoryPages !== undefined
79
+ ) {
80
+ throw new TypeError(
81
+ 'Pass either memory or initialMemoryPages/maximumMemoryPages, not both',
82
+ )
83
+ }
84
+ let __buffer
85
+ try {
86
+ // Brand check: the getter throws for anything that is not a genuine
87
+ // WebAssembly.Memory, including a cross-realm look-alike object.
88
+ __buffer = Reflect.apply(__memoryBufferGetter, __provided, [])
89
+ } catch {
90
+ throw new TypeError('memory must be an unshared WebAssembly.Memory')
91
+ }
92
+ try {
93
+ // Throws for a SharedArrayBuffer. This loader has no threads, and shared
94
+ // growth does not detach: external views handed to the addon would
95
+ // silently outlive the bytes they describe.
96
+ Reflect.apply(__arrayBufferByteLengthGetter, __buffer, [])
97
+ } catch {
98
+ throw new TypeError(
99
+ 'The deferred loader requires an unshared WebAssembly.Memory',
100
+ )
101
+ }
102
+ // The intrinsic getters above accept a genuine Memory from ANY realm, but
103
+ // the loader's dependencies do not: `WASI.setMemory` in
104
+ // `@napi-rs/wasm-runtime` and emnapi identify a Memory with a realm-local
105
+ // `instanceof`. A Memory built in another realm (a `node:vm` context, a
106
+ // same-origin iframe) would pass every check here and only fail deep inside
107
+ // initialization. Reject it up front, and before the claim below, so the
108
+ // caller keeps it usable in the realm that made it.
109
+ if (!(__provided instanceof WebAssembly.Memory)) {
110
+ throw new TypeError(
111
+ 'memory must be a WebAssembly.Memory created in the same realm as this loader',
112
+ )
113
+ }
114
+ if (__claimedMemories.has(__provided)) {
115
+ throw new TypeError(
116
+ 'This WebAssembly.Memory has already been used for a deferred initialization attempt and cannot be reused, including after a failed initialization or a disposal',
117
+ )
118
+ }
119
+ // Last step, after every check: a rejected option bag must leave the Memory
120
+ // unclaimed, or a caller could not fix the call and retry with it.
121
+ __claimedMemories.add(__provided)
122
+ return __provided
123
+ }
124
+
125
+ export const __napiBindingTarget = 'wasm32-wasip1'
126
+ function __napiStampBindingTarget(exportsObject, target) {
127
+ if (
128
+ Object.prototype.hasOwnProperty.call(exportsObject, '__napiBindingTarget')
129
+ ) {
130
+ if (exportsObject.__napiBindingTarget === target) {
131
+ // Already ours: the root entry aliases the object it loaded, so a WASI
132
+ // fallback candidate — or a `NAPI_RS_NATIVE_LIBRARY_PATH` override that
133
+ // is a generated loader — arrives already stamped with this same value.
134
+ return target
135
+ }
136
+ const error = new Error(
137
+ '`__napiBindingTarget` is reserved by the generated binding loader, but the loaded binding already exports it. Rename the export, e.g. #[napi(js_name = "...")].',
138
+ )
139
+ error.code = 'ERR_NAPI_BINDING_TARGET_CONFLICT'
140
+ throw error
141
+ }
142
+ if (!Object.isExtensible(exportsObject)) {
143
+ // A `#[napi(module_exports)]` hook may seal or freeze this object
144
+ // (`Object::seal` / `Object::freeze`). Reporting the artifact is metadata,
145
+ // never a reason to fail an otherwise successful load, so the stamp is
146
+ // skipped. What a consumer still sees then follows the entry point: the
147
+ // browser and deferred loaders declare `__napiBindingTarget` at module
148
+ // level and go on reporting it, while the CommonJS entries hand back this
149
+ // very object as `module.exports`, so there the value is absent.
150
+ return target
151
+ }
152
+ try {
153
+ // [[Define]], not [[Set]]: an ordinary assignment walks the prototype
154
+ // chain, so an inherited accessor could swallow the value or throw and
155
+ // fail an otherwise successful load. The descriptor is what a successful
156
+ // assignment would have produced.
157
+ Object.defineProperty(exportsObject, '__napiBindingTarget', {
158
+ configurable: true,
159
+ enumerable: true,
160
+ value: target,
161
+ writable: true,
162
+ })
163
+ } catch {
164
+ // Same rule as the non-extensible skip above: reporting the artifact is
165
+ // metadata, never a reason to fail an otherwise successful load. An exotic
166
+ // object (a Proxy whose defineProperty trap refuses) is skipped, not
167
+ // thrown over.
168
+ }
169
+ // The CommonJS loaders assign this return value so `cjs-module-lexer` — and
170
+ // therefore Node's CJS -> ESM named export detection — can see
171
+ // `__napiBindingTarget` statically.
172
+ return target
173
+ }
174
+
175
+ /**
176
+ * Deferred, workerd-safe instantiation: no top-level I/O, no compile-from-bytes.
177
+ * Accepts ONLY a precompiled WebAssembly.Module, or a Promise resolving to one
178
+ * (e.g. `import mod from './phron-image.wasm32-wasip1.wasm'` under a CompiledWasm
179
+ * module rule / wrangler module import). Byte buffers, URLs and Response
180
+ * objects are rejected: they require dynamic Wasm compilation, which
181
+ * Cloudflare Workers disallows.
182
+ */
183
+ async function __resolveModule(__wasmInput) {
184
+ const __module = await __wasmInput
185
+ // Brand check, not `instanceof`: `WebAssembly.Module.imports` throws unless
186
+ // its argument is a genuine WebAssembly.Module, so prototype-spoofed byte
187
+ // buffers are rejected while cross-realm Module instances are accepted.
188
+ try {
189
+ WebAssembly.Module.imports(__module)
190
+ } catch {
191
+ throw new TypeError(
192
+ "instantiate() and createInstance() expect a precompiled WebAssembly.Module (or a Promise resolving to one), " +
193
+ "e.g. import mod from './phron-image.wasm32-wasip1.wasm' under a CompiledWasm module rule / wrangler module import. " +
194
+ "Byte buffers, URLs and Response objects require dynamic Wasm compilation, which Cloudflare Workers disallows.",
195
+ )
196
+ }
197
+ return __module
198
+ }
199
+
200
+ let __normalizedModules
201
+
202
+ function __rememberNormalizedModule(__module, __normalizedModule) {
203
+ if (!__normalizedModules) {
204
+ __normalizedModules = new WeakMap()
205
+ }
206
+ __normalizedModules.set(__module, __normalizedModule)
207
+ return __normalizedModule
208
+ }
209
+
210
+ async function __normalizeModuleForEmnapi(__module) {
211
+ if (__module instanceof WebAssembly.Module) {
212
+ return __module
213
+ }
214
+ if (__normalizedModules) {
215
+ const __normalizedModule = __normalizedModules.get(__module)
216
+ if (__normalizedModule) {
217
+ return __normalizedModule
218
+ }
219
+ }
220
+ // @emnapi/core currently performs realm-local `instanceof` checks after
221
+ // accepting the module. Structured cloning preserves compiled code without
222
+ // compiling bytes and produces a Module owned by the current realm.
223
+ if (typeof structuredClone === 'function') {
224
+ try {
225
+ const __normalizedModule = structuredClone(__module)
226
+ if (__normalizedModule instanceof WebAssembly.Module) {
227
+ return __rememberNormalizedModule(__module, __normalizedModule)
228
+ }
229
+ } catch {}
230
+ }
231
+ // MessageChannel uses the same structured-clone semantics and covers older
232
+ // browser/Node hosts that expose it but not the structuredClone function.
233
+ if (typeof MessageChannel === 'function') {
234
+ let __channel
235
+ try {
236
+ __channel = new MessageChannel()
237
+ const __normalizedModule = await new Promise((resolve, reject) => {
238
+ __channel.port1.onmessage = (event) => resolve(event.data)
239
+ __channel.port1.onmessageerror = () =>
240
+ reject(new TypeError('Failed to clone WebAssembly.Module'))
241
+ try {
242
+ __channel.port2.postMessage(__module)
243
+ } catch (error) {
244
+ reject(error)
245
+ }
246
+ })
247
+ if (__normalizedModule instanceof WebAssembly.Module) {
248
+ return __rememberNormalizedModule(__module, __normalizedModule)
249
+ }
250
+ } catch {
251
+ } finally {
252
+ try {
253
+ __channel?.port1.close()
254
+ } catch {}
255
+ try {
256
+ __channel?.port2.close()
257
+ } catch {}
258
+ }
259
+ }
260
+ // Last-resort compatibility for genuine, extensible foreign Modules.
261
+ try {
262
+ Object.setPrototypeOf(__module, WebAssembly.Module.prototype)
263
+ } catch {}
264
+ if (__module instanceof WebAssembly.Module) {
265
+ return __module
266
+ }
267
+ throw new TypeError(
268
+ 'This host cannot normalize a cross-realm WebAssembly.Module; ' +
269
+ 'provide structuredClone or MessageChannel support.',
270
+ )
271
+ }
272
+
273
+ function __wrapEmnapiContextDestroyForSettlement(
274
+ context,
275
+ prepareEnvCleanup,
276
+ isPreparingEnvCleanup,
277
+ ) {
278
+ let destroy
279
+ try {
280
+ destroy = context.destroy
281
+ } catch {
282
+ return context
283
+ }
284
+ if (typeof destroy !== 'function') {
285
+ return context
286
+ }
287
+ try {
288
+ Object.defineProperty(context, 'destroy', {
289
+ configurable: true,
290
+ enumerable: false,
291
+ writable: true,
292
+ value: function () {
293
+ // Reentered from a promise hook that fired inside the barrier: the
294
+ // frame running it destroys as soon as it returns.
295
+ if (isPreparingEnvCleanup?.()) {
296
+ return
297
+ }
298
+ prepareEnvCleanup?.()
299
+ return Reflect.apply(destroy, this, arguments)
300
+ },
301
+ })
302
+ } catch {}
303
+ return context
304
+ }
305
+
306
+ function __captureEmnapiAutoDestroyListener(__process) {
307
+ if (
308
+ !__process ||
309
+ typeof __process.prependListener !== 'function' ||
310
+ typeof __process.removeListener !== 'function'
311
+ ) {
312
+ return
313
+ }
314
+ let __autoDestroyListener
315
+ const __captureListener = (__event, __listener) => {
316
+ if (__event === 'beforeExit' && __autoDestroyListener === undefined) {
317
+ __autoDestroyListener = __listener
318
+ }
319
+ }
320
+ try {
321
+ // Run before existing newListener hooks so a hook that registers its own
322
+ // beforeExit listener cannot be mistaken for emnapi's registration.
323
+ __process.prependListener('newListener', __captureListener)
324
+ } catch {
325
+ return
326
+ }
327
+ return () => {
328
+ try {
329
+ __process.removeListener('newListener', __captureListener)
330
+ } catch {}
331
+ if (__autoDestroyListener !== undefined) {
332
+ try {
333
+ __process.removeListener('beforeExit', __autoDestroyListener)
334
+ } catch {}
335
+ }
336
+ }
337
+ }
338
+
339
+ function __attachCleanupError(__error, __cleanupError) {
340
+ try {
341
+ if (
342
+ __error &&
343
+ (typeof __error === 'object' || typeof __error === 'function') &&
344
+ __error.cause === undefined
345
+ ) {
346
+ __error.cause = __cleanupError
347
+ }
348
+ } catch {}
349
+ }
350
+
351
+ // Mirror the primitive @emnapi/core schedules its threadsafe-function dispatch
352
+ // on, so the drain turns below interleave with that dispatch instead of racing
353
+ // ahead of it on a faster queue.
354
+ const __scheduleMacrotask = (function () {
355
+ if (typeof setImmediate === 'function') {
356
+ return function (__callback) {
357
+ setImmediate(__callback)
358
+ }
359
+ }
360
+ const __MessageChannel = globalThis.MessageChannel
361
+ if (typeof __MessageChannel === 'function') {
362
+ return function (__callback) {
363
+ const __channel = new __MessageChannel()
364
+ __channel.port1.onmessage = function () {
365
+ __channel.port1.onmessage = null
366
+ try {
367
+ __channel.port1.close()
368
+ } catch {}
369
+ try {
370
+ __channel.port2.close()
371
+ } catch {}
372
+ __callback()
373
+ }
374
+ __channel.port2.postMessage(null)
375
+ }
376
+ }
377
+ return function (__callback) {
378
+ setTimeout(__callback, 0)
379
+ }
380
+ })()
381
+
382
+ // Turns to wait for while the addon still reports queued settlements. Reaching
383
+ // zero is the only success. A counter still nonzero at this bound rejects the
384
+ // disposal as retryable (`ERR_NAPI_WASI_CLEANUP_PENDING`) rather than
385
+ // destroying the context over a still-queued settlement — the wait stays
386
+ // bounded either way.
387
+ const __WASM_ENV_CLEANUP_DRAIN_TURNS = 128
388
+ // Without `napi_wasm_env_cleanup_pending` the queue is not observable. Fall
389
+ // back to the number of turns @emnapi/core needs to coalesce and dispatch a
390
+ // call made on this thread (two), plus a margin.
391
+ const __WASM_ENV_CLEANUP_BLIND_DRAIN_TURNS = 4
392
+
393
+ /**
394
+ * `napi_prepare_wasm_env_cleanup` only *queues* the promise settlements of the
395
+ * tasks it cancelled: `napi_call_threadsafe_function` appends to the
396
+ * threadsafe-function queue, and @emnapi/core dispatches that queue from a
397
+ * macrotask — two coalescing turns later, even for a call made on this very
398
+ * thread. `Context.destroy()` then runs the threadsafe function's cleanup hook,
399
+ * which drains the queue with a null env and *discards* whatever is still in it.
400
+ *
401
+ * So destroying without yielding first strands exactly the promises the barrier
402
+ * exists to settle. Yield real event-loop turns until the addon reports the
403
+ * queue empty; microtask checkpoints cannot help, no number of them lets a
404
+ * macrotask run.
405
+ *
406
+ * Returns nothing when there is nothing to wait for, which keeps disposal
407
+ * synchronous in the common case.
408
+ */
409
+ function __drainWasmEnvCleanup(__instance) {
410
+ const __pending = __instance?.exports.napi_wasm_env_cleanup_pending
411
+ const __observable = typeof __pending === 'function'
412
+ if (__observable) {
413
+ let __queued
414
+ try {
415
+ __queued = __pending()
416
+ } catch {
417
+ return
418
+ }
419
+ if (!__queued) {
420
+ return
421
+ }
422
+ }
423
+ const __limit = __observable
424
+ ? __WASM_ENV_CLEANUP_DRAIN_TURNS
425
+ : __WASM_ENV_CLEANUP_BLIND_DRAIN_TURNS
426
+ return (async () => {
427
+ let __queued = 0
428
+ for (let __turn = 0; __turn < __limit; __turn++) {
429
+ await new Promise((resolve) => {
430
+ __scheduleMacrotask(resolve)
431
+ })
432
+ if (!__observable) {
433
+ continue
434
+ }
435
+ try {
436
+ __queued = __pending()
437
+ } catch {
438
+ return
439
+ }
440
+ if (!__queued) {
441
+ return
442
+ }
443
+ }
444
+ if (!__observable) {
445
+ // Blind wait: without `napi_wasm_env_cleanup_pending` the bound IS the
446
+ // contract — there is nothing to consult, so finishing the turns is
447
+ // finishing the drain.
448
+ return
449
+ }
450
+ // The counter is still nonzero after every turn the bound allows. The wait
451
+ // stays bounded — but claiming success here would be indistinguishable from
452
+ // the stranding this drain exists to prevent: disposal would go on to
453
+ // destroy the context, whose cleanup hook discards the still-queued
454
+ // settlement with a null env, and the promise it was for hangs forever.
455
+ // Reject instead, as a retryable cleanup failure: `__prepareForDisposal`
456
+ // leaves its drained flag unset, dispose() (and the instantiation-failure
457
+ // path) decline to destroy, and a later dispose() runs the drain again — by
458
+ // which time the queue has usually been delivered. A counter that is
459
+ // somehow stuck nonzero therefore costs each attempt at most another
460
+ // bounded wait and a rejection, never a stranded promise; the managed
461
+ // beforeExit destroyer still reclaims the context.
462
+ const __drainError = new Error(
463
+ 'the wasm environment still reports ' +
464
+ __queued +
465
+ ' queued settlement(s) after ' +
466
+ __limit +
467
+ ' event-loop turns; the context was not destroyed - retry dispose() to wait for the queue again',
468
+ )
469
+ __drainError.code = 'ERR_NAPI_WASI_CLEANUP_PENDING'
470
+ throw __drainError
471
+ })()
472
+ }
473
+
474
+ // A real, *referenced* timer for the async-work wait below, which polls the
475
+ // addon rather than interleaving with the @emnapi/core dispatch: a zero-delay
476
+ // macrotask there would spin the loop instead of yielding it. Falls back to the
477
+ // macrotask scheduler on a host without timers.
478
+ function __scheduleTimer(__callback, __delay) {
479
+ const __setTimer = globalThis.setTimeout
480
+ if (typeof __setTimer !== 'function') {
481
+ __scheduleMacrotask(__callback)
482
+ return
483
+ }
484
+ try {
485
+ __setTimer(__callback, __delay)
486
+ } catch {
487
+ __scheduleMacrotask(__callback)
488
+ }
489
+ }
490
+
491
+ // A real, referenced timer rather than a zero-delay macrotask, for the same
492
+ // reason the async-work wait uses one: this polls the addon instead of
493
+ // interleaving with the @emnapi/core dispatch, so a zero-delay turn would spin
494
+ // the loop instead of yielding it.
495
+ const __WASM_RUNTIME_WORK_POLL_INTERVAL_MS = 1
496
+ // Arrivals it takes before the poll paces on the host's timers alone. One
497
+ // proves nothing: a timer armed before the host's timers stopped still fires.
498
+ const __WASM_RUNTIME_WORK_POLL_TRUSTED_ARRIVALS = 2
499
+ // How long a parked turn's own timer must already have been due before a
500
+ // backup that runs calls it dropped. Slack, not a deadline: a timer is due
501
+ // against the event loop's clock, which is read once per iteration, while
502
+ // these are `Date.now()` readings taken part-way through one, so the two
503
+ // drift apart by however long the loop has been inside the current iteration.
504
+ const __WASM_RUNTIME_WORK_POLL_STALL_MS = 50
505
+ // How long a backup itself waits. What is left of it after the slack and one
506
+ // interval — 149 ms — has to cover the *two* poll turns that can separate a
507
+ // parked turn from the last backup armed while the host's timers still
508
+ // worked, so the ceiling on a single turn is half of it. See the invariant on
509
+ // `__armWasmRuntimePollStallBackup`.
510
+ const __WASM_RUNTIME_WORK_POLL_BACKUP_MS = 200
511
+
512
+ /**
513
+ * Pacing state for one runtime-work poll.
514
+ *
515
+ * Per poll, never per module: whether the host's timers arrive is not a
516
+ * property of the module. A host can lose its timers between two disposals,
517
+ * and in the deferred shape every instance shares this module — one healthy
518
+ * instance must not disarm the fallback for the next one.
519
+ */
520
+ function __createWasmRuntimePollPace() {
521
+ return {
522
+ // Timers armed by *this* poll that have actually arrived.
523
+ arrivals: 0,
524
+ // The turn waiting on a timer alone *right now* — undefined whenever no
525
+ // turn is parked — and when that turn's own timer came due.
526
+ settleTurn: undefined,
527
+ turnTimerDueAt: 0,
528
+ }
529
+ }
530
+
531
+ /**
532
+ * The backup that ends a turn whose timer is never going to arrive.
533
+ *
534
+ * Once the poll paces on the timer alone it has nothing left to fall back on
535
+ * if the host's timers stop mid-poll: the turn that armed the dead timer is
536
+ * the turn that parks, and a parked poll schedules nothing that could notice.
537
+ * So every turn arms one of these before it yields, and each one compares due
538
+ * times instead of measuring how long the parked turn has been waiting.
539
+ *
540
+ * Invariant: a parked turn is ended by the newest backup that was armed while
541
+ * the host's timers still worked, and a backup ends a turn only when that
542
+ * turn's own timer was already due a whole window before the backup itself.
543
+ * Neither half turns on how far apart the arms happen to fall — what bounds
544
+ * the rescue is how far back that newest live backup is:
545
+ *
546
+ * - *Ends it.* Hosts run timers in due order, so a backup that runs while a
547
+ * turn due a whole window earlier is still parked proves that turn's timer
548
+ * was dropped rather than merely late. That same comparison is what leaves a
549
+ * healthy host alone: there the turn's timer has already run and cleared
550
+ * `settleTurn` before any backup due after it can look.
551
+ * - *Two turns back, not one.* A turn that ended does not prove its own timer
552
+ * arrived: until `…_TRUSTED_ARRIVALS` is reached every turn arms both
553
+ * primitives and the macrotask wins, so such a turn can end with its own
554
+ * timer — and the backup armed one line before it — already dead. The
555
+ * arrival that then flips the poll onto the timer alone can itself be a
556
+ * timer armed before the host's timers died. So the turn that parks can sit
557
+ * two turns past the last live arm, and the newest live backup is due
558
+ * `…_BACKUP_MS` less *two* turn lengths after that turn's own timer.
559
+ * Arming on every turn is what holds it to two, rather than however far back
560
+ * a throttle last let one through.
561
+ * - *Ceiling.* Coverage therefore holds while two consecutive poll turns fit
562
+ * inside `…_BACKUP_MS` less the slack and one interval: 149 ms, so 74 ms
563
+ * per turn (measured: a 74 ms turn is still rescued, a 75 ms one parks).
564
+ * Past that the turn stays parked and the disposal promise never settles.
565
+ * The bound is deliberate: reaching it takes a host that drops timers
566
+ * mid-poll *and* keeps every poll turn busy for more than 74 ms, and neither
567
+ * Node nor WebContainer — the hosts that run the threaded artifact — does
568
+ * the second.
569
+ *
570
+ * The poll then goes back to arming both primitives until two fresh arrivals
571
+ * prove the timers again. A host that stops running the timers it has
572
+ * *already* accepted leaves nothing to fire, and the disposal promise stays
573
+ * pending rather than wedging the thread — the same outcome as a blocking
574
+ * closure that never returns. Unreferenced wherever the host allows it: the
575
+ * poll's own turn timers are what keep the loop alive, never these.
576
+ */
577
+ function __armWasmRuntimePollStallBackup(__pace) {
578
+ const __setTimer = globalThis.setTimeout
579
+ if (typeof __setTimer !== 'function') {
580
+ // Nothing to back up: `__scheduleTimer` is on the macrotask channel
581
+ // already, and that one cannot park.
582
+ return
583
+ }
584
+ // Read before arming, so this never claims to be due earlier than the timer
585
+ // actually is: a backup ends a turn only when it is provably due after it.
586
+ const __dueAt = Date.now() + __WASM_RUNTIME_WORK_POLL_BACKUP_MS
587
+ let __handle
588
+ try {
589
+ __handle = __setTimer(() => {
590
+ const __settleTurn = __pace.settleTurn
591
+ if (
592
+ !__settleTurn ||
593
+ __pace.turnTimerDueAt > __dueAt - __WASM_RUNTIME_WORK_POLL_STALL_MS
594
+ ) {
595
+ // No turn is parked, or the parked one's timer came due too close to
596
+ // this backup to call it dropped — it may still arrive, and the turn
597
+ // that armed it armed a backup due a whole window after *that*.
598
+ return
599
+ }
600
+ __pace.arrivals = 0
601
+ __pace.settleTurn = undefined
602
+ __settleTurn()
603
+ }, __WASM_RUNTIME_WORK_POLL_BACKUP_MS)
604
+ } catch {
605
+ return
606
+ }
607
+ if (__handle && typeof __handle.unref === 'function') {
608
+ try {
609
+ __handle.unref()
610
+ } catch {}
611
+ }
612
+ }
613
+
614
+ /**
615
+ * One turn of the runtime-work poll.
616
+ *
617
+ * `__scheduleTimer` falls back to the macrotask scheduler when `setTimeout` is
618
+ * missing or throws, but not when it is present, returns a handle and never
619
+ * fires — fake timers in a test suite that disposes from an `afterEach`, or a
620
+ * host whose timers belong to an IO context that is already gone. That host
621
+ * would park this poll forever, and the poll is unbounded, so nothing would
622
+ * ever call `…_finish`.
623
+ *
624
+ * Arm both primitives until timers armed by this poll have arrived twice, and
625
+ * let whichever lands first end the turn; the loser resolves nothing. A host
626
+ * with working timers therefore pays the double arming for the first turn or
627
+ * two — the macrotask wins the race, but the timers behind it still arrive and
628
+ * are counted — and paces on the timer alone from then on, instead of spinning
629
+ * the loop on a zero-delay queue. A host whose timers never arrive keeps both,
630
+ * and the macrotask is what keeps the poll moving. A host whose timers stop
631
+ * after proving themselves is caught by `__armWasmRuntimePollStallBackup`,
632
+ * which ends the parked turn and puts this poll back on both.
633
+ */
634
+ function __yieldWasmRuntimePollTurn(__pace) {
635
+ // Armed before the turn yields, and by every turn: what rescues a parked
636
+ // turn has to have been armed while the host's timers still worked, and the
637
+ // turn that parks is the one whose own timer is already dead.
638
+ __armWasmRuntimePollStallBackup(__pace)
639
+ return new Promise((resolve) => {
640
+ let __settled = false
641
+ const __settle = () => {
642
+ if (__settled) {
643
+ return
644
+ }
645
+ __settled = true
646
+ if (__pace.settleTurn === __settle) {
647
+ // Nothing is parked any more: a backup running later must not read a
648
+ // due time this turn has already answered.
649
+ __pace.settleTurn = undefined
650
+ }
651
+ resolve()
652
+ }
653
+ __scheduleTimer(() => {
654
+ __pace.arrivals++
655
+ __settle()
656
+ }, __WASM_RUNTIME_WORK_POLL_INTERVAL_MS)
657
+ // Read next to the arming it describes; see
658
+ // `__armWasmRuntimePollStallBackup` for what the two due times mean.
659
+ const __turnTimerDueAt = Date.now() + __WASM_RUNTIME_WORK_POLL_INTERVAL_MS
660
+ if (__pace.arrivals < __WASM_RUNTIME_WORK_POLL_TRUSTED_ARRIVALS) {
661
+ __scheduleMacrotask(__settle)
662
+ return
663
+ }
664
+ // Paced by the timer alone from here; the backup is what ends this turn if
665
+ // the timer never arrives.
666
+ __pace.settleTurn = __settle
667
+ __pace.turnTimerDueAt = __turnTimerDueAt
668
+ })
669
+ }
670
+
671
+ /**
672
+ * Yield event-loop turns until the addon reports its runtime work finished.
673
+ *
674
+ * The window between `napi_prepare_wasm_env_cleanup_begin` and
675
+ * `…_finish` — the turns are the entire point of splitting the barrier, because
676
+ * on a threaded artifact the work `…_finish` joins can itself be waiting for a
677
+ * JavaScript turn from this very thread.
678
+ *
679
+ * Unbounded, for the same reason the async-work wait above is: giving up means
680
+ * calling `…_finish`, which joins on this thread, and the work it would join is
681
+ * the work waiting for a turn from this thread — so a bound does not end the
682
+ * wait, it only moves it somewhere the JavaScript thread can no longer be
683
+ * reached. A blocking closure that never returns keeps the disposal promise
684
+ * pending instead. The host contract is in `crates/async-runtime/README.md`: a
685
+ * blocking closure must never wait on a JavaScript turn.
686
+ */
687
+ async function __pollWasmRuntimeWork(__workPending) {
688
+ const __pace = __createWasmRuntimePollPace()
689
+ for (;;) {
690
+ await __yieldWasmRuntimePollTurn(__pace)
691
+ try {
692
+ if (!__workPending()) {
693
+ return
694
+ }
695
+ } catch {
696
+ // A trap is the only way this fails, and a trapped instance has no
697
+ // reachable work left. Stop polling and finish.
698
+ return
699
+ }
700
+ }
701
+ }
702
+
703
+ // How often to re-read `napi_wasm_async_work_pending` while waiting. The wait
704
+ // ends when the addon reports zero, so this only decides how promptly disposal
705
+ // notices — not how long it waits.
706
+ const __WASI_ASYNC_WORK_POLL_INTERVAL_MS = 1
707
+
708
+ /**
709
+ * Settles this instance's outstanding `napi_async_work` before the teardown
710
+ * that would strand it.
711
+ *
712
+ * The same hole the eager loaders had, and the same fix: the environment
713
+ * cleanup barrier covers promise settlements queued on the threadsafe-function
714
+ * queue and says nothing about `napi_async_work`, so destroying the context
715
+ * with a work still outstanding leaves its completion callback with nowhere to
716
+ * run and its promise unsettled forever.
717
+ *
718
+ * This flavor is threadless, so `compute` runs on the JavaScript thread inside
719
+ * the macrotask that dequeued it: while this is running, any outstanding work
720
+ * is queued rather than executing, and `napi_wasm_cancel_pending_async_work`
721
+ * can take all of it. The poll is still what decides when the drain is done —
722
+ * cancellation delivers those completions from a later macrotask, not
723
+ * synchronously.
724
+ *
725
+ * Per instance, from that instance's own exports: two instances of this loader
726
+ * have separate registries and must not wait on each other.
727
+ *
728
+ * Both exports are optional, so an addon built against a napi crate that
729
+ * predates them keeps the previous behavior.
730
+ *
731
+ * Returns nothing when there is nothing outstanding, which keeps disposal
732
+ * synchronous in the common case. No deadline: giving up would destroy the
733
+ * environment with a completion callback still owed.
734
+ */
735
+ function __drainInstanceAsyncWork(__instance) {
736
+ const __exports = __instance?.exports
737
+ const __pending = __exports?.napi_wasm_async_work_pending
738
+ const __cancelPending = __exports?.napi_wasm_cancel_pending_async_work
739
+ if (typeof __pending !== 'function' || typeof __cancelPending !== 'function') {
740
+ return
741
+ }
742
+
743
+ const __readPending = () => {
744
+ try {
745
+ return __pending()
746
+ } catch (__error) {
747
+ // A trap is the only way this call fails: it reads a counter and cannot
748
+ // allocate or call back into JavaScript. A trapped instance can no longer
749
+ // run anything, so its outstanding work is unreachable by definition and
750
+ // best-effort is the honest answer. Anything else means the export is not
751
+ // what this loader thinks it is — a defect worth surfacing.
752
+ if (__error instanceof globalThis.WebAssembly.RuntimeError) {
753
+ return 0
754
+ }
755
+ throw __error
756
+ }
757
+ }
758
+
759
+ if (!__readPending()) {
760
+ return
761
+ }
762
+ try {
763
+ __cancelPending()
764
+ } catch {
765
+ // Cancellation only bounds the wait. Failing it means waiting for the queue
766
+ // to run instead, which the poll below already does.
767
+ }
768
+ if (!__readPending()) {
769
+ return
770
+ }
771
+
772
+ return (async () => {
773
+ while (__readPending()) {
774
+ await new Promise((__resolve) => {
775
+ __scheduleTimer(__resolve, __WASI_ASYNC_WORK_POLL_INTERVAL_MS)
776
+ })
777
+ }
778
+ })()
779
+ }
780
+
781
+ function __createLifecycleReentryError(__operation) {
782
+ const __error = new Error(
783
+ __operation +
784
+ '() cannot run while an emnapi Context.destroy() call is still active; await the original cleanup promise instead.',
785
+ )
786
+ __error.code = 'ERR_NAPI_WASI_LIFECYCLE_REENTRY'
787
+ return __error
788
+ }
789
+
790
+ const __managedEmnapiContextDestroyers = new Set()
791
+ let __managedCleanupProcess
792
+ let __managedBeforeExitListener
793
+ let __managedDestroyPromise
794
+ let __managedDestroyersInFlight
795
+ let __managedBeforeExitRegistrationRetryCount = 0
796
+ let __managedBeforeExitRegistrationRetryScheduled = false
797
+ let __moduleLifecycleDestroyDepth = 0
798
+
799
+ function __removeManagedEmnapiCleanupListeners() {
800
+ const __process = __managedCleanupProcess
801
+ const __beforeExitListener = __managedBeforeExitListener
802
+ __managedCleanupProcess = undefined
803
+ __managedBeforeExitListener = undefined
804
+ __managedBeforeExitRegistrationRetryCount = 0
805
+ if (__process && __beforeExitListener) {
806
+ try {
807
+ __process.removeListener('beforeExit', __beforeExitListener)
808
+ } catch {}
809
+ }
810
+ }
811
+
812
+ function __scheduleManagedBeforeExitListenerRegistration() {
813
+ if (
814
+ !__managedCleanupProcess ||
815
+ __managedBeforeExitListener ||
816
+ __managedEmnapiContextDestroyers.size === 0 ||
817
+ __managedBeforeExitRegistrationRetryScheduled ||
818
+ __managedBeforeExitRegistrationRetryCount >= 3
819
+ ) {
820
+ return
821
+ }
822
+ __managedBeforeExitRegistrationRetryScheduled = true
823
+ __managedBeforeExitRegistrationRetryCount++
824
+ queueMicrotask(() => {
825
+ __managedBeforeExitRegistrationRetryScheduled = false
826
+ if (
827
+ !__managedCleanupProcess ||
828
+ __managedBeforeExitListener ||
829
+ __managedEmnapiContextDestroyers.size === 0
830
+ ) {
831
+ return
832
+ }
833
+ try {
834
+ __registerManagedBeforeExitListener()
835
+ } catch {}
836
+ })
837
+ }
838
+
839
+ function __registerManagedBeforeExitListener() {
840
+ if (!__managedCleanupProcess || __managedBeforeExitListener) {
841
+ return
842
+ }
843
+ try {
844
+ __managedCleanupProcess.once(
845
+ 'beforeExit',
846
+ __destroyManagedEmnapiContextsBeforeExit,
847
+ )
848
+ } catch (error) {
849
+ __scheduleManagedBeforeExitListenerRegistration()
850
+ throw error
851
+ }
852
+ __managedBeforeExitListener = __destroyManagedEmnapiContextsBeforeExit
853
+ __managedBeforeExitRegistrationRetryCount = 0
854
+ }
855
+
856
+ function __settleManagedEmnapiContextDestroy(__promise) {
857
+ if (__managedDestroyPromise === __promise) {
858
+ __managedDestroyPromise = undefined
859
+ __managedDestroyersInFlight = undefined
860
+ }
861
+ if (__managedEmnapiContextDestroyers.size === 0) {
862
+ __removeManagedEmnapiCleanupListeners()
863
+ return
864
+ }
865
+ try {
866
+ __registerManagedBeforeExitListener()
867
+ } catch {}
868
+ }
869
+
870
+ function __destroyManagedEmnapiContexts(__excludedDestroyers) {
871
+ if (__managedDestroyPromise) {
872
+ return __managedDestroyPromise
873
+ }
874
+ const __destroyers = Array.from(__managedEmnapiContextDestroyers).filter(
875
+ (__destroy) => !__excludedDestroyers?.has(__destroy),
876
+ )
877
+ if (__destroyers.length === 0) {
878
+ return Promise.resolve()
879
+ }
880
+ let __resolveDestroy
881
+ let __rejectDestroy
882
+ const __promise = new Promise((resolve, reject) => {
883
+ __resolveDestroy = resolve
884
+ __rejectDestroy = reject
885
+ })
886
+ __managedDestroyPromise = __promise
887
+ __managedDestroyersInFlight = new Set(__destroyers)
888
+ void Promise.all(
889
+ __destroyers.map((__destroy) => {
890
+ try {
891
+ return Promise.resolve(__destroy()).then(
892
+ () => ({ failed: false }),
893
+ (error) => ({ failed: true, error }),
894
+ )
895
+ } catch (error) {
896
+ return { failed: true, error }
897
+ }
898
+ }),
899
+ ).then((__results) => {
900
+ let __primaryError
901
+ let __failed = false
902
+ for (const __result of __results) {
903
+ if (!__result.failed) {
904
+ continue
905
+ }
906
+ if (!__failed) {
907
+ __failed = true
908
+ __primaryError = __result.error
909
+ } else {
910
+ __attachCleanupError(__primaryError, __result.error)
911
+ }
912
+ }
913
+ if (__failed) {
914
+ __rejectDestroy(__primaryError)
915
+ } else {
916
+ __resolveDestroy()
917
+ }
918
+ }, __rejectDestroy)
919
+ void __promise.then(
920
+ () => {
921
+ __settleManagedEmnapiContextDestroy(__promise)
922
+ },
923
+ () => {
924
+ __settleManagedEmnapiContextDestroy(__promise)
925
+ },
926
+ )
927
+ return __promise
928
+ }
929
+
930
+ async function __drainManagedEmnapiContexts(__excludedDestroyers) {
931
+ const __attemptedDestroyers = new Set(__excludedDestroyers)
932
+ let __primaryError
933
+ let __failed = false
934
+ while (true) {
935
+ let __promise = __managedDestroyPromise
936
+ let __destroyers = __managedDestroyersInFlight
937
+ if (!__promise) {
938
+ __promise = __destroyManagedEmnapiContexts(__attemptedDestroyers)
939
+ __destroyers = __managedDestroyersInFlight
940
+ if (!__destroyers) {
941
+ break
942
+ }
943
+ }
944
+ for (const __destroy of __destroyers) {
945
+ __attemptedDestroyers.add(__destroy)
946
+ }
947
+ try {
948
+ await __promise
949
+ } catch (error) {
950
+ if (!__failed) {
951
+ __failed = true
952
+ __primaryError = error
953
+ } else {
954
+ __attachCleanupError(__primaryError, error)
955
+ }
956
+ }
957
+ }
958
+ if (__failed) {
959
+ throw __primaryError
960
+ }
961
+ }
962
+
963
+ function __destroyManagedEmnapiContextsBeforeExit() {
964
+ // A once listener is consumed before Node invokes it, including when another
965
+ // cleanup batch is still pending.
966
+ __managedBeforeExitListener = undefined
967
+ if (__managedDestroyPromise) {
968
+ return
969
+ }
970
+ void __destroyManagedEmnapiContexts().catch((error) => {
971
+ queueMicrotask(() => {
972
+ throw error
973
+ })
974
+ })
975
+ }
976
+
977
+ function __registerManagedEmnapiContext(__process, __destroy) {
978
+ __managedEmnapiContextDestroyers.add(__destroy)
979
+ if (
980
+ !__managedCleanupProcess &&
981
+ __process &&
982
+ typeof __process.once === 'function' &&
983
+ typeof __process.removeListener === 'function'
984
+ ) {
985
+ __managedCleanupProcess = __process
986
+ }
987
+ let __registered = true
988
+ return () => {
989
+ if (!__registered) {
990
+ return
991
+ }
992
+ __registered = false
993
+ __managedEmnapiContextDestroyers.delete(__destroy)
994
+ if (__managedEmnapiContextDestroyers.size === 0) {
995
+ __removeManagedEmnapiCleanupListeners()
996
+ }
997
+ }
998
+ }
999
+
1000
+ async function __createManagedEmnapiContext(
1001
+ __prepareEnvCleanup,
1002
+ __isPreparingEnvCleanup,
1003
+ ) {
1004
+ const __process =
1005
+ typeof process === 'object' && process !== null ? process : undefined
1006
+ const __finishAutoDestroyCapture =
1007
+ __captureEmnapiAutoDestroyListener(__process)
1008
+ let __emnapiContext
1009
+ let __contextInitializationError
1010
+ let __contextInitializationFailed = false
1011
+ try {
1012
+ __emnapiContext = __wrapEmnapiContextDestroyForSettlement(
1013
+ __emnapiCreateContext({ autoDestroy: false }),
1014
+ __prepareEnvCleanup,
1015
+ __isPreparingEnvCleanup,
1016
+ )
1017
+ // emnapi 2.x still registers an unconditional process.once('beforeExit')
1018
+ // auto-destroy listener on Node hosts, and suppressDestroy() only
1019
+ // neutralizes its callback without removing it. This loader must stay
1020
+ // side-effect free per instance, so the listener is captured and removed;
1021
+ // suppressDestroy() remains the safety net when removal is unavailable.
1022
+ __emnapiContext.suppressDestroy()
1023
+ } catch (error) {
1024
+ __contextInitializationError = error
1025
+ __contextInitializationFailed = true
1026
+ } finally {
1027
+ // Remove only the exact emnapi callback captured above.
1028
+ __finishAutoDestroyCapture?.()
1029
+ }
1030
+ if (__emnapiContext === undefined) {
1031
+ throw __contextInitializationError
1032
+ }
1033
+ let __disposed = false
1034
+ let __destroying = false
1035
+ let __destroyPromise
1036
+ let __cleanupRegistered = false
1037
+ let __unregisterCleanup
1038
+ const __destroy = (__blocksModuleLifecycle = false) => {
1039
+ if (__disposed) {
1040
+ return
1041
+ }
1042
+ if (__destroying) {
1043
+ throw __createLifecycleReentryError('dispose')
1044
+ }
1045
+ if (__destroyPromise) {
1046
+ return __destroyPromise
1047
+ }
1048
+ __destroying = true
1049
+ let __result
1050
+ const __finishDestroyInvocation = () => {
1051
+ __destroying = false
1052
+ }
1053
+ const __finishModuleLifecycleDestroy = () => {
1054
+ if (__blocksModuleLifecycle) {
1055
+ __blocksModuleLifecycle = false
1056
+ __moduleLifecycleDestroyDepth--
1057
+ }
1058
+ }
1059
+ if (__blocksModuleLifecycle) {
1060
+ __moduleLifecycleDestroyDepth++
1061
+ }
1062
+ try {
1063
+ // Context.destroy() disables JS before cleanup hooks run, so settle
1064
+ // runtime-owned promises while this environment can still call JS.
1065
+ __prepareEnvCleanup?.()
1066
+ if (__isPreparingEnvCleanup?.()) {
1067
+ // Reached from inside the barrier, so `Context.destroy()` below would
1068
+ // hit the wrapper's in-flight no-op. Recording that as a completed
1069
+ // destroy is what makes the frame that *did* start the barrier skip the
1070
+ // real one afterwards, leaving the context retained with its cleanup
1071
+ // hooks unrun. Refuse instead: nothing is flagged, the context stays
1072
+ // registered for managed beforeExit cleanup, and a later destroy still
1073
+ // works. dispose() coalesces reentrancy before it can get here, so this
1074
+ // is the backstop for any other caller that manages to. A handshake
1075
+ // parked between the two halves of the barrier does not reach here —
1076
+ // `__prepareEnvCleanup` closes one rather than skipping it.
1077
+ throw __createLifecycleReentryError('dispose')
1078
+ }
1079
+ __result = __emnapiContext.destroy()
1080
+ } catch (error) {
1081
+ __finishDestroyInvocation()
1082
+ __finishModuleLifecycleDestroy()
1083
+ throw error
1084
+ }
1085
+ let __then
1086
+ try {
1087
+ if (
1088
+ __result !== null &&
1089
+ (typeof __result === 'object' || typeof __result === 'function')
1090
+ ) {
1091
+ __then = __result.then
1092
+ }
1093
+ } catch (error) {
1094
+ __finishDestroyInvocation()
1095
+ __finishModuleLifecycleDestroy()
1096
+ throw error
1097
+ }
1098
+ if (typeof __then === 'function') {
1099
+ let __resolveResult
1100
+ let __rejectResult
1101
+ const __resultPromise = new Promise((resolve, reject) => {
1102
+ __resolveResult = resolve
1103
+ __rejectResult = reject
1104
+ })
1105
+ const __promise = __resultPromise.then(
1106
+ (value) => {
1107
+ __finishDestroyInvocation()
1108
+ __finishModuleLifecycleDestroy()
1109
+ __disposed = true
1110
+ __destroyPromise = undefined
1111
+ __unregisterCleanup?.()
1112
+ return value
1113
+ },
1114
+ (error) => {
1115
+ __finishDestroyInvocation()
1116
+ __finishModuleLifecycleDestroy()
1117
+ __destroyPromise = undefined
1118
+ throw error
1119
+ },
1120
+ )
1121
+ __destroyPromise = __promise
1122
+ try {
1123
+ Reflect.apply(__then, __result, [__resolveResult, __rejectResult])
1124
+ } catch (error) {
1125
+ __rejectResult(error)
1126
+ }
1127
+ return __promise
1128
+ }
1129
+ __finishDestroyInvocation()
1130
+ __finishModuleLifecycleDestroy()
1131
+ __disposed = true
1132
+ __unregisterCleanup?.()
1133
+ }
1134
+ const __destroyForModuleLifecycle = () => __destroy(true)
1135
+ const __registerCleanup = (
1136
+ __beforeExitDestroy = __destroyForModuleLifecycle,
1137
+ ) => {
1138
+ if (__cleanupRegistered || __disposed) {
1139
+ return
1140
+ }
1141
+ __unregisterCleanup = __registerManagedEmnapiContext(
1142
+ __process,
1143
+ __beforeExitDestroy,
1144
+ )
1145
+ __cleanupRegistered = true
1146
+ __registerManagedBeforeExitListener()
1147
+ }
1148
+ if (__contextInitializationFailed) {
1149
+ let __registrationError
1150
+ let __registrationFailed = false
1151
+ try {
1152
+ __registerCleanup()
1153
+ } catch (error) {
1154
+ __attachCleanupError(__contextInitializationError, error)
1155
+ __registrationError = error
1156
+ __registrationFailed = true
1157
+ }
1158
+ try {
1159
+ await __destroyForModuleLifecycle()
1160
+ } catch (error) {
1161
+ __attachCleanupError(
1162
+ __registrationFailed
1163
+ ? __registrationError
1164
+ : __contextInitializationError,
1165
+ error,
1166
+ )
1167
+ try {
1168
+ __registerManagedBeforeExitListener()
1169
+ } catch {}
1170
+ }
1171
+ throw __contextInitializationError
1172
+ }
1173
+ return {
1174
+ context: __emnapiContext,
1175
+ destroy: __destroy,
1176
+ destroyForModuleLifecycle: __destroyForModuleLifecycle,
1177
+ registerCleanup: __registerCleanup,
1178
+ }
1179
+ }
1180
+
1181
+ async function __createInstance(
1182
+ __wasmInput,
1183
+ __options,
1184
+ __beforeExitDestroy,
1185
+ __onManagedDestroyer,
1186
+ ) {
1187
+ const __module = await __resolveModule(__wasmInput)
1188
+ const __emnapiModule = await __normalizeModuleForEmnapi(__module)
1189
+ const __wasi = new __WASI({
1190
+ version: 'preview1',
1191
+ })
1192
+ // The wasm module is linked with `--import-memory`, so a Memory must be
1193
+ // provided. It is resolved here in function scope (workerd bans global scope
1194
+ // allocation) and is never shared (no threads, no SharedArrayBuffer).
1195
+ // Resolve it before the emnapi context so a rejected option bag or a host
1196
+ // memory-limit failure cannot leak a context that never reaches
1197
+ // instantiation.
1198
+ const __wasmMemory = __resolveInstanceMemory(__options)
1199
+ let __lifecycleState = 'pending'
1200
+ let __destroyEmnapiContext
1201
+ let __destroyOwnedContext
1202
+ let __destroyManagedOwnedContext
1203
+ let __napiInstance
1204
+ let __wasmEnvCleanupRan = false
1205
+ let __wasmEnvCleanupPrepared = false
1206
+ let __wasmEnvCleanupPreparing = false
1207
+ // The closer for a barrier parked between `…_begin` and `…_finish`, set only
1208
+ // while that window is open. `__wasmEnvCleanupPreparing` cannot tell that
1209
+ // apart from a purely synchronous frame, which must not be re-entered and
1210
+ // which nothing outside it can finish; this window spans real event-loop
1211
+ // turns, so a caller that cannot yield — the managed beforeExit teardown —
1212
+ // can land inside it, and can close it. See `__prepareEnvCleanup`.
1213
+ let __finishParkedEnvCleanup
1214
+ // Raised while a caller that can still yield is driving the barrier, so the
1215
+ // queue it leaves behind is expected rather than lost.
1216
+ let __wasmEnvCleanupYielding = false
1217
+ let __wasmEnvSettlementLossReported = false
1218
+ let __wasmEnvCleanupDrained = false
1219
+ let __wasmEnvCleanupDrainPromise
1220
+ const __isPreparingEnvCleanup = () => __wasmEnvCleanupPreparing
1221
+ /**
1222
+ * Say so when the barrier leaves settlements queued and nothing is left that
1223
+ * could deliver them.
1224
+ *
1225
+ * Only `dispose()` and the initialization rollback yield the event-loop turns
1226
+ * @emnapi/core needs to dispatch its queue. Every other caller of the barrier
1227
+ * destroys in the same turn — a raw `Context.destroy()`, the managed
1228
+ * beforeExit teardown — and `Context.destroy()` runs the threadsafe
1229
+ * function's cleanup hook, which drains that queue with a null env and
1230
+ * discards it. The promises those settlements were for then hang forever,
1231
+ * silently.
1232
+ *
1233
+ * Loud, once, and never throwing: this runs from inside `Context.destroy()`,
1234
+ * where throwing would take the whole teardown down with it. Destroying
1235
+ * anyway is still the right trade — the queue is already unreachable by then.
1236
+ */
1237
+ const __reportUnreachedSettlements = () => {
1238
+ if (__wasmEnvCleanupYielding || __wasmEnvSettlementLossReported) {
1239
+ return
1240
+ }
1241
+ const __pending = __napiInstance?.exports.napi_wasm_env_cleanup_pending
1242
+ if (typeof __pending !== 'function') {
1243
+ return
1244
+ }
1245
+ let __queued
1246
+ try {
1247
+ __queued = __pending()
1248
+ } catch {
1249
+ return
1250
+ }
1251
+ if (!__queued) {
1252
+ return
1253
+ }
1254
+ __wasmEnvSettlementLossReported = true
1255
+ try {
1256
+ const __consoleHost = globalThis.console
1257
+ if (__consoleHost && typeof __consoleHost.error === 'function') {
1258
+ __consoleHost.error(
1259
+ 'napi-rs: the wasm environment is being destroyed with ' +
1260
+ __queued +
1261
+ ' queued promise settlement(s). Context.destroy() discards them, so those promises never settle. Dispose the instance instead: only dispose() yields the event-loop turns the settlements need.',
1262
+ )
1263
+ }
1264
+ } catch {}
1265
+ }
1266
+ const __prepareEnvCleanup = () => {
1267
+ if (__wasmEnvCleanupPrepared) {
1268
+ return
1269
+ }
1270
+ // A handshake parked between its two halves is one this frame can close,
1271
+ // and must: every caller of this is about to destroy the context, and the
1272
+ // turns the poll is waiting for will not come. Closing it runs `…_finish`,
1273
+ // which is the call that joins, so this degrades to exactly the single
1274
+ // call below. Leaving it open destroys the context with the barrier still
1275
+ // raised and the runtime never joined.
1276
+ const __finishParked = __finishParkedEnvCleanup
1277
+ if (__finishParked !== undefined) {
1278
+ __finishParked()
1279
+ __reportUnreachedSettlements()
1280
+ return
1281
+ }
1282
+ if (__wasmEnvCleanupPreparing) {
1283
+ return
1284
+ }
1285
+ const __prepareWasmEnvCleanup =
1286
+ __napiInstance?.exports.napi_prepare_wasm_env_cleanup
1287
+ if (typeof __prepareWasmEnvCleanup === 'function') {
1288
+ // The addon settles the promises it cancels synchronously, under a
1289
+ // non-reentrant lifecycle mutex: anything a promise hook calls from in
1290
+ // here must not reach this export again.
1291
+ __wasmEnvCleanupPreparing = true
1292
+ try {
1293
+ __prepareWasmEnvCleanup()
1294
+ } finally {
1295
+ __wasmEnvCleanupPreparing = false
1296
+ }
1297
+ __wasmEnvCleanupRan = true
1298
+ __reportUnreachedSettlements()
1299
+ }
1300
+ __wasmEnvCleanupPrepared = true
1301
+ }
1302
+ /**
1303
+ * The barrier for the callers that can yield: `__prepareEnvCleanup` with real
1304
+ * event-loop turns in the middle.
1305
+ *
1306
+ * `napi_prepare_wasm_env_cleanup` waits — it returns only once the addon's
1307
+ * async runtime has quiesced, and the work it waits for can itself be waiting
1308
+ * for a JavaScript turn from this thread. The addon's two-phase form splits
1309
+ * that: `…_begin` stops the runtime without joining and reports whether
1310
+ * anything is still live, `napi_wasm_runtime_work_pending` answers that again
1311
+ * without blocking, and `…_finish` joins.
1312
+ *
1313
+ * The poll is unbounded — see `__pollWasmRuntimeWork` — but a caller that
1314
+ * cannot yield closes the handshake itself rather than waiting for it, so
1315
+ * `…_finish` still runs on every teardown path. Feature-detected like every
1316
+ * other export here, and returns nothing whenever the handshake finished
1317
+ * without yielding.
1318
+ */
1319
+ const __prepareEnvCleanupWithTurns = () => {
1320
+ if (__wasmEnvCleanupPrepared || __wasmEnvCleanupPreparing) {
1321
+ return
1322
+ }
1323
+ const __exports = __napiInstance?.exports
1324
+ const __begin = __exports?.napi_prepare_wasm_env_cleanup_begin
1325
+ const __finish = __exports?.napi_prepare_wasm_env_cleanup_finish
1326
+ if (typeof __begin !== 'function' || typeof __finish !== 'function') {
1327
+ // No split to use. The settlement drain still follows this, so the queue
1328
+ // the single call leaves behind is expected rather than lost.
1329
+ __wasmEnvCleanupYielding = true
1330
+ try {
1331
+ __prepareEnvCleanup()
1332
+ } finally {
1333
+ __wasmEnvCleanupYielding = false
1334
+ }
1335
+ return
1336
+ }
1337
+ const __workPending = __exports?.napi_wasm_runtime_work_pending
1338
+ // The in-flight flag stays raised across the turns below, so a `destroy()`
1339
+ // from one of the JavaScript handlers they run is the same no-op it is
1340
+ // inside the single call: the barrier is up and the runtime is
1341
+ // mid-teardown, and destroying between the halves would strand exactly what
1342
+ // this delivers.
1343
+ __wasmEnvCleanupPreparing = true
1344
+ let __live
1345
+ try {
1346
+ __live = __begin()
1347
+ } catch (__error) {
1348
+ __wasmEnvCleanupPreparing = false
1349
+ throw __error
1350
+ }
1351
+ __wasmEnvCleanupRan = true
1352
+ const __finishEnvCleanup = () => {
1353
+ if (__wasmEnvCleanupPrepared) {
1354
+ // Already closed by a caller that could not yield. `…_finish` is
1355
+ // idempotent, but the flags it lowers are not.
1356
+ return
1357
+ }
1358
+ __finishParkedEnvCleanup = undefined
1359
+ try {
1360
+ __finish()
1361
+ } finally {
1362
+ __wasmEnvCleanupPreparing = false
1363
+ }
1364
+ __wasmEnvCleanupPrepared = true
1365
+ }
1366
+ if (!__live || typeof __workPending !== 'function') {
1367
+ __finishEnvCleanup()
1368
+ return
1369
+ }
1370
+ // Publish the closer before yielding: from here until `__finishEnvCleanup`
1371
+ // runs, a caller that cannot yield is entitled to end this handshake.
1372
+ __finishParkedEnvCleanup = __finishEnvCleanup
1373
+ return __pollWasmRuntimeWork(__workPending).then(
1374
+ __finishEnvCleanup,
1375
+ __finishEnvCleanup,
1376
+ )
1377
+ }
1378
+ // The barrier + settlement drain, hoisted out of the context destroyer so the
1379
+ // drain can yield without widening the destroyer's reentry window. Both
1380
+ // yielding paths run it — dispose() and the initialization-failure rollback.
1381
+ // The destroyer still runs the barrier itself (idempotently) for the one path
1382
+ // that cannot yield: managed beforeExit cleanup of an instance whose rollback
1383
+ // is being retried.
1384
+ //
1385
+ // "Drained" is recorded only once a wait has actually finished. Scheduling a
1386
+ // macrotask can fail — a host-provided or patched `setImmediate` that throws
1387
+ // is enough — and dispose() stays retryable after it rejects, so marking the
1388
+ // drain complete up front would make the retry skip it and destroy the context
1389
+ // with the barrier's settlements still queued.
1390
+ const __drainAfterEnvCleanup = () => {
1391
+ if (!__wasmEnvCleanupRan) {
1392
+ return
1393
+ }
1394
+ const __drained = __drainWasmEnvCleanup(__napiInstance)
1395
+ if (!__drained || typeof __drained.then !== 'function') {
1396
+ __wasmEnvCleanupDrained = true
1397
+ return
1398
+ }
1399
+ return __drained.then((__value) => {
1400
+ __wasmEnvCleanupDrained = true
1401
+ return __value
1402
+ })
1403
+ }
1404
+ const __prepareForDisposal = () => {
1405
+ if (__wasmEnvCleanupDrained) {
1406
+ return
1407
+ }
1408
+ if (__wasmEnvCleanupDrainPromise) {
1409
+ return __wasmEnvCleanupDrainPromise
1410
+ }
1411
+ // The barrier itself can yield now, so the memo below has to cover it too:
1412
+ // a reentrant caller must join this handshake rather than start a second
1413
+ // one while the first is parked between the two halves.
1414
+ const __prepared = __prepareEnvCleanupWithTurns()
1415
+ const __settled =
1416
+ __prepared && typeof __prepared.then === 'function'
1417
+ ? __prepared.then(__drainAfterEnvCleanup)
1418
+ : __drainAfterEnvCleanup()
1419
+ if (!__settled || typeof __settled.then !== 'function') {
1420
+ return
1421
+ }
1422
+ const __tracked = __settled.then(
1423
+ (__value) => {
1424
+ __wasmEnvCleanupDrainPromise = undefined
1425
+ return __value
1426
+ },
1427
+ (__error) => {
1428
+ __wasmEnvCleanupDrainPromise = undefined
1429
+ throw __error
1430
+ },
1431
+ )
1432
+ __wasmEnvCleanupDrainPromise = __tracked
1433
+ return __tracked
1434
+ }
1435
+ let __disposed = false
1436
+ const __runInstanceDisposal = async () => {
1437
+ if (__lifecycleState !== 'failed') {
1438
+ __lifecycleState = 'disposal'
1439
+ }
1440
+ // Outstanding async work first, while the environment is still completely
1441
+ // live: its completion callbacks run addon code, and the barrier and
1442
+ // `Context.destroy()` below each take that away. Undefined unless work is
1443
+ // outstanding.
1444
+ const __asyncWorkDrained = __drainInstanceAsyncWork(__napiInstance)
1445
+ if (__asyncWorkDrained) {
1446
+ await __asyncWorkDrained
1447
+ }
1448
+ // Then settle what the barrier cancelled, before the environment stops
1449
+ // accepting JavaScript calls. Undefined unless something is queued, so
1450
+ // an idle disposal is not delayed by a single turn.
1451
+ const __drained = __prepareForDisposal()
1452
+ if (__drained) {
1453
+ await __drained
1454
+ }
1455
+ const __result = await (__beforeExitDestroy
1456
+ ? __destroyManagedOwnedContext()
1457
+ : __destroyOwnedContext())
1458
+ // Only a completed destroy retires the instance; a throw above leaves
1459
+ // the counter untouched so a retried dispose() cannot double-decrement.
1460
+ if (!__disposed) {
1461
+ __disposed = true
1462
+ __liveInstances -= 1
1463
+ }
1464
+ return __result
1465
+ }
1466
+ let __instanceDisposePromise
1467
+ /**
1468
+ * The disposal frame runs the barrier, and the barrier settles the promises it
1469
+ * cancels synchronously — so a promise hook firing inside it can call this
1470
+ * same instance's dispose() again while the first call is still in its drain.
1471
+ * That nested call finds the barrier flagged in flight, prepares nothing,
1472
+ * drains nothing, and falls straight through to the context destroyer, whose
1473
+ * `Context.destroy()` hits the wrapper's in-flight no-op. It would record a
1474
+ * destruction that never happened, and the outer frame would then skip the
1475
+ * real one: both disposals resolve, no cleanup hook runs, the context stays
1476
+ * retained.
1477
+ *
1478
+ * Memoize before any of that starts, exactly like the eager loaders'
1479
+ * `__disposeWasiBinding`, so there is only ever one disposal frame per
1480
+ * instance and a reentrant caller awaits it instead of racing it. Cleared on
1481
+ * rejection: a drain that failed has to stay retryable.
1482
+ */
1483
+ const __disposeInstance = () => {
1484
+ if (__instanceDisposePromise) {
1485
+ return __instanceDisposePromise
1486
+ }
1487
+ let __resolveDispose
1488
+ let __rejectDispose
1489
+ const __disposePromise = new Promise((__resolve, __reject) => {
1490
+ __resolveDispose = __resolve
1491
+ __rejectDispose = __reject
1492
+ })
1493
+ __instanceDisposePromise = __disposePromise
1494
+ __runInstanceDisposal().then(__resolveDispose, (__error) => {
1495
+ __instanceDisposePromise = undefined
1496
+ __rejectDispose(__error)
1497
+ })
1498
+ return __disposePromise
1499
+ }
1500
+ const __destroyBeforeExit = __beforeExitDestroy
1501
+ ? async () => {
1502
+ if (__lifecycleState === 'failed') {
1503
+ await __destroyManagedOwnedContext()
1504
+ return
1505
+ }
1506
+ __lifecycleState = 'disposal'
1507
+ try {
1508
+ await __beforeExitDestroy()
1509
+ } catch (error) {
1510
+ if (__lifecycleState !== 'failed') {
1511
+ throw error
1512
+ }
1513
+ // The singleton's initialization rejection is already observable
1514
+ // through instantiate() and dispose(). Managed beforeExit cleanup
1515
+ // owns only context destruction, including retrying a failed rollback.
1516
+ await __destroyManagedOwnedContext()
1517
+ }
1518
+ }
1519
+ : undefined
1520
+ const {
1521
+ context: __emnapiContext,
1522
+ destroy,
1523
+ destroyForModuleLifecycle,
1524
+ registerCleanup: __registerCleanup,
1525
+ } = await __createManagedEmnapiContext(
1526
+ __prepareEnvCleanup,
1527
+ __isPreparingEnvCleanup,
1528
+ )
1529
+ __destroyEmnapiContext = destroy
1530
+ __destroyOwnedContext = () => __destroyEmnapiContext()
1531
+ __destroyManagedOwnedContext = destroyForModuleLifecycle
1532
+ try {
1533
+ if (__destroyBeforeExit) {
1534
+ __onManagedDestroyer(__destroyBeforeExit)
1535
+ await __registerCleanup(__destroyBeforeExit)
1536
+ }
1537
+ let __napiModule
1538
+ ;({
1539
+ instance: __napiInstance,
1540
+ napiModule: __napiModule,
1541
+ } = await __emnapiInstantiateNapiModule(__emnapiModule, {
1542
+ context: __emnapiContext,
1543
+ asyncWorkPoolSize: 0,
1544
+ plugins: [__emnapiAsyncWorkPlugin, __emnapiTSFNPlugin],
1545
+ wasi: __wasi,
1546
+ overwriteImports(importObject) {
1547
+ importObject.env = {
1548
+ ...importObject.env,
1549
+ ...importObject.napi,
1550
+ ...importObject.emnapi,
1551
+ memory: __wasmMemory,
1552
+ }
1553
+ return importObject
1554
+ },
1555
+ beforeInit({ instance }) {
1556
+ __napiInstance = instance
1557
+ for (const name of Object.keys(instance.exports)) {
1558
+ if (name.startsWith('__napi_register__')) {
1559
+ instance.exports[name]()
1560
+ }
1561
+ }
1562
+ },
1563
+ }))
1564
+ // `instantiate()` and `createInstance().exports` hand out this object; a
1565
+ // named module export does not travel with it. After the instance host
1566
+ // install, which hands the same object to addon-provided registration
1567
+ // functions that may put anything on it, and inside this `try`, so a
1568
+ // claimed name flips `__lifecycleState` to 'failed' and tears the instance
1569
+ // down rather than escaping a half-built one.
1570
+ __napiStampBindingTarget(__napiModule.exports, __napiBindingTarget)
1571
+ if (__lifecycleState === 'pending') {
1572
+ __lifecycleState = 'succeeded'
1573
+ }
1574
+ __createdInstances += 1
1575
+ __liveInstances += 1
1576
+ return {
1577
+ exports: __napiModule.exports,
1578
+ get memory() {
1579
+ return __wasmMemory
1580
+ },
1581
+ get memoryBytes() {
1582
+ // The Memory outlives the environment, so this stays readable after a
1583
+ // FAILED dispose() (which leaves the instance undisposed and
1584
+ // retryable). It reports 0 only once disposal has actually completed.
1585
+ return __disposed ? 0 : __wasmMemory.buffer.byteLength
1586
+ },
1587
+ get disposed() {
1588
+ return __disposed
1589
+ },
1590
+ dispose: __disposeInstance,
1591
+ }
1592
+ } catch (error) {
1593
+ __lifecycleState = 'failed'
1594
+ // Instantiation can fail *after* registration has run, and registration runs
1595
+ // with a live environment: a module-init hook can start async work and then
1596
+ // return an error, and the promise it created may already have escaped into
1597
+ // JavaScript. Settle what the barrier cancels before the context is
1598
+ // destroyed, exactly like dispose() does; destroying without yielding
1599
+ // discards the queue with a null env. Undefined unless something is queued,
1600
+ // so a failure before beforeInit costs no extra turn.
1601
+ let __settlementsUnreached = false
1602
+ try {
1603
+ // Registration can start async work too, and this path destroys the same
1604
+ // environment its completions need. A drain that cannot finish leaves the
1605
+ // work outstanding, so it counts as settlements unreached and stops the
1606
+ // rollback short of destroying, exactly like a failed barrier drain.
1607
+ const __asyncWorkDrained = __drainInstanceAsyncWork(__napiInstance)
1608
+ if (__asyncWorkDrained) {
1609
+ await __asyncWorkDrained
1610
+ }
1611
+ const __drained = __prepareForDisposal()
1612
+ if (__drained) {
1613
+ await __drained
1614
+ }
1615
+ } catch (drainError) {
1616
+ __attachCleanupError(error, drainError)
1617
+ __settlementsUnreached = true
1618
+ }
1619
+ let __registrationError
1620
+ let __registrationFailed = false
1621
+ if (!__beforeExitDestroy) {
1622
+ try {
1623
+ // Independent instances are caller-owned while pending and after
1624
+ // success. Register only failed rollback so cleanup remains retryable.
1625
+ await __registerCleanup()
1626
+ } catch (registrationError) {
1627
+ __attachCleanupError(error, registrationError)
1628
+ __registrationError = registrationError
1629
+ __registrationFailed = true
1630
+ }
1631
+ }
1632
+ if (__settlementsUnreached) {
1633
+ // The barrier or the drain did not finish, so the settlements it queued
1634
+ // are still in the threadsafe-function queue. Destroying now runs the
1635
+ // cleanup hook that drains that queue with a null env and discards it,
1636
+ // stranding a promise that already escaped into JavaScript — with nothing
1637
+ // left that could ever settle it. dispose() refuses to destroy for exactly
1638
+ // this reason (a rejected drain there never reaches the destroy), so this
1639
+ // path refuses too.
1640
+ //
1641
+ // Nothing leaks. The registration just above — and, for the singleton, the
1642
+ // one made before instantiation — leaves this context in
1643
+ // `__managedEmnapiContextDestroyers`, so beforeExit destroys it and
1644
+ // dispose() can retry it. Only the destruction is deferred, and the turns
1645
+ // that pass in the meantime are exactly what the queue needed.
1646
+ try {
1647
+ __registerManagedBeforeExitListener()
1648
+ } catch {}
1649
+ throw error
1650
+ }
1651
+ try {
1652
+ await __destroyManagedOwnedContext()
1653
+ } catch (disposeError) {
1654
+ // Initialization is the primary failure. Preserve it even if cleanup
1655
+ // also fails, while retaining the cleanup error when the value is
1656
+ // extensible and has no existing cause.
1657
+ __attachCleanupError(
1658
+ __registrationFailed ? __registrationError : error,
1659
+ disposeError,
1660
+ )
1661
+ try {
1662
+ __registerManagedBeforeExitListener()
1663
+ } catch {}
1664
+ }
1665
+ throw error
1666
+ }
1667
+ }
1668
+
1669
+ /**
1670
+ * Create an independent instance. Call and await dispose() when the instance
1671
+ * is no longer needed so emnapi cleanup hooks run deterministically.
1672
+ *
1673
+ * The optional second argument selects this instance's linear memory: either
1674
+ * `memory` (an unshared, single-use WebAssembly.Memory you allocated) or
1675
+ * `initialMemoryPages` / `maximumMemoryPages`, never both. Omitted, the
1676
+ * loader allocates WASM_MEMORY.initialPages..WASM_MEMORY.maximumPages.
1677
+ *
1678
+ * A provided Memory must come from this loader's own realm: the WASI and
1679
+ * emnapi layers underneath identify one with a realm-local `instanceof`, so a
1680
+ * Memory built in a `node:vm` context or another frame is rejected. Every
1681
+ * Memory an instance runs on is single-use, the loader-allocated one included:
1682
+ * `instance.memory` cannot be recycled into a second `createInstance()`.
1683
+ */
1684
+ export async function createInstance(__wasmInput, __options) {
1685
+ return __createInstance(__wasmInput, __options)
1686
+ }
1687
+
1688
+ let __defaultModulePromise
1689
+ let __defaultInstancePromise
1690
+ let __defaultDisposePromise
1691
+ let __defaultDisposalStarted = false
1692
+ const __defaultManagedDestroyers = new WeakMap()
1693
+ let __moduleDisposePromise
1694
+
1695
+ /**
1696
+ * Instantiate a module-local singleton. Concurrent and repeated calls
1697
+ * with the same module share one instance and one Memory allocation.
1698
+ */
1699
+ export function instantiate(__wasmInput) {
1700
+ const __modulePromise = __resolveModule(__wasmInput)
1701
+ if (__moduleLifecycleDestroyDepth !== 0) {
1702
+ void __modulePromise.catch(() => {})
1703
+ return Promise.reject(__createLifecycleReentryError('instantiate'))
1704
+ }
1705
+ if (__moduleDisposePromise) {
1706
+ void __modulePromise.catch(() => {})
1707
+ return __moduleDisposePromise.then(() => instantiate(__modulePromise))
1708
+ }
1709
+ if (__defaultDisposalStarted) {
1710
+ // Observe rejected input immediately, but preserve lifecycle ordering and
1711
+ // error precedence by instantiating only after disposal succeeds. A failed
1712
+ // disposal retains the old instance only so its cleanup can be retried.
1713
+ void __modulePromise.catch(() => {})
1714
+ const __disposePromise = __defaultDisposePromise ?? dispose()
1715
+ return __disposePromise.then(() => instantiate(__modulePromise))
1716
+ }
1717
+ if (!__defaultInstancePromise) {
1718
+ __defaultModulePromise = __modulePromise
1719
+ const __instancePromise = __modulePromise.then((__module) =>
1720
+ __createInstance(
1721
+ __module,
1722
+ undefined,
1723
+ __disposeDefaultInstance,
1724
+ (__managedDestroyer) => {
1725
+ __defaultManagedDestroyers.set(
1726
+ __instancePromise,
1727
+ __managedDestroyer,
1728
+ )
1729
+ },
1730
+ ),
1731
+ )
1732
+ __defaultInstancePromise = __instancePromise
1733
+ void __instancePromise.catch(() => {
1734
+ if (__defaultInstancePromise === __instancePromise) {
1735
+ __defaultInstancePromise = undefined
1736
+ __defaultModulePromise = undefined
1737
+ }
1738
+ })
1739
+ return __instancePromise.then((__instance) => __instance.exports)
1740
+ }
1741
+ const __defaultModulePromiseForCall = __defaultModulePromise
1742
+ const __defaultInstancePromiseForCall = __defaultInstancePromise
1743
+ return Promise.all([__defaultModulePromiseForCall, __modulePromise]).then(
1744
+ async ([__defaultModule, __module]) => {
1745
+ if (__defaultModule !== __module) {
1746
+ throw new Error(
1747
+ 'instantiate() already owns a different WebAssembly.Module; call dispose() first or use createInstance() for independent instances.',
1748
+ )
1749
+ }
1750
+ return (await __defaultInstancePromiseForCall).exports
1751
+ },
1752
+ )
1753
+ }
1754
+
1755
+ async function __disposeDefaultInstance(__onDestroy) {
1756
+ if (__defaultDisposePromise) {
1757
+ return __defaultDisposePromise
1758
+ }
1759
+ const __instancePromise = __defaultInstancePromise
1760
+ if (!__instancePromise) {
1761
+ __defaultDisposalStarted = false
1762
+ return
1763
+ }
1764
+ __defaultDisposalStarted = true
1765
+ const __disposePromise = (async () => {
1766
+ let __instance
1767
+ try {
1768
+ __instance = await __instancePromise
1769
+ } catch (error) {
1770
+ const __managedDestroyer =
1771
+ __defaultManagedDestroyers.get(__instancePromise)
1772
+ if (__managedDestroyer) {
1773
+ __onDestroy?.(__managedDestroyer)
1774
+ }
1775
+ __defaultManagedDestroyers.delete(__instancePromise)
1776
+ throw error
1777
+ }
1778
+ const __managedDestroyer =
1779
+ __defaultManagedDestroyers.get(__instancePromise)
1780
+ if (__managedDestroyer) {
1781
+ __onDestroy?.(__managedDestroyer)
1782
+ }
1783
+ await __instance.dispose()
1784
+ if (__defaultInstancePromise === __instancePromise) {
1785
+ __defaultInstancePromise = undefined
1786
+ __defaultModulePromise = undefined
1787
+ __defaultDisposalStarted = false
1788
+ }
1789
+ __defaultManagedDestroyers.delete(__instancePromise)
1790
+ })()
1791
+ __defaultDisposePromise = __disposePromise
1792
+ try {
1793
+ await __disposePromise
1794
+ } finally {
1795
+ if (__defaultDisposePromise === __disposePromise) {
1796
+ __defaultDisposePromise = undefined
1797
+ }
1798
+ }
1799
+ }
1800
+
1801
+ async function __dispose() {
1802
+ let __defaultDisposeError
1803
+ let __defaultDisposeFailed = false
1804
+ let __attemptedDefaultDestroyer
1805
+ try {
1806
+ await __disposeDefaultInstance((__destroyer) => {
1807
+ __attemptedDefaultDestroyer = __destroyer
1808
+ })
1809
+ } catch (error) {
1810
+ __defaultDisposeError = error
1811
+ __defaultDisposeFailed = true
1812
+ }
1813
+ const __excludedDestroyers = new Set()
1814
+ if (__defaultDisposeFailed && __attemptedDefaultDestroyer) {
1815
+ __excludedDestroyers.add(__attemptedDefaultDestroyer)
1816
+ }
1817
+ try {
1818
+ await __drainManagedEmnapiContexts(__excludedDestroyers)
1819
+ } catch (error) {
1820
+ if (!__defaultDisposeFailed) {
1821
+ throw error
1822
+ }
1823
+ if (error !== __defaultDisposeError) {
1824
+ __attachCleanupError(__defaultDisposeError, error)
1825
+ }
1826
+ }
1827
+ if (__defaultDisposeFailed) {
1828
+ throw __defaultDisposeError
1829
+ }
1830
+ }
1831
+
1832
+ /**
1833
+ * Dispose the singleton created by instantiate(). A later call may create a
1834
+ * fresh instance, including from a different module. This also retries cleanup
1835
+ * retained after a failed initialization rollback.
1836
+ */
1837
+ export function dispose() {
1838
+ if (__moduleLifecycleDestroyDepth !== 0) {
1839
+ return Promise.reject(__createLifecycleReentryError('dispose'))
1840
+ }
1841
+ if (__moduleDisposePromise) {
1842
+ return __moduleDisposePromise
1843
+ }
1844
+ let __resolveDispose
1845
+ let __rejectDispose
1846
+ const __promise = new Promise((resolve, reject) => {
1847
+ __resolveDispose = resolve
1848
+ __rejectDispose = reject
1849
+ })
1850
+ __moduleDisposePromise = __promise
1851
+ void __dispose().then(__resolveDispose, __rejectDispose)
1852
+ void __promise.then(
1853
+ () => {
1854
+ if (__moduleDisposePromise === __promise) {
1855
+ __moduleDisposePromise = undefined
1856
+ }
1857
+ },
1858
+ () => {
1859
+ if (__moduleDisposePromise === __promise) {
1860
+ __moduleDisposePromise = undefined
1861
+ }
1862
+ },
1863
+ )
1864
+ return __promise
1865
+ }