@petrea/wasm 0.0.0 → 0.5.1

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,1488 @@
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 './binding.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 './binding.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
+ // How often to re-read `napi_wasm_async_work_pending` while waiting. The wait
492
+ // ends when the addon reports zero, so this only decides how promptly disposal
493
+ // notices — not how long it waits.
494
+ const __WASI_ASYNC_WORK_POLL_INTERVAL_MS = 1
495
+
496
+ /**
497
+ * Settles this instance's outstanding `napi_async_work` before the teardown
498
+ * that would strand it.
499
+ *
500
+ * The same hole the eager loaders had, and the same fix: the environment
501
+ * cleanup barrier covers promise settlements queued on the threadsafe-function
502
+ * queue and says nothing about `napi_async_work`, so destroying the context
503
+ * with a work still outstanding leaves its completion callback with nowhere to
504
+ * run and its promise unsettled forever.
505
+ *
506
+ * This flavor is threadless, so `compute` runs on the JavaScript thread inside
507
+ * the macrotask that dequeued it: while this is running, any outstanding work
508
+ * is queued rather than executing, and `napi_wasm_cancel_pending_async_work`
509
+ * can take all of it. The poll is still what decides when the drain is done —
510
+ * cancellation delivers those completions from a later macrotask, not
511
+ * synchronously.
512
+ *
513
+ * Per instance, from that instance's own exports: two instances of this loader
514
+ * have separate registries and must not wait on each other.
515
+ *
516
+ * Both exports are optional, so an addon built against a napi crate that
517
+ * predates them keeps the previous behavior.
518
+ *
519
+ * Returns nothing when there is nothing outstanding, which keeps disposal
520
+ * synchronous in the common case. No deadline: giving up would destroy the
521
+ * environment with a completion callback still owed.
522
+ */
523
+ function __drainInstanceAsyncWork(__instance) {
524
+ const __exports = __instance?.exports
525
+ const __pending = __exports?.napi_wasm_async_work_pending
526
+ const __cancelPending = __exports?.napi_wasm_cancel_pending_async_work
527
+ if (typeof __pending !== 'function' || typeof __cancelPending !== 'function') {
528
+ return
529
+ }
530
+
531
+ const __readPending = () => {
532
+ try {
533
+ return __pending()
534
+ } catch (__error) {
535
+ // A trap is the only way this call fails: it reads a counter and cannot
536
+ // allocate or call back into JavaScript. A trapped instance can no longer
537
+ // run anything, so its outstanding work is unreachable by definition and
538
+ // best-effort is the honest answer. Anything else means the export is not
539
+ // what this loader thinks it is — a defect worth surfacing.
540
+ if (__error instanceof globalThis.WebAssembly.RuntimeError) {
541
+ return 0
542
+ }
543
+ throw __error
544
+ }
545
+ }
546
+
547
+ if (!__readPending()) {
548
+ return
549
+ }
550
+ try {
551
+ __cancelPending()
552
+ } catch {
553
+ // Cancellation only bounds the wait. Failing it means waiting for the queue
554
+ // to run instead, which the poll below already does.
555
+ }
556
+ if (!__readPending()) {
557
+ return
558
+ }
559
+
560
+ return (async () => {
561
+ while (__readPending()) {
562
+ await new Promise((__resolve) => {
563
+ __scheduleTimer(__resolve, __WASI_ASYNC_WORK_POLL_INTERVAL_MS)
564
+ })
565
+ }
566
+ })()
567
+ }
568
+
569
+ function __createLifecycleReentryError(__operation) {
570
+ const __error = new Error(
571
+ __operation +
572
+ '() cannot run while an emnapi Context.destroy() call is still active; await the original cleanup promise instead.',
573
+ )
574
+ __error.code = 'ERR_NAPI_WASI_LIFECYCLE_REENTRY'
575
+ return __error
576
+ }
577
+
578
+ const __managedEmnapiContextDestroyers = new Set()
579
+ let __managedCleanupProcess
580
+ let __managedBeforeExitListener
581
+ let __managedDestroyPromise
582
+ let __managedDestroyersInFlight
583
+ let __managedBeforeExitRegistrationRetryCount = 0
584
+ let __managedBeforeExitRegistrationRetryScheduled = false
585
+ let __moduleLifecycleDestroyDepth = 0
586
+
587
+ function __removeManagedEmnapiCleanupListeners() {
588
+ const __process = __managedCleanupProcess
589
+ const __beforeExitListener = __managedBeforeExitListener
590
+ __managedCleanupProcess = undefined
591
+ __managedBeforeExitListener = undefined
592
+ __managedBeforeExitRegistrationRetryCount = 0
593
+ if (__process && __beforeExitListener) {
594
+ try {
595
+ __process.removeListener('beforeExit', __beforeExitListener)
596
+ } catch {}
597
+ }
598
+ }
599
+
600
+ function __scheduleManagedBeforeExitListenerRegistration() {
601
+ if (
602
+ !__managedCleanupProcess ||
603
+ __managedBeforeExitListener ||
604
+ __managedEmnapiContextDestroyers.size === 0 ||
605
+ __managedBeforeExitRegistrationRetryScheduled ||
606
+ __managedBeforeExitRegistrationRetryCount >= 3
607
+ ) {
608
+ return
609
+ }
610
+ __managedBeforeExitRegistrationRetryScheduled = true
611
+ __managedBeforeExitRegistrationRetryCount++
612
+ queueMicrotask(() => {
613
+ __managedBeforeExitRegistrationRetryScheduled = false
614
+ if (
615
+ !__managedCleanupProcess ||
616
+ __managedBeforeExitListener ||
617
+ __managedEmnapiContextDestroyers.size === 0
618
+ ) {
619
+ return
620
+ }
621
+ try {
622
+ __registerManagedBeforeExitListener()
623
+ } catch {}
624
+ })
625
+ }
626
+
627
+ function __registerManagedBeforeExitListener() {
628
+ if (!__managedCleanupProcess || __managedBeforeExitListener) {
629
+ return
630
+ }
631
+ try {
632
+ __managedCleanupProcess.once(
633
+ 'beforeExit',
634
+ __destroyManagedEmnapiContextsBeforeExit,
635
+ )
636
+ } catch (error) {
637
+ __scheduleManagedBeforeExitListenerRegistration()
638
+ throw error
639
+ }
640
+ __managedBeforeExitListener = __destroyManagedEmnapiContextsBeforeExit
641
+ __managedBeforeExitRegistrationRetryCount = 0
642
+ }
643
+
644
+ function __settleManagedEmnapiContextDestroy(__promise) {
645
+ if (__managedDestroyPromise === __promise) {
646
+ __managedDestroyPromise = undefined
647
+ __managedDestroyersInFlight = undefined
648
+ }
649
+ if (__managedEmnapiContextDestroyers.size === 0) {
650
+ __removeManagedEmnapiCleanupListeners()
651
+ return
652
+ }
653
+ try {
654
+ __registerManagedBeforeExitListener()
655
+ } catch {}
656
+ }
657
+
658
+ function __destroyManagedEmnapiContexts(__excludedDestroyers) {
659
+ if (__managedDestroyPromise) {
660
+ return __managedDestroyPromise
661
+ }
662
+ const __destroyers = Array.from(__managedEmnapiContextDestroyers).filter(
663
+ (__destroy) => !__excludedDestroyers?.has(__destroy),
664
+ )
665
+ if (__destroyers.length === 0) {
666
+ return Promise.resolve()
667
+ }
668
+ let __resolveDestroy
669
+ let __rejectDestroy
670
+ const __promise = new Promise((resolve, reject) => {
671
+ __resolveDestroy = resolve
672
+ __rejectDestroy = reject
673
+ })
674
+ __managedDestroyPromise = __promise
675
+ __managedDestroyersInFlight = new Set(__destroyers)
676
+ void Promise.all(
677
+ __destroyers.map((__destroy) => {
678
+ try {
679
+ return Promise.resolve(__destroy()).then(
680
+ () => ({ failed: false }),
681
+ (error) => ({ failed: true, error }),
682
+ )
683
+ } catch (error) {
684
+ return { failed: true, error }
685
+ }
686
+ }),
687
+ ).then((__results) => {
688
+ let __primaryError
689
+ let __failed = false
690
+ for (const __result of __results) {
691
+ if (!__result.failed) {
692
+ continue
693
+ }
694
+ if (!__failed) {
695
+ __failed = true
696
+ __primaryError = __result.error
697
+ } else {
698
+ __attachCleanupError(__primaryError, __result.error)
699
+ }
700
+ }
701
+ if (__failed) {
702
+ __rejectDestroy(__primaryError)
703
+ } else {
704
+ __resolveDestroy()
705
+ }
706
+ }, __rejectDestroy)
707
+ void __promise.then(
708
+ () => {
709
+ __settleManagedEmnapiContextDestroy(__promise)
710
+ },
711
+ () => {
712
+ __settleManagedEmnapiContextDestroy(__promise)
713
+ },
714
+ )
715
+ return __promise
716
+ }
717
+
718
+ async function __drainManagedEmnapiContexts(__excludedDestroyers) {
719
+ const __attemptedDestroyers = new Set(__excludedDestroyers)
720
+ let __primaryError
721
+ let __failed = false
722
+ while (true) {
723
+ let __promise = __managedDestroyPromise
724
+ let __destroyers = __managedDestroyersInFlight
725
+ if (!__promise) {
726
+ __promise = __destroyManagedEmnapiContexts(__attemptedDestroyers)
727
+ __destroyers = __managedDestroyersInFlight
728
+ if (!__destroyers) {
729
+ break
730
+ }
731
+ }
732
+ for (const __destroy of __destroyers) {
733
+ __attemptedDestroyers.add(__destroy)
734
+ }
735
+ try {
736
+ await __promise
737
+ } catch (error) {
738
+ if (!__failed) {
739
+ __failed = true
740
+ __primaryError = error
741
+ } else {
742
+ __attachCleanupError(__primaryError, error)
743
+ }
744
+ }
745
+ }
746
+ if (__failed) {
747
+ throw __primaryError
748
+ }
749
+ }
750
+
751
+ function __destroyManagedEmnapiContextsBeforeExit() {
752
+ // A once listener is consumed before Node invokes it, including when another
753
+ // cleanup batch is still pending.
754
+ __managedBeforeExitListener = undefined
755
+ if (__managedDestroyPromise) {
756
+ return
757
+ }
758
+ void __destroyManagedEmnapiContexts().catch((error) => {
759
+ queueMicrotask(() => {
760
+ throw error
761
+ })
762
+ })
763
+ }
764
+
765
+ function __registerManagedEmnapiContext(__process, __destroy) {
766
+ __managedEmnapiContextDestroyers.add(__destroy)
767
+ if (
768
+ !__managedCleanupProcess &&
769
+ __process &&
770
+ typeof __process.once === 'function' &&
771
+ typeof __process.removeListener === 'function'
772
+ ) {
773
+ __managedCleanupProcess = __process
774
+ }
775
+ let __registered = true
776
+ return () => {
777
+ if (!__registered) {
778
+ return
779
+ }
780
+ __registered = false
781
+ __managedEmnapiContextDestroyers.delete(__destroy)
782
+ if (__managedEmnapiContextDestroyers.size === 0) {
783
+ __removeManagedEmnapiCleanupListeners()
784
+ }
785
+ }
786
+ }
787
+
788
+ async function __createManagedEmnapiContext(
789
+ __prepareEnvCleanup,
790
+ __isPreparingEnvCleanup,
791
+ ) {
792
+ const __process =
793
+ typeof process === 'object' && process !== null ? process : undefined
794
+ const __finishAutoDestroyCapture =
795
+ __captureEmnapiAutoDestroyListener(__process)
796
+ let __emnapiContext
797
+ let __contextInitializationError
798
+ let __contextInitializationFailed = false
799
+ try {
800
+ __emnapiContext = __wrapEmnapiContextDestroyForSettlement(
801
+ __emnapiCreateContext({ autoDestroy: false }),
802
+ __prepareEnvCleanup,
803
+ __isPreparingEnvCleanup,
804
+ )
805
+ // emnapi 2.x still registers an unconditional process.once('beforeExit')
806
+ // auto-destroy listener on Node hosts, and suppressDestroy() only
807
+ // neutralizes its callback without removing it. This loader must stay
808
+ // side-effect free per instance, so the listener is captured and removed;
809
+ // suppressDestroy() remains the safety net when removal is unavailable.
810
+ __emnapiContext.suppressDestroy()
811
+ } catch (error) {
812
+ __contextInitializationError = error
813
+ __contextInitializationFailed = true
814
+ } finally {
815
+ // Remove only the exact emnapi callback captured above.
816
+ __finishAutoDestroyCapture?.()
817
+ }
818
+ if (__emnapiContext === undefined) {
819
+ throw __contextInitializationError
820
+ }
821
+ let __disposed = false
822
+ let __destroying = false
823
+ let __destroyPromise
824
+ let __cleanupRegistered = false
825
+ let __unregisterCleanup
826
+ const __destroy = (__blocksModuleLifecycle = false) => {
827
+ if (__disposed) {
828
+ return
829
+ }
830
+ if (__destroying) {
831
+ throw __createLifecycleReentryError('dispose')
832
+ }
833
+ if (__destroyPromise) {
834
+ return __destroyPromise
835
+ }
836
+ __destroying = true
837
+ let __result
838
+ const __finishDestroyInvocation = () => {
839
+ __destroying = false
840
+ }
841
+ const __finishModuleLifecycleDestroy = () => {
842
+ if (__blocksModuleLifecycle) {
843
+ __blocksModuleLifecycle = false
844
+ __moduleLifecycleDestroyDepth--
845
+ }
846
+ }
847
+ if (__blocksModuleLifecycle) {
848
+ __moduleLifecycleDestroyDepth++
849
+ }
850
+ try {
851
+ // Context.destroy() disables JS before cleanup hooks run, so settle
852
+ // runtime-owned promises while this environment can still call JS.
853
+ __prepareEnvCleanup?.()
854
+ if (__isPreparingEnvCleanup?.()) {
855
+ // Reached from inside the barrier, so `Context.destroy()` below would
856
+ // hit the wrapper's in-flight no-op. Recording that as a completed
857
+ // destroy is what makes the frame that *did* start the barrier skip the
858
+ // real one afterwards, leaving the context retained with its cleanup
859
+ // hooks unrun. Refuse instead: nothing is flagged, the context stays
860
+ // registered for managed beforeExit cleanup, and a later destroy still
861
+ // works. dispose() coalesces reentrancy before it can get here, so this
862
+ // is the backstop for any other caller that manages to.
863
+ throw __createLifecycleReentryError('dispose')
864
+ }
865
+ __result = __emnapiContext.destroy()
866
+ } catch (error) {
867
+ __finishDestroyInvocation()
868
+ __finishModuleLifecycleDestroy()
869
+ throw error
870
+ }
871
+ let __then
872
+ try {
873
+ if (
874
+ __result !== null &&
875
+ (typeof __result === 'object' || typeof __result === 'function')
876
+ ) {
877
+ __then = __result.then
878
+ }
879
+ } catch (error) {
880
+ __finishDestroyInvocation()
881
+ __finishModuleLifecycleDestroy()
882
+ throw error
883
+ }
884
+ if (typeof __then === 'function') {
885
+ let __resolveResult
886
+ let __rejectResult
887
+ const __resultPromise = new Promise((resolve, reject) => {
888
+ __resolveResult = resolve
889
+ __rejectResult = reject
890
+ })
891
+ const __promise = __resultPromise.then(
892
+ (value) => {
893
+ __finishDestroyInvocation()
894
+ __finishModuleLifecycleDestroy()
895
+ __disposed = true
896
+ __destroyPromise = undefined
897
+ __unregisterCleanup?.()
898
+ return value
899
+ },
900
+ (error) => {
901
+ __finishDestroyInvocation()
902
+ __finishModuleLifecycleDestroy()
903
+ __destroyPromise = undefined
904
+ throw error
905
+ },
906
+ )
907
+ __destroyPromise = __promise
908
+ try {
909
+ Reflect.apply(__then, __result, [__resolveResult, __rejectResult])
910
+ } catch (error) {
911
+ __rejectResult(error)
912
+ }
913
+ return __promise
914
+ }
915
+ __finishDestroyInvocation()
916
+ __finishModuleLifecycleDestroy()
917
+ __disposed = true
918
+ __unregisterCleanup?.()
919
+ }
920
+ const __destroyForModuleLifecycle = () => __destroy(true)
921
+ const __registerCleanup = (
922
+ __beforeExitDestroy = __destroyForModuleLifecycle,
923
+ ) => {
924
+ if (__cleanupRegistered || __disposed) {
925
+ return
926
+ }
927
+ __unregisterCleanup = __registerManagedEmnapiContext(
928
+ __process,
929
+ __beforeExitDestroy,
930
+ )
931
+ __cleanupRegistered = true
932
+ __registerManagedBeforeExitListener()
933
+ }
934
+ if (__contextInitializationFailed) {
935
+ let __registrationError
936
+ let __registrationFailed = false
937
+ try {
938
+ __registerCleanup()
939
+ } catch (error) {
940
+ __attachCleanupError(__contextInitializationError, error)
941
+ __registrationError = error
942
+ __registrationFailed = true
943
+ }
944
+ try {
945
+ await __destroyForModuleLifecycle()
946
+ } catch (error) {
947
+ __attachCleanupError(
948
+ __registrationFailed
949
+ ? __registrationError
950
+ : __contextInitializationError,
951
+ error,
952
+ )
953
+ try {
954
+ __registerManagedBeforeExitListener()
955
+ } catch {}
956
+ }
957
+ throw __contextInitializationError
958
+ }
959
+ return {
960
+ context: __emnapiContext,
961
+ destroy: __destroy,
962
+ destroyForModuleLifecycle: __destroyForModuleLifecycle,
963
+ registerCleanup: __registerCleanup,
964
+ }
965
+ }
966
+
967
+ async function __createInstance(
968
+ __wasmInput,
969
+ __options,
970
+ __beforeExitDestroy,
971
+ __onManagedDestroyer,
972
+ ) {
973
+ const __module = await __resolveModule(__wasmInput)
974
+ const __emnapiModule = await __normalizeModuleForEmnapi(__module)
975
+ const __wasi = new __WASI({
976
+ version: 'preview1',
977
+ })
978
+ // The wasm module is linked with `--import-memory`, so a Memory must be
979
+ // provided. It is resolved here in function scope (workerd bans global scope
980
+ // allocation) and is never shared (no threads, no SharedArrayBuffer).
981
+ // Resolve it before the emnapi context so a rejected option bag or a host
982
+ // memory-limit failure cannot leak a context that never reaches
983
+ // instantiation.
984
+ const __wasmMemory = __resolveInstanceMemory(__options)
985
+ let __lifecycleState = 'pending'
986
+ let __destroyEmnapiContext
987
+ let __destroyOwnedContext
988
+ let __destroyManagedOwnedContext
989
+ let __napiInstance
990
+ let __wasmEnvCleanupRan = false
991
+ let __wasmEnvCleanupPrepared = false
992
+ let __wasmEnvCleanupPreparing = false
993
+ let __wasmEnvCleanupDrained = false
994
+ let __wasmEnvCleanupDrainPromise
995
+ const __isPreparingEnvCleanup = () => __wasmEnvCleanupPreparing
996
+ const __prepareEnvCleanup = () => {
997
+ if (__wasmEnvCleanupPrepared || __wasmEnvCleanupPreparing) {
998
+ return
999
+ }
1000
+ const __prepareWasmEnvCleanup =
1001
+ __napiInstance?.exports.napi_prepare_wasm_env_cleanup
1002
+ if (typeof __prepareWasmEnvCleanup === 'function') {
1003
+ // The addon settles the promises it cancels synchronously, under a
1004
+ // non-reentrant lifecycle mutex: anything a promise hook calls from in
1005
+ // here must not reach this export again.
1006
+ __wasmEnvCleanupPreparing = true
1007
+ try {
1008
+ __prepareWasmEnvCleanup()
1009
+ } finally {
1010
+ __wasmEnvCleanupPreparing = false
1011
+ }
1012
+ __wasmEnvCleanupRan = true
1013
+ }
1014
+ __wasmEnvCleanupPrepared = true
1015
+ }
1016
+ // The barrier + settlement drain, hoisted out of the context destroyer so the
1017
+ // drain can yield without widening the destroyer's reentry window. Both
1018
+ // yielding paths run it — dispose() and the initialization-failure rollback.
1019
+ // The destroyer still runs the barrier itself (idempotently) for the one path
1020
+ // that cannot yield: managed beforeExit cleanup of an instance whose rollback
1021
+ // is being retried.
1022
+ //
1023
+ // "Drained" is recorded only once a wait has actually finished. Scheduling a
1024
+ // macrotask can fail — a host-provided or patched `setImmediate` that throws
1025
+ // is enough — and dispose() stays retryable after it rejects, so marking the
1026
+ // drain complete up front would make the retry skip it and destroy the context
1027
+ // with the barrier's settlements still queued.
1028
+ const __prepareForDisposal = () => {
1029
+ if (__wasmEnvCleanupDrained) {
1030
+ return
1031
+ }
1032
+ if (__wasmEnvCleanupDrainPromise) {
1033
+ return __wasmEnvCleanupDrainPromise
1034
+ }
1035
+ __prepareEnvCleanup()
1036
+ if (!__wasmEnvCleanupRan) {
1037
+ return
1038
+ }
1039
+ const __drained = __drainWasmEnvCleanup(__napiInstance)
1040
+ if (!__drained || typeof __drained.then !== 'function') {
1041
+ __wasmEnvCleanupDrained = true
1042
+ return
1043
+ }
1044
+ const __tracked = __drained.then(
1045
+ (__value) => {
1046
+ __wasmEnvCleanupDrained = true
1047
+ __wasmEnvCleanupDrainPromise = undefined
1048
+ return __value
1049
+ },
1050
+ (__error) => {
1051
+ __wasmEnvCleanupDrainPromise = undefined
1052
+ throw __error
1053
+ },
1054
+ )
1055
+ __wasmEnvCleanupDrainPromise = __tracked
1056
+ return __tracked
1057
+ }
1058
+ let __disposed = false
1059
+ const __runInstanceDisposal = async () => {
1060
+ if (__lifecycleState !== 'failed') {
1061
+ __lifecycleState = 'disposal'
1062
+ }
1063
+ // Outstanding async work first, while the environment is still completely
1064
+ // live: its completion callbacks run addon code, and the barrier and
1065
+ // `Context.destroy()` below each take that away. Undefined unless work is
1066
+ // outstanding.
1067
+ const __asyncWorkDrained = __drainInstanceAsyncWork(__napiInstance)
1068
+ if (__asyncWorkDrained) {
1069
+ await __asyncWorkDrained
1070
+ }
1071
+ // Then settle what the barrier cancelled, before the environment stops
1072
+ // accepting JavaScript calls. Undefined unless something is queued, so
1073
+ // an idle disposal is not delayed by a single turn.
1074
+ const __drained = __prepareForDisposal()
1075
+ if (__drained) {
1076
+ await __drained
1077
+ }
1078
+ const __result = await (__beforeExitDestroy
1079
+ ? __destroyManagedOwnedContext()
1080
+ : __destroyOwnedContext())
1081
+ // Only a completed destroy retires the instance; a throw above leaves
1082
+ // the counter untouched so a retried dispose() cannot double-decrement.
1083
+ if (!__disposed) {
1084
+ __disposed = true
1085
+ __liveInstances -= 1
1086
+ }
1087
+ return __result
1088
+ }
1089
+ let __instanceDisposePromise
1090
+ /**
1091
+ * The disposal frame runs the barrier, and the barrier settles the promises it
1092
+ * cancels synchronously — so a promise hook firing inside it can call this
1093
+ * same instance's dispose() again while the first call is still in its drain.
1094
+ * That nested call finds the barrier flagged in flight, prepares nothing,
1095
+ * drains nothing, and falls straight through to the context destroyer, whose
1096
+ * `Context.destroy()` hits the wrapper's in-flight no-op. It would record a
1097
+ * destruction that never happened, and the outer frame would then skip the
1098
+ * real one: both disposals resolve, no cleanup hook runs, the context stays
1099
+ * retained.
1100
+ *
1101
+ * Memoize before any of that starts, exactly like the eager loaders'
1102
+ * `__disposeWasiBinding`, so there is only ever one disposal frame per
1103
+ * instance and a reentrant caller awaits it instead of racing it. Cleared on
1104
+ * rejection: a drain that failed has to stay retryable.
1105
+ */
1106
+ const __disposeInstance = () => {
1107
+ if (__instanceDisposePromise) {
1108
+ return __instanceDisposePromise
1109
+ }
1110
+ let __resolveDispose
1111
+ let __rejectDispose
1112
+ const __disposePromise = new Promise((__resolve, __reject) => {
1113
+ __resolveDispose = __resolve
1114
+ __rejectDispose = __reject
1115
+ })
1116
+ __instanceDisposePromise = __disposePromise
1117
+ __runInstanceDisposal().then(__resolveDispose, (__error) => {
1118
+ __instanceDisposePromise = undefined
1119
+ __rejectDispose(__error)
1120
+ })
1121
+ return __disposePromise
1122
+ }
1123
+ const __destroyBeforeExit = __beforeExitDestroy
1124
+ ? async () => {
1125
+ if (__lifecycleState === 'failed') {
1126
+ await __destroyManagedOwnedContext()
1127
+ return
1128
+ }
1129
+ __lifecycleState = 'disposal'
1130
+ try {
1131
+ await __beforeExitDestroy()
1132
+ } catch (error) {
1133
+ if (__lifecycleState !== 'failed') {
1134
+ throw error
1135
+ }
1136
+ // The singleton's initialization rejection is already observable
1137
+ // through instantiate() and dispose(). Managed beforeExit cleanup
1138
+ // owns only context destruction, including retrying a failed rollback.
1139
+ await __destroyManagedOwnedContext()
1140
+ }
1141
+ }
1142
+ : undefined
1143
+ const {
1144
+ context: __emnapiContext,
1145
+ destroy,
1146
+ destroyForModuleLifecycle,
1147
+ registerCleanup: __registerCleanup,
1148
+ } = await __createManagedEmnapiContext(
1149
+ __prepareEnvCleanup,
1150
+ __isPreparingEnvCleanup,
1151
+ )
1152
+ __destroyEmnapiContext = destroy
1153
+ __destroyOwnedContext = () => __destroyEmnapiContext()
1154
+ __destroyManagedOwnedContext = destroyForModuleLifecycle
1155
+ try {
1156
+ if (__destroyBeforeExit) {
1157
+ __onManagedDestroyer(__destroyBeforeExit)
1158
+ await __registerCleanup(__destroyBeforeExit)
1159
+ }
1160
+ let __napiModule
1161
+ ;({
1162
+ instance: __napiInstance,
1163
+ napiModule: __napiModule,
1164
+ } = await __emnapiInstantiateNapiModule(__emnapiModule, {
1165
+ context: __emnapiContext,
1166
+ asyncWorkPoolSize: 0,
1167
+ plugins: [__emnapiAsyncWorkPlugin, __emnapiTSFNPlugin],
1168
+ wasi: __wasi,
1169
+ overwriteImports(importObject) {
1170
+ importObject.env = {
1171
+ ...importObject.env,
1172
+ ...importObject.napi,
1173
+ ...importObject.emnapi,
1174
+ memory: __wasmMemory,
1175
+ }
1176
+ return importObject
1177
+ },
1178
+ beforeInit({ instance }) {
1179
+ __napiInstance = instance
1180
+ for (const name of Object.keys(instance.exports)) {
1181
+ if (name.startsWith('__napi_register__')) {
1182
+ instance.exports[name]()
1183
+ }
1184
+ }
1185
+ },
1186
+ }))
1187
+ // `instantiate()` and `createInstance().exports` hand out this object; a
1188
+ // named module export does not travel with it. After the instance host
1189
+ // install, which hands the same object to addon-provided registration
1190
+ // functions that may put anything on it, and inside this `try`, so a
1191
+ // claimed name flips `__lifecycleState` to 'failed' and tears the instance
1192
+ // down rather than escaping a half-built one.
1193
+ __napiStampBindingTarget(__napiModule.exports, __napiBindingTarget)
1194
+ if (__lifecycleState === 'pending') {
1195
+ __lifecycleState = 'succeeded'
1196
+ }
1197
+ __createdInstances += 1
1198
+ __liveInstances += 1
1199
+ return {
1200
+ exports: __napiModule.exports,
1201
+ get memory() {
1202
+ return __wasmMemory
1203
+ },
1204
+ get memoryBytes() {
1205
+ // The Memory outlives the environment, so this stays readable after a
1206
+ // FAILED dispose() (which leaves the instance undisposed and
1207
+ // retryable). It reports 0 only once disposal has actually completed.
1208
+ return __disposed ? 0 : __wasmMemory.buffer.byteLength
1209
+ },
1210
+ get disposed() {
1211
+ return __disposed
1212
+ },
1213
+ dispose: __disposeInstance,
1214
+ }
1215
+ } catch (error) {
1216
+ __lifecycleState = 'failed'
1217
+ // Instantiation can fail *after* registration has run, and registration runs
1218
+ // with a live environment: a module-init hook can start async work and then
1219
+ // return an error, and the promise it created may already have escaped into
1220
+ // JavaScript. Settle what the barrier cancels before the context is
1221
+ // destroyed, exactly like dispose() does; destroying without yielding
1222
+ // discards the queue with a null env. Undefined unless something is queued,
1223
+ // so a failure before beforeInit costs no extra turn.
1224
+ let __settlementsUnreached = false
1225
+ try {
1226
+ // Registration can start async work too, and this path destroys the same
1227
+ // environment its completions need. A drain that cannot finish leaves the
1228
+ // work outstanding, so it counts as settlements unreached and stops the
1229
+ // rollback short of destroying, exactly like a failed barrier drain.
1230
+ const __asyncWorkDrained = __drainInstanceAsyncWork(__napiInstance)
1231
+ if (__asyncWorkDrained) {
1232
+ await __asyncWorkDrained
1233
+ }
1234
+ const __drained = __prepareForDisposal()
1235
+ if (__drained) {
1236
+ await __drained
1237
+ }
1238
+ } catch (drainError) {
1239
+ __attachCleanupError(error, drainError)
1240
+ __settlementsUnreached = true
1241
+ }
1242
+ let __registrationError
1243
+ let __registrationFailed = false
1244
+ if (!__beforeExitDestroy) {
1245
+ try {
1246
+ // Independent instances are caller-owned while pending and after
1247
+ // success. Register only failed rollback so cleanup remains retryable.
1248
+ await __registerCleanup()
1249
+ } catch (registrationError) {
1250
+ __attachCleanupError(error, registrationError)
1251
+ __registrationError = registrationError
1252
+ __registrationFailed = true
1253
+ }
1254
+ }
1255
+ if (__settlementsUnreached) {
1256
+ // The barrier or the drain did not finish, so the settlements it queued
1257
+ // are still in the threadsafe-function queue. Destroying now runs the
1258
+ // cleanup hook that drains that queue with a null env and discards it,
1259
+ // stranding a promise that already escaped into JavaScript — with nothing
1260
+ // left that could ever settle it. dispose() refuses to destroy for exactly
1261
+ // this reason (a rejected drain there never reaches the destroy), so this
1262
+ // path refuses too.
1263
+ //
1264
+ // Nothing leaks. The registration just above — and, for the singleton, the
1265
+ // one made before instantiation — leaves this context in
1266
+ // `__managedEmnapiContextDestroyers`, so beforeExit destroys it and
1267
+ // dispose() can retry it. Only the destruction is deferred, and the turns
1268
+ // that pass in the meantime are exactly what the queue needed.
1269
+ try {
1270
+ __registerManagedBeforeExitListener()
1271
+ } catch {}
1272
+ throw error
1273
+ }
1274
+ try {
1275
+ await __destroyManagedOwnedContext()
1276
+ } catch (disposeError) {
1277
+ // Initialization is the primary failure. Preserve it even if cleanup
1278
+ // also fails, while retaining the cleanup error when the value is
1279
+ // extensible and has no existing cause.
1280
+ __attachCleanupError(
1281
+ __registrationFailed ? __registrationError : error,
1282
+ disposeError,
1283
+ )
1284
+ try {
1285
+ __registerManagedBeforeExitListener()
1286
+ } catch {}
1287
+ }
1288
+ throw error
1289
+ }
1290
+ }
1291
+
1292
+ /**
1293
+ * Create an independent instance. Call and await dispose() when the instance
1294
+ * is no longer needed so emnapi cleanup hooks run deterministically.
1295
+ *
1296
+ * The optional second argument selects this instance's linear memory: either
1297
+ * `memory` (an unshared, single-use WebAssembly.Memory you allocated) or
1298
+ * `initialMemoryPages` / `maximumMemoryPages`, never both. Omitted, the
1299
+ * loader allocates WASM_MEMORY.initialPages..WASM_MEMORY.maximumPages.
1300
+ *
1301
+ * A provided Memory must come from this loader's own realm: the WASI and
1302
+ * emnapi layers underneath identify one with a realm-local `instanceof`, so a
1303
+ * Memory built in a `node:vm` context or another frame is rejected. Every
1304
+ * Memory an instance runs on is single-use, the loader-allocated one included:
1305
+ * `instance.memory` cannot be recycled into a second `createInstance()`.
1306
+ */
1307
+ export async function createInstance(__wasmInput, __options) {
1308
+ return __createInstance(__wasmInput, __options)
1309
+ }
1310
+
1311
+ let __defaultModulePromise
1312
+ let __defaultInstancePromise
1313
+ let __defaultDisposePromise
1314
+ let __defaultDisposalStarted = false
1315
+ const __defaultManagedDestroyers = new WeakMap()
1316
+ let __moduleDisposePromise
1317
+
1318
+ /**
1319
+ * Instantiate a module-local singleton. Concurrent and repeated calls
1320
+ * with the same module share one instance and one Memory allocation.
1321
+ */
1322
+ export function instantiate(__wasmInput) {
1323
+ const __modulePromise = __resolveModule(__wasmInput)
1324
+ if (__moduleLifecycleDestroyDepth !== 0) {
1325
+ void __modulePromise.catch(() => {})
1326
+ return Promise.reject(__createLifecycleReentryError('instantiate'))
1327
+ }
1328
+ if (__moduleDisposePromise) {
1329
+ void __modulePromise.catch(() => {})
1330
+ return __moduleDisposePromise.then(() => instantiate(__modulePromise))
1331
+ }
1332
+ if (__defaultDisposalStarted) {
1333
+ // Observe rejected input immediately, but preserve lifecycle ordering and
1334
+ // error precedence by instantiating only after disposal succeeds. A failed
1335
+ // disposal retains the old instance only so its cleanup can be retried.
1336
+ void __modulePromise.catch(() => {})
1337
+ const __disposePromise = __defaultDisposePromise ?? dispose()
1338
+ return __disposePromise.then(() => instantiate(__modulePromise))
1339
+ }
1340
+ if (!__defaultInstancePromise) {
1341
+ __defaultModulePromise = __modulePromise
1342
+ const __instancePromise = __modulePromise.then((__module) =>
1343
+ __createInstance(
1344
+ __module,
1345
+ undefined,
1346
+ __disposeDefaultInstance,
1347
+ (__managedDestroyer) => {
1348
+ __defaultManagedDestroyers.set(
1349
+ __instancePromise,
1350
+ __managedDestroyer,
1351
+ )
1352
+ },
1353
+ ),
1354
+ )
1355
+ __defaultInstancePromise = __instancePromise
1356
+ void __instancePromise.catch(() => {
1357
+ if (__defaultInstancePromise === __instancePromise) {
1358
+ __defaultInstancePromise = undefined
1359
+ __defaultModulePromise = undefined
1360
+ }
1361
+ })
1362
+ return __instancePromise.then((__instance) => __instance.exports)
1363
+ }
1364
+ const __defaultModulePromiseForCall = __defaultModulePromise
1365
+ const __defaultInstancePromiseForCall = __defaultInstancePromise
1366
+ return Promise.all([__defaultModulePromiseForCall, __modulePromise]).then(
1367
+ async ([__defaultModule, __module]) => {
1368
+ if (__defaultModule !== __module) {
1369
+ throw new Error(
1370
+ 'instantiate() already owns a different WebAssembly.Module; call dispose() first or use createInstance() for independent instances.',
1371
+ )
1372
+ }
1373
+ return (await __defaultInstancePromiseForCall).exports
1374
+ },
1375
+ )
1376
+ }
1377
+
1378
+ async function __disposeDefaultInstance(__onDestroy) {
1379
+ if (__defaultDisposePromise) {
1380
+ return __defaultDisposePromise
1381
+ }
1382
+ const __instancePromise = __defaultInstancePromise
1383
+ if (!__instancePromise) {
1384
+ __defaultDisposalStarted = false
1385
+ return
1386
+ }
1387
+ __defaultDisposalStarted = true
1388
+ const __disposePromise = (async () => {
1389
+ let __instance
1390
+ try {
1391
+ __instance = await __instancePromise
1392
+ } catch (error) {
1393
+ const __managedDestroyer =
1394
+ __defaultManagedDestroyers.get(__instancePromise)
1395
+ if (__managedDestroyer) {
1396
+ __onDestroy?.(__managedDestroyer)
1397
+ }
1398
+ __defaultManagedDestroyers.delete(__instancePromise)
1399
+ throw error
1400
+ }
1401
+ const __managedDestroyer =
1402
+ __defaultManagedDestroyers.get(__instancePromise)
1403
+ if (__managedDestroyer) {
1404
+ __onDestroy?.(__managedDestroyer)
1405
+ }
1406
+ await __instance.dispose()
1407
+ if (__defaultInstancePromise === __instancePromise) {
1408
+ __defaultInstancePromise = undefined
1409
+ __defaultModulePromise = undefined
1410
+ __defaultDisposalStarted = false
1411
+ }
1412
+ __defaultManagedDestroyers.delete(__instancePromise)
1413
+ })()
1414
+ __defaultDisposePromise = __disposePromise
1415
+ try {
1416
+ await __disposePromise
1417
+ } finally {
1418
+ if (__defaultDisposePromise === __disposePromise) {
1419
+ __defaultDisposePromise = undefined
1420
+ }
1421
+ }
1422
+ }
1423
+
1424
+ async function __dispose() {
1425
+ let __defaultDisposeError
1426
+ let __defaultDisposeFailed = false
1427
+ let __attemptedDefaultDestroyer
1428
+ try {
1429
+ await __disposeDefaultInstance((__destroyer) => {
1430
+ __attemptedDefaultDestroyer = __destroyer
1431
+ })
1432
+ } catch (error) {
1433
+ __defaultDisposeError = error
1434
+ __defaultDisposeFailed = true
1435
+ }
1436
+ const __excludedDestroyers = new Set()
1437
+ if (__defaultDisposeFailed && __attemptedDefaultDestroyer) {
1438
+ __excludedDestroyers.add(__attemptedDefaultDestroyer)
1439
+ }
1440
+ try {
1441
+ await __drainManagedEmnapiContexts(__excludedDestroyers)
1442
+ } catch (error) {
1443
+ if (!__defaultDisposeFailed) {
1444
+ throw error
1445
+ }
1446
+ if (error !== __defaultDisposeError) {
1447
+ __attachCleanupError(__defaultDisposeError, error)
1448
+ }
1449
+ }
1450
+ if (__defaultDisposeFailed) {
1451
+ throw __defaultDisposeError
1452
+ }
1453
+ }
1454
+
1455
+ /**
1456
+ * Dispose the singleton created by instantiate(). A later call may create a
1457
+ * fresh instance, including from a different module. This also retries cleanup
1458
+ * retained after a failed initialization rollback.
1459
+ */
1460
+ export function dispose() {
1461
+ if (__moduleLifecycleDestroyDepth !== 0) {
1462
+ return Promise.reject(__createLifecycleReentryError('dispose'))
1463
+ }
1464
+ if (__moduleDisposePromise) {
1465
+ return __moduleDisposePromise
1466
+ }
1467
+ let __resolveDispose
1468
+ let __rejectDispose
1469
+ const __promise = new Promise((resolve, reject) => {
1470
+ __resolveDispose = resolve
1471
+ __rejectDispose = reject
1472
+ })
1473
+ __moduleDisposePromise = __promise
1474
+ void __dispose().then(__resolveDispose, __rejectDispose)
1475
+ void __promise.then(
1476
+ () => {
1477
+ if (__moduleDisposePromise === __promise) {
1478
+ __moduleDisposePromise = undefined
1479
+ }
1480
+ },
1481
+ () => {
1482
+ if (__moduleDisposePromise === __promise) {
1483
+ __moduleDisposePromise = undefined
1484
+ }
1485
+ },
1486
+ )
1487
+ return __promise
1488
+ }