Typed callback and resource handoff adapters for moonbitlang/async on JS.Uses Hosi121/lifetime for immediate synchronous cleanup. No separate scheduler,dynamic value type, unchecked generic cast, or npm runtime dependency.
wait(life, register, discard?) turns a one-shot callback into a cancellableasync operation. register receives (Result[T, PortError]) -> Unit andreturns a synchronous unregister callback. It may complete synchronously.
acquire(life, register, release~) uses the same release function for bothdiscarded values and values adopted by the lifetime. Ownership is registeredbefore returning, with no intervening async suspension.
run(life, task, failed) enters official async from a synchronous host callback.task receives TaskGroup[Unit]; its children stay in that group. Root completionor failure closes life. Failure is reported after cleanup; cancellation isobserved without an unhandled Promise rejection.
guard_sync(fn) converts a synchronous host exception to checked PortError.Async callback exceptions need their own boundary conversion.
check(life) raises Stopped when closed. This is distinct from officialasync's cancellation signal.
Inbox[T](life, limit~) is bounded ingress from JS callbacks to one asyncreader. offer returns false when full or closed; next waits via the officialPromise bridge and raises ConcurrentRead on overlapping reads. limit mustbe positive (invalid programmer input aborts). Queued values must not requiredisposal; closing drops them, and rejected values remain with the producer.
Successful callbacks transfer fresh values. Calling success again with thesame owned handle is invalid: duplicate success values go to discard/release.Only one result reaches the caller. Failure results after completion are ignored.The host unregister callback runs once on completion, failure or cancellation.If registration throws before returning a callback, it must clean up any partialregistration itself; the adapter cannot discover that resource.
An unabortable operation may still finish after close; the adapter stops waitingand disposes its eventual successful value. A value ready before close but notyet delivered is also disposed. If unregister reentrantly closes the parent,the value is disposed instead of returned. wait callers own delivered valuesand must register cleanup before their next suspension. Prefer acquire forresources; wait is convenient for data.
Cleanup, discard and failed callbacks must not throw. Lifetime close releasesregistered resources synchronously and requests root cancellation; it does notwait for asynchronous finalizers. Use protected official async cleanup foroperations such as a database rollback. Do not wrap arbitrary throwing host I/Oin an async task and expect JS exceptions to become MoonBit checked errors.
T and Result remain in MoonBit. The FFI Promise carries only Unit to wakeofficial async, avoiding exposure of MoonBit's generic representation to JS.
For normal abortable Promise APIs, first consider official js_async.run_promise.For lexical cleanup, use defer / errdefer. This adapter addresses host-drivenlifetime closure and resource delivery races beyond those simple cases.
JS only, tested with official async 0.22.1 and MoonBit 0.10.14. The synchronousHosi121/lifetime module is separately usable on native and Wasm GC.