@mcp-b/do-runtime 0.2.3 → 0.3.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 @@
1
+ {"version":3,"file":"io-context-D89n6HVM.js","names":["#resolve","#reject","#brokenPromise","#gate","#requireGate","#parent","#parentLock","#hooks","#lock","#setBroken","#pastLocksPromise","#brokenState","#setPastLocks","#timer","#timeouts","#nextId","#arm","#cancel","#fire","#tasks","#taskFailed","#actor","#currentInputLocks","#abortPromise","#rejectAbort","#waitUntilTasks","#requireCurrent","#lastGateUse","#abortException","#addTaskCounter","#waitUntilStatus","#runImpl","#awaitIoImpl","#transformGateUses","#transformGateStack","#runCriticalSection","#exit"],"sources":["../../src/io/io-gate.ts","../../src/io/io-context.ts"],"sourcesContent":["/**\n * ← workerd `src/workerd/io/io-gate.{h,c++}`\n *\n * An I/O gate allows someone to \"lock\" a type of I/O so that other concurrent tasks trying to\n * perform that type of I/O are blocked until the lock is released.\n *\n * I/O gates are used in actors to implement consistency guarantees, allowing in-memory state and\n * storage to be synchronized.\n *\n * Each Actor has two main gates:\n * - Input gate: While locked, blocks all incoming I/O events of any type from being delivered to\n * the actor, other than the specific event or events that hold the lock. This includes\n * blocking responses to subrequests, timer events, input streams, etc. Used when storage\n * operations are outstanding, so that awaiting a storage operation does not risk allowing\n * concurrent events that render the state inconsistent.\n * - Output gate: While locked, blocks all outgoing messages from an actor that would allow the\n * rest of the world to observe the actor's state. Held while writes that have been confirmed\n * to the application are still being flushed to disk. If the flush fails, these messages will\n * never be sent, so that the rest of the world cannot observe a prematurely-confirmed write.\n *\n * Three things kj gives for free and JS does not, resolved the same way at every site:\n *\n * 1. **Destructors.** `~Lock` releases and `~CriticalSection` diagnoses a dropped section as\n * deadlock. Both become explicit: `Lock.release()` and `CriticalSection.drop()`. A `Lock`\n * released twice throws rather than corrupting the refcount.\n * 2. **Cancel-by-drop.** Dropping a `kj::Promise` unwinds its waiter. Every such site takes an\n * `AbortSignal`, the convention `Timer.afterDelay` already set in `io-context.ts`. Aborting a\n * `wait()` rejects it with `CanceledError`; a never-settling promise would be an invisible\n * hang, which is what this repo's fail-closed tenet exists to prevent.\n * 3. **`kj::ForkedPromise` holding an exception with no branches.** JS reports that as an\n * unhandled rejection, so every promise this module stores keeps a no-op `catch` of its own\n * and hands observers a separate view.\n *\n * Error strings are copied verbatim from upstream; users and upstream tests match on them.\n *\n * Not ported: `SpanParent`/`SpanBuilder` tracing, since there is no `trace.h` here and upstream's\n * own tests pass `nullptr` at every call site; and the `~InputGate` assertion that no locks\n * outlive the gate, which guards against dangling references GC makes impossible.\n *\n * Spec: §1.1, §1.2, §1.5, decisions 3, 5 and 13 in\n * docs/decisions.md.\n */\n\n/**\n * Raised when a `wait()` is cancelled through its `AbortSignal`.\n *\n * kj has no equivalent, because a cancelled continuation simply never runs. It is deliberately\n * NOT a gate failure: a cancelled waiter leaves the gate exactly as it found it, so\n * `CriticalSection.wait()` rethrows this one without calling `setBroken()`.\n */\nexport class CanceledError extends Error {\n override readonly name = \"CanceledError\";\n}\n\n/** Mirrors `OutputGate::makeUnfulfilledException()`: one place that spells the exception. */\nfunction makeCanceledError(): CanceledError {\n return new CanceledError(\"input gate wait was canceled\");\n}\n\n/**\n * `addEventListener(\"abort\", ...)` never fires for a signal that has already aborted, so a\n * pre-aborted signal would silently hold its lock forever. Every cancellation site goes through\n * here, and every one of them first rejects a pre-aborted wait before touching gate state, so\n * \"cancelled\" always means \"left the gate exactly as it found it\".\n */\nfunction onAbort(signal: AbortSignal | undefined, run: () => void): void {\n if (signal === undefined) return;\n if (signal.aborted) {\n run();\n return;\n }\n signal.addEventListener(\"abort\", run, { once: true });\n}\n\n/**\n * ← `kj::OneOf<kj::Own<kj::PromiseFulfiller<void>>, kj::Exception> brokenState`.\n *\n * `InputGate` starts in `fulfiller` — it builds its promise in the constructor — while\n * `OutputGate` starts in `none` and only makes one when `onBroken()` is first called.\n */\ntype BrokenState =\n | { readonly kind: \"none\" }\n | { readonly kind: \"fulfiller\"; readonly reject: (exception: unknown) => void }\n | { readonly kind: \"exception\"; readonly exception: unknown };\n\n// =======================================================================================\n// InputGate\n\n/**\n * Hooks that can be used to customize InputGate behavior.\n *\n * Technically, everything implemented here could be accomplished by a class that wraps\n * InputGate, but the part of the code that wants to implement these hooks is far away from the\n * part of the code that calls into the InputGate, and so it was more convenient to give the\n * caller a way to inject behavior into InputGate.\n */\nexport interface InputGateHooks {\n inputGateLocked(): void;\n inputGateReleased(): void;\n inputGateWaiterAdded(): void;\n inputGateWaiterRemoved(): void;\n}\n\n/** ← `InputGate::Hooks::DEFAULT`. */\nexport const DEFAULT_INPUT_GATE_HOOKS: InputGateHooks = {\n inputGateLocked() {},\n inputGateReleased() {},\n inputGateWaiterAdded() {},\n inputGateWaiterRemoved() {},\n};\n\n/** ← `InputGate::Waiter`: a `kj::List` node plus the adapted promise's fulfiller. */\nclass Waiter {\n /** Rewritten by `CriticalSection.succeeded()` when a straggler is reparented. */\n gate: InputGate;\n readonly isChildWaiter: boolean;\n /** ← `link.isLinked()`. */\n linked = true;\n\n readonly #resolve: (lock: Lock) => void;\n readonly #reject: (exception: unknown) => void;\n\n constructor(\n gate: InputGate,\n isChildWaiter: boolean,\n resolve: (lock: Lock) => void,\n reject: (exception: unknown) => void,\n ) {\n this.gate = gate;\n this.isChildWaiter = isChildWaiter;\n this.#resolve = resolve;\n this.#reject = reject;\n\n gate.hooks.inputGateWaiterAdded();\n if (isChildWaiter) {\n gate.waitingChildren.push(this);\n } else {\n gate.waiters.push(this);\n }\n }\n\n unlink(): void {\n if (!this.linked) return;\n this.linked = false;\n const list = this.isChildWaiter ? this.gate.waitingChildren : this.gate.waiters;\n const index = list.indexOf(this);\n if (index < 0) {\n throw new Error(\"InputGate::Waiter is linked but absent from its gate's list\");\n }\n list.splice(index, 1);\n }\n\n fulfill(lock: Lock): void {\n this.unlink();\n this.gate.hooks.inputGateWaiterRemoved();\n this.#resolve(lock);\n }\n\n reject(exception: unknown): void {\n this.unlink();\n this.gate.hooks.inputGateWaiterRemoved();\n this.#reject(exception);\n }\n}\n\n/**\n * An InputGate blocks incoming events from being delivered to an actor while the lock is held.\n *\n * Upstream marks the state below `private` and befriends `Lock` and `CriticalSection`.\n * TypeScript has no friendship, and `protected` would not let `CriticalSection` reach these\n * members on its *parent* gate, which `succeeded()` does. The boundary that actually holds is\n * the package facade in `src/index.ts`, which exports none of these types.\n */\nexport class InputGate {\n readonly hooks: InputGateHooks;\n\n /**\n * How many instances of `Lock` currently exist? When this reaches zero, we'll release some\n * waiters.\n *\n * Upstream also carries a `bool isCriticalSection`, because `CriticalSection` inherits\n * `InputGate` privately and has to `static_cast` back. `instanceof` is the same test with no\n * cast and no field that can disagree with the object it describes.\n */\n lockCount = 0;\n\n readonly waiters: Waiter[] = [];\n\n /**\n * Waiters representing CriticalSections that are ready to start. These take priority over other\n * waiters.\n */\n readonly waitingChildren: Waiter[] = [];\n\n /** A fulfiller for onBroken(), or an exception if already broken. */\n brokenState: BrokenState;\n\n readonly #brokenPromise: Promise<never>;\n\n constructor(hooks: InputGateHooks = DEFAULT_INPUT_GATE_HOOKS) {\n this.hooks = hooks;\n const { promise, reject } = Promise.withResolvers<never>();\n this.#brokenPromise = promise;\n this.brokenState = { kind: \"fulfiller\", reject };\n // kj's ForkedPromise just holds the exception until someone adds a branch.\n void promise.catch(() => {});\n }\n\n /** Wait until there are no `Lock`s, then create a new one and return it. */\n wait(signal?: AbortSignal): Promise<Lock> {\n if (signal?.aborted === true) {\n return Promise.reject(makeCanceledError());\n } else if (this.brokenState.kind === \"exception\") {\n return Promise.reject(this.brokenState.exception);\n } else if (this.lockCount === 0) {\n return Promise.resolve(new Lock(this));\n } else {\n return this.newWaiterPromise(false, signal);\n }\n }\n\n /**\n * Rejects if and when calls to `wait()` become broken due to a failed critical section. The\n * actor should be shut down in this case. This promise never resolves, only rejects.\n */\n onBroken(): Promise<never> {\n if (this.brokenState.kind === \"exception\") {\n return Promise.reject(this.brokenState.exception);\n } else {\n return this.#brokenPromise;\n }\n }\n\n /** ← `kj::newAdaptedPromise<Lock, Waiter>(gate, isChildWaiter, span)`. */\n newWaiterPromise(isChildWaiter: boolean, signal?: AbortSignal): Promise<Lock> {\n const { promise, resolve, reject } = Promise.withResolvers<Lock>();\n const waiter = new Waiter(this, isChildWaiter, resolve, reject);\n onAbort(signal, () => {\n // ← `~Waiter` on the cancellation path. A waiter that already settled is unlinked, and\n // cancelling it is the no-op that dropping a settled promise is.\n if (!waiter.linked) return;\n waiter.reject(makeCanceledError());\n });\n return promise;\n }\n\n releaseLock(): void {\n if (this instanceof CriticalSection && this.state === \"REPARENTED\") {\n // This lock was for a critical section that has already completed, therefore the lock\n // should be considered \"reparented\", and we should forward the release to the parent.\n\n // Ensure any waiters on us have already been reparented.\n if (this.waitingChildren.length !== 0 || this.waiters.length !== 0 || this.lockCount !== 0) {\n throw new Error(\"releasing a lock on a reparented CriticalSection that still holds state\");\n }\n\n this.parentAsInputGate().releaseLock();\n return;\n }\n\n if (this.lockCount === 0) {\n throw new Error(\"InputGate::releaseLock() with no locks outstanding\");\n }\n this.lockCount--;\n\n // Check if any waiters can be released.\n if (this.lockCount === 0) {\n this.hooks.inputGateReleased();\n const child = this.waitingChildren[0];\n if (child !== undefined) {\n child.fulfill(new Lock(this));\n } else {\n const waiter = this.waiters[0];\n if (waiter !== undefined) {\n waiter.fulfill(new Lock(this));\n }\n }\n }\n }\n\n /** Called when a critical section fails. All future waiters will throw this exception. */\n setBroken(exception: unknown): void {\n // `reject()` unlinks the waiter it settles, so walk copies of both lists.\n for (const waiter of [...this.waitingChildren]) waiter.reject(exception);\n for (const waiter of [...this.waiters]) waiter.reject(exception);\n if (this.brokenState.kind === \"fulfiller\") {\n this.brokenState.reject(exception);\n }\n this.brokenState = { kind: \"exception\", exception };\n }\n}\n\n/** ← `InputGate::Lock`. A lock that blocks all new events from being delivered while it exists. */\nexport class Lock {\n /** ← \"Becomes null on move.\" Here it becomes undefined on `release()`. */\n #gate: InputGate | undefined;\n\n constructor(gate: InputGate) {\n this.#gate = gate;\n\n // Upstream keeps a second member, `kj::Own<CriticalSection> cs`, whose job is to hold the\n // section alive for the lock's lifetime. `gate` already points at the same object, and GC\n // does the holding, so here it is a local.\n let gateToLock: InputGate = gate;\n if (gate instanceof CriticalSection && gate.state === \"REPARENTED\") {\n gateToLock = gate.parentAsInputGate();\n }\n\n if (++gateToLock.lockCount === 1) {\n gateToLock.hooks.inputGateLocked();\n }\n }\n\n /** ← `~Lock`. */\n release(): void {\n const gate = this.#gate;\n if (gate === undefined) {\n throw new Error(\"InputGate::Lock was released twice\");\n }\n this.#gate = undefined;\n gate.releaseLock();\n }\n\n /**\n * Increments the lock's refcount, returning a duplicate `Lock`. All `Lock`s must be released\n * before the gate is unlocked.\n */\n addRef(): Lock {\n return new Lock(this.#requireGate());\n }\n\n /**\n * Start a new critical section from this lock. After `wait()` has been called on the returned\n * critical section for the first time, no further Locks will be handed out by\n * InputGate::wait() until the CriticalSection has been dropped.\n *\n * CriticalSections can be nested. If this Lock is itself part of a CriticalSection, the new\n * CriticalSection will be nested within it and the outer CriticalSection's wait() won't\n * produce a Lock again until the inner CriticalSection is dropped.\n */\n startCriticalSection(): CriticalSection {\n return new CriticalSection(this.#requireGate());\n }\n\n /** If this lock was taken in a CriticalSection, return it. */\n getCriticalSection(): CriticalSection | undefined {\n const gate = this.#requireGate();\n return gate instanceof CriticalSection ? gate : undefined;\n }\n\n isFor(otherGate: InputGate): boolean {\n if (otherGate instanceof CriticalSection) {\n throw new Error(\"InputGate::Lock::isFor() takes the root gate, not a CriticalSection\");\n }\n\n let ptr = this.#requireGate();\n while (ptr instanceof CriticalSection) {\n ptr = ptr.parentAsInputGate();\n }\n return ptr === otherGate;\n }\n\n /** ← `operator==`. */\n equals(other: Lock): boolean {\n return this.#requireGate() === other.#requireGate();\n }\n\n #requireGate(): InputGate {\n const gate = this.#gate;\n if (gate === undefined) {\n throw new Error(\"InputGate::Lock was used after release\");\n }\n return gate;\n }\n}\n\n/** ← `InputGate::CriticalSection::State`. */\ntype CriticalSectionState =\n /** wait() hasn't been called. */\n | \"NOT_STARTED\"\n /** wait() has been called once, and that wait hasn't finished yet. */\n | \"INITIAL_WAIT\"\n /** First lock has been obtained, waiting for success() or failed(). */\n | \"RUNNING\"\n /** success() or failed() has been called. */\n | \"REPARENTED\";\n\n/**\n * A CriticalSection is a procedure that must not be interrupted by anything \"external\".\n * While a CriticalSection is running, all events that were not initiated by the\n * CriticalSection itself will be blocked from being delivered.\n *\n * The difference between a Lock and a CriticalSection is that a critical section may succeed\n * or fail. A failed critical section permanently breaks the input gate. Locks, on the other\n * hand, are simply released when dropped.\n *\n * A CriticalSection itself holds a Lock, which blocks the \"parent scope\" from continuing\n * execution until the critical section is done. Meanwhile, the code running inside the critical\n * section obtains nested Locks. These nested locks control concurrency of the operations\n * initiated within the critical section in the same way that input locks normally do at the\n * top-level scope. E.g., if a critical section initiates a storage read and a fetch() at the\n * same time, the fetch() is prevented from returning until after the storage read has returned.\n */\nexport class CriticalSection extends InputGate {\n state: CriticalSectionState = \"NOT_STARTED\";\n\n /**\n * Points to the parent scope, which may be another CriticalSection in the case of nesting.\n * ← `kj::OneOf<InputGate*, kj::Own<CriticalSection>>`; the two arms are one `instanceof` apart.\n */\n readonly #parent: InputGate;\n\n /**\n * A lock in the parent scope. `parentLock` becomes non-null after the first lock is obtained,\n * and becomes null again when succeeded() is called.\n */\n #parentLock: Lock | undefined;\n\n constructor(parent: InputGate) {\n // Upstream's CriticalSection has no base initializer, so it gets `Hooks::DEFAULT` rather than\n // the parent gate's hooks: metrics are counted once, at the root.\n super();\n this.#parent = parent;\n }\n\n /**\n * Wait for a nested lock in order to continue this CriticalSection.\n *\n * The first call to wait() begins the CriticalSection. After that wait completes, until the\n * CriticalSection is done and dropped, no other locks will be allowed on this InputGate, except\n * locks requested by calling wait() on this CriticalSection -- or one of its children.\n *\n * Everything before the first `await` runs in the caller's synchronous slice, which is what\n * lets the NOT_STARTED path take its parent lock before any other event can queue behind it.\n */\n override async wait(signal?: AbortSignal): Promise<Lock> {\n // Before the state machine, not inside it: the NOT_STARTED arm takes a parent lock in its\n // synchronous slice, and a cancelled wait must not leave one behind.\n if (signal?.aborted === true) throw makeCanceledError();\n\n for (;;) {\n switch (this.state) {\n case \"NOT_STARTED\": {\n this.state = \"INITIAL_WAIT\";\n\n const target = this.parentAsInputGate();\n if (target.brokenState.kind === \"exception\") {\n // Oops, we're broken.\n const exception = target.brokenState.exception;\n this.setBroken(exception);\n throw exception;\n }\n\n // Add ourselves to this parent's child waiter list.\n if (target.lockCount === 0) {\n this.state = \"RUNNING\";\n this.#parentLock = new Lock(target);\n continue;\n } else {\n let lock: Lock;\n try {\n lock = await target.newWaiterPromise(true, signal);\n } catch (exception) {\n // kj destroys the coroutine on cancellation, so no catch runs and the state stays\n // INITIAL_WAIT — the case `drop()` calls \"had better have been canceled\".\n if (exception instanceof CanceledError) throw exception;\n this.state = \"RUNNING\";\n this.setBroken(exception);\n throw exception;\n }\n this.state = \"RUNNING\";\n this.#parentLock = lock;\n continue;\n }\n }\n case \"INITIAL_WAIT\":\n // To avoid the need for a ForkedPromise, we assume wait() is called once initially to\n // get things started. This is the case in practice because any further tasks would be\n // started only after some code runs under the initial lock.\n throw new Error(\"CriticalSection::wait() should be called once initially\");\n case \"RUNNING\":\n // CriticalSection is active, so defer to InputGate implementation.\n return await super.wait(signal);\n case \"REPARENTED\":\n // Once the CriticalSection has declared itself done, then any straggler tasks it\n // initiated are adopted by the parent. Upstream needs a KJ_SWITCH_ONEOF here so as not\n // to bypass a parent CriticalSection's own override of wait(); JS is always virtual.\n return await this.#parent.wait(signal);\n }\n }\n }\n\n /**\n * Call when the critical section has completed successfully. If this is not called before the\n * CriticalSection is dropped, then failed() is called implicitly.\n *\n * Returns the input lock that was held on the parent critical section. This can be used to\n * continue execution in the parent before any other input arrives.\n */\n succeeded(): Lock {\n if (this.state !== \"RUNNING\") {\n throw new Error(\"CriticalSection::succeeded() requires a running critical section\");\n }\n\n // Once the CriticalSection has declared itself done, then any straggler tasks it initiated are\n // adopted by the parent. Appending preserves FIFO order against the parent's own waiters.\n const parentGate = this.parentAsInputGate();\n for (const waiter of this.waitingChildren) waiter.gate = parentGate;\n parentGate.waitingChildren.push(...this.waitingChildren);\n this.waitingChildren.length = 0;\n for (const waiter of this.waiters) waiter.gate = parentGate;\n parentGate.waiters.push(...this.waiters);\n this.waiters.length = 0;\n parentGate.lockCount += this.lockCount;\n this.lockCount = 0;\n\n this.state = \"REPARENTED\";\n const result = this.#parentLock;\n if (result === undefined) {\n throw new Error(\"CriticalSection::succeeded() with no parent lock\");\n }\n this.#parentLock = undefined;\n return result;\n }\n\n /**\n * Call to indicate the CriticalSection has failed with the given exception. This immediately\n * breaks the InputGate.\n */\n failed(exception: unknown): void {\n if (this.brokenState.kind === \"exception\") {\n // Already failed I guess.\n return;\n }\n\n this.setBroken(exception);\n if (this.#parent instanceof CriticalSection) {\n this.#parent.failed(exception);\n } else {\n this.#parent.setBroken(exception);\n }\n }\n\n /** ← `~CriticalSection`. */\n drop(): void {\n switch (this.state) {\n case \"NOT_STARTED\":\n // Oh well.\n break;\n case \"INITIAL_WAIT\":\n // The initial wait() had better have been canceled... but we have no way to tell here.\n break;\n case \"RUNNING\":\n this.failed(\n new Error(\n \"jsg.Error: A critical section within this Durable Object awaited a Promise that \" +\n \"apparently will never complete. This could happen in particular if a critical \" +\n \"section awaits a task that was initiated outside of the critical section. Since \" +\n \"a critical section blocks all other tasks from completing, this leads to \" +\n \"deadlock.\",\n ),\n );\n break;\n case \"REPARENTED\":\n // Common case.\n break;\n }\n\n // `parentLock` is a `kj::Maybe<Lock>` MEMBER (`io-gate.h:234`), so the destructor body above\n // is followed by its destruction, which hands the parent lock back. After the switch, not\n // inside it: `failed()` must see the lock count upstream would show it.\n //\n // Only RUNNING can still hold one, and not even always — see `wait()`'s catch arm, which\n // sets RUNNING for a section broken during its initial wait without ever acquiring a lock.\n // NOT_STARTED and INITIAL_WAIT never assign it, and REPARENTED gave it away in\n // `succeeded()`. Clearing first, as `succeeded()` does, makes a second drop a no-op rather\n // than a \"released twice\" throw.\n const parentLock = this.#parentLock;\n if (parentLock !== undefined) {\n this.#parentLock = undefined;\n parentLock.release();\n }\n }\n\n /** Return a reference for the parent scope, skipping any reparented CriticalSections */\n parentAsInputGate(): InputGate {\n // Upstream walks a `ptr` starting at `this` and reads `ptr->parent` each turn; walking the\n // parent link itself visits the same chain without aliasing `this`.\n let parent = this.#parent;\n for (;;) {\n if (!(parent instanceof CriticalSection)) return parent;\n if (parent.state !== \"REPARENTED\") return parent;\n // Keep looping...\n parent = parent.#parent;\n }\n }\n}\n\n// =======================================================================================\n// makeReentryCallback\n\n/**\n * ← the gate half of `IoContext::makeReentryCallback()` (`io-context.h:1507`), which is\n * `ctx.run(func, cs)` with the critical section captured here rather than looked up later.\n *\n * Upstream, on why the critical section travels with the callback at all:\n *\n * > \"What if the call was made within blockConcurrencyWhile()? The callback will be blocked until\n * > the critical section ends, which could lead to deadlock if the critical section code is\n * > waiting on it? ... The callback is allowed to run within the critical section\n * > (blockConcurrencyWhile()) from which it was called.\"\n *\n * `criticalSection` must come from the capturing lock's `getCriticalSection()`, at the moment of\n * capture. It cannot be recovered from gate state on invocation: a *new* external event that\n * inherited the running section would skip the queue, and `blockConcurrencyWhile` would silently\n * block nothing (Part 4).\n *\n * The lock covers the callback's synchronous slice and is released when the callback returns\n * control — decision 1, and the reason a callback that awaits something does not wedge the gate.\n * A callback needing the lock across an await takes `lock.addRef()`, which is upstream's\n * `awaitIoWithInputLock` in the one shape io-gate can express (§1.2).\n *\n * The returned function can be called multiple times.\n */\nexport function makeReentryCallback<Args extends unknown[], Result>(\n gate: InputGate,\n criticalSection: CriticalSection | undefined,\n func: (lock: Lock, ...args: Args) => Result | PromiseLike<Result>,\n): (...args: Args) => Promise<Result> {\n return async (...args: Args): Promise<Result> => {\n const lock = await (criticalSection === undefined ? gate.wait() : criticalSection.wait());\n\n let result: Result | PromiseLike<Result>;\n try {\n result = func(lock, ...args);\n } finally {\n lock.release();\n }\n return await result;\n };\n}\n\n// =======================================================================================\n// OutputGate\n\n/**\n * Hooks that can be used to customize OutputGate behavior. See `InputGateHooks` for why these\n * are injected rather than wrapped.\n */\nexport interface OutputGateHooks {\n /**\n * Optionally make a promise which should be raced with the lock promise to implement a\n * timeout. The returned promise should be something that throws an exception after some\n * timeout has expired.\n */\n makeTimeoutPromise(): Promise<never>;\n\n outputGateLocked(): void;\n outputGateReleased(): void;\n outputGateWaiterAdded(): void;\n outputGateWaiterRemoved(): void;\n}\n\n/** ← `OutputGate::Hooks::DEFAULT`. */\nexport const DEFAULT_OUTPUT_GATE_HOOKS: OutputGateHooks = {\n /** ← `kj::NEVER_DONE`. */\n makeTimeoutPromise: () => new Promise<never>(() => {}),\n outputGateLocked() {},\n outputGateReleased() {},\n outputGateWaiterAdded() {},\n outputGateWaiterRemoved() {},\n};\n\n/** ← `kj::Own<kj::PromiseFulfiller<void>>`, the one link `lockWhile` holds in the chain. */\ninterface VoidFulfiller {\n isWaiting(): boolean;\n fulfill(): void;\n reject(exception: unknown): void;\n}\n\n/** ← `OutputGate::makeUnfulfilledException()`. */\nfunction makeUnfulfilledException(): Error {\n return new Error(\"output lock was canceled before completion\");\n}\n\n/**\n * An OutputGate blocks outgoing messages from an Actor until writes which they might depend on\n * are confirmed.\n *\n * A promise chain, not a counter (§1.1): each `lockWhile` joins a new link onto the chain and\n * re-forks it, so a `wait()` is bound to exactly the locks outstanding when it was taken and is\n * unaffected by any later `lockWhile`.\n */\nexport class OutputGate {\n readonly #hooks: OutputGateHooks;\n #pastLocksPromise: Promise<void> = Promise.resolve();\n /** A fulfiller for onBroken(), or an exception if already broken. */\n #brokenState: BrokenState = { kind: \"none\" };\n\n constructor(hooks: OutputGateHooks = DEFAULT_OUTPUT_GATE_HOOKS) {\n this.#hooks = hooks;\n }\n\n /**\n * Block all future `wait()` calls until `promise` completes. Returns a wrapper around\n * `promise`. If `promise` rejects, the exception will propagate to all future `wait()`s. If the\n * returned promise is canceled before completion, all future `wait()`s will also throw.\n */\n lockWhile<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {\n const fulfiller = this.#lock();\n const raced = Promise.race([promise, this.#hooks.makeTimeoutPromise()]);\n\n this.#hooks.outputGateLocked();\n\n return new Promise<T>((resolve, reject) => {\n // ← the `kj::defer(rejectIfCanceled)` arm that runs when the coroutine is destroyed.\n // Upstream leaves the dropped promise unobservable; here the caller still holds it, and a\n // promise that never settles is a hang nobody can see, so it takes the same exception.\n onAbort(signal, () => {\n // The guard comes first, as it does in `Waiter`. Upstream can call the hook before its\n // own check because `kj::defer` runs once ever, on whichever path exits the scope; an\n // abort listener can fire after the lock already settled, so the invariant to preserve\n // is \"exactly one release per lockWhile\", not upstream's statement order.\n if (!fulfiller.isWaiting()) return;\n this.#hooks.outputGateReleased();\n const exception = makeUnfulfilledException();\n this.#setBroken(exception);\n fulfiller.reject(exception);\n reject(exception);\n });\n\n void raced.then(\n (value) => {\n // kj would have destroyed this frame on cancellation; there is nothing left to settle.\n if (!fulfiller.isWaiting()) return;\n fulfiller.fulfill();\n this.#hooks.outputGateReleased();\n resolve(value);\n },\n (exception: unknown) => {\n if (!fulfiller.isWaiting()) return;\n this.#setBroken(exception);\n fulfiller.reject(exception);\n this.#hooks.outputGateReleased();\n reject(exception);\n },\n );\n });\n }\n\n /**\n * Wait until all preceding locks are released. The wait will not be affected by any future\n * call to `lockWhile()`.\n */\n wait(): Promise<void> {\n this.#hooks.outputGateWaiterAdded();\n return this.#pastLocksPromise.then(\n () => {\n this.#hooks.outputGateWaiterRemoved();\n },\n (exception: unknown) => {\n this.#hooks.outputGateWaiterRemoved();\n throw exception;\n },\n );\n }\n\n /**\n * Rejects if and when calls to `wait()` become broken due to a failed lockWhile(). The actor\n * should be shut down in this case. This promise never resolves, only rejects.\n *\n * This method can only be called once.\n */\n onBroken(): Promise<never> {\n if (this.#brokenState.kind === \"fulfiller\") {\n throw new Error(\"onBroken() can only be called once\");\n }\n\n if (this.#brokenState.kind === \"exception\") {\n return Promise.reject(this.#brokenState.exception);\n } else {\n const { promise, reject } = Promise.withResolvers<never>();\n this.#brokenState = { kind: \"fulfiller\", reject };\n void promise.catch(() => {});\n return promise;\n }\n }\n\n isBroken(): boolean {\n return this.#brokenState.kind === \"exception\";\n }\n\n #lock(): VoidFulfiller {\n const { promise, resolve, reject } = Promise.withResolvers<void>();\n\n // ← `kj::joinPromises`, which waits for EVERY branch and only then propagates the first\n // exception. `Promise.all` is fail-fast, which \"OutputGate exception\" explicitly forbids: a\n // later lock failing must not release an earlier `wait()`.\n this.#setPastLocks(\n Promise.allSettled([this.#pastLocksPromise, promise]).then((results) => {\n for (const result of results) {\n if (result.status === \"rejected\") throw result.reason;\n }\n }),\n );\n\n let waiting = true;\n return {\n isWaiting: () => waiting,\n fulfill: () => {\n waiting = false;\n resolve();\n },\n reject: (exception) => {\n waiting = false;\n reject(exception);\n },\n };\n }\n\n #setPastLocks(promise: Promise<void>): void {\n this.#pastLocksPromise = promise;\n // kj's ForkedPromise holds a rejection with no branches attached; JS calls that unhandled.\n void promise.catch(() => {});\n }\n\n #setBroken(exception: unknown): void {\n // We assume the exception is already propagated into `pastLocksPromise`, so all we need to do\n // is handle onBroken().\n if (this.#brokenState.kind === \"fulfiller\") {\n this.#brokenState.reject(exception);\n }\n this.#brokenState = { kind: \"exception\", exception };\n }\n}\n","/**\n * ← workerd `src/workerd/io/io-context.{h,c++}`\n *\n * IoContext: the one door, plus the two await forms.\n *\n * This is the file with no true upstream correspondence for its *enforcement*.\n * Workerd acquires the input gate at isolate entry, so acquisition is\n * structural. We have no isolate hook, so a lock is taken at our own dispatch\n * boundary instead. See \"The enforcement point is the one thing we cannot port\"\n * in the design record.\n *\n * What this file does, stated before anything about how it got here:\n *\n * 1. `#currentInputLocks` is a STACK of the locks held by the slices that are\n * running. It is upstream's single `kj::Maybe<InputGate::Lock>\n * currentInputLock` member (`io-context.h:993`) with the `Maybe` widened,\n * and a \"frame\" is an entry in it. A frame carries nothing else: the only\n * two things ever read from one are the lock (`getInputLock`) and the\n * critical section that lock belongs to (`getCriticalSection`), and the\n * lock answers both.\n * 2. A lock is released, and leaves the stack, at the END OF THE MICROTASK\n * CHECKPOINT of the slice holding it — not when that slice's synchronous\n * body returns. That is upstream's own boundary, not a relaxation of it:\n * `runImpl`'s inner `KJ_DEFER` calls `js.runMicrotasks()`\n * (`io-context.c++:1262`) and `runInContextScope`'s outer one then clears\n * `currentInputLock` (`:1214`), inner scope first, so the whole checkpoint\n * drains under the lock. `currentInputLock` holds the `Lock` by value, so\n * clearing it is the release. `atCheckpointEnd` below is that point.\n * 3. NEITHER await form releases anything at the moment of the await.\n * `awaitIo` reads `getCriticalSection()` and `awaitIoWithInputLock` takes\n * an `addRef`; both then return a promise and let the slice end normally.\n * So an invocation that calls `awaitIo` keeps the gate until its own\n * checkpoint-end exit and the gate opens there — which is still before any\n * real I/O can complete, so \"a bare timer releases the input gate\" holds.\n * Upstream is the same: `getCriticalSection()` (`io-context.c++:362`) does\n * not touch `currentInputLock`, and `:1214` is the only place that clears\n * it. The difference between the two forms is entirely on the far side —\n * `awaitIo` re-enters through `run(func, criticalSection)` and queues for a\n * fresh lock, `awaitIoWithInputLock` re-enters holding the ref it took.\n * 4. Removal from the stack is by identity, not by popping, because entries do\n * overlap — three deep in the unit tests. One invocation can have several\n * held awaits outstanding (`Promise.all` over two storage reads), and a\n * section's last body slice overlaps the slice that resolves it.\n *\n * Two mechanics were validated against real workerd before this scaffolding\n * landed, and both are easy to get wrong:\n *\n * 1. The ambient \"which lock am I under\" must be a STACK of invocation frames,\n * not a single slot, so that `current` always names the running invocation.\n * The prototype that found this stated it as \"`awaitIo` splices its frame\n * out and re-pushes the SAME frame on resume\". That is not what happens\n * here and the difference is worth knowing: nothing splices at the point of\n * the await, because a lock already leaves the stack when its slice ends,\n * which is the same event that releases it (`#exit` does both). A\n * resumption then pushes a fresh lock. The prototype needed the splice\n * because its door held one lock for a whole invocation; the identity that\n * has to survive an await here is the critical section, and that is\n * captured at the call rather than looked up later.\n * 2. That ambient is safe here for a reason that does NOT generalise to the\n * §2.3 ambient-field hazard: the gate guarantees pushes and pops nest\n * properly in time, because only one holder chain is inside at once.\n * `_cf_currentSubAgentBridge` has no such guarantee, which is why it is a\n * live bug and this is not.\n *\n * Consequence: no async context is required. Do not add a dependency on\n * decision 8 here without re-running the conformance gate suite first.\n *\n * The invariant a future simplifier has to re-check: a single slot would pass\n * every test in `io-context.test.ts`, and that was established by trying it,\n * not by argument. `current()` is only read from inside a slice, every slice\n * pushes its own lock last, and no second slice can begin while an earlier\n * frame is still waiting for its checkpoint-end exit — that frame is holding\n * the gate. So the top of a stack and the last write to a slot always agree.\n * The stack is still what is here, because a slot would be holding a stale\n * value at every one of the overlaps in (4), and that is unobservable for\n * exactly as long as the invariant holds. Tightening (2) back to the end of the\n * synchronous body breaks it immediately, which is the regime the mechanic was\n * found in.\n *\n * Spec: §1.2, §1.3, §1.5, §1.6, §1.7.1, §1.9, decisions 1, 2, 4 and 13 in\n * docs/decisions.md.\n *\n * `TimeoutManager` is the only part of this file a consumer's own code reaches\n * directly: every host-provided async primitive a\n * Durable Object can await has to route through here, or the continuation after\n * it resumes with an empty invocation stack. `TimeoutManager` below is the\n * timer half; `api/global-scope.ts` and `api/web-socket.ts` are the rest.\n *\n * Not ported, because the substrate has no equivalent to port onto: isolate and\n * async locks (`Worker::Lock`, `jsg::Lock`, `takeAsyncLock`) and everything\n * that exists to enter or leave an isolate — which is precisely the thing this\n * file substitutes for; the limit enforcer and `afterLimitTimeout` (the\n * deadline takes the `Timer` port instead); trace spans, already a recorded\n * divergence documented in §1.12; subrequest channels and HTTP, which are\n * `api/http.ts`'s gating over the substrate's own `fetch`;\n * `IoOwn`/`IoPtr`/`DeleteQueue`, which guard cross-context dereferences that GC\n * makes impossible; hang detection and `registerPendingEvent`, which need the\n * isolate's own idea of pending work; and the thread-local\n * `IoContext::current()` static, whose lock-resolving half the invocation stack\n * replaces — its *identity* half is `currentSlice` below, narrowed to the\n * synchronous slice, with one consumer and no resolver. `EventOutcome` and\n * `RequestObserver` are metrics types with no port, so `waitUntilStatus()`\n * returns the first exception instead.\n */\n\nimport {\n CriticalSection,\n type InputGate,\n Lock,\n type OutputGate,\n} from \"./io-gate\";\n\n/**\n * ← `kj::Timer`, threaded into IoContext upstream.\n *\n * A port with one production implementation would be an invented seam; this\n * one has two (real clock in browser and workerd) plus a fake the conformance\n * suite cannot exist without — the 30-second critical-section deadline and the\n * alarm retry ladder are not assertable on wall-clock time in CI.\n */\nexport interface Timer {\n now(): number;\n /** `kj::Timer::afterDelay`. The signal replaces kj's cancel-by-drop. */\n afterDelay(ms: number, signal?: AbortSignal): Promise<void>;\n}\n\n/**\n * ← the `Worker::Actor` surface reached through an `IoContext`:\n * `a.getInputGate()`, `a.getOutputGate()` and `a.shutdownActorCache()` from\n * `io-context.{h,c++}` itself, plus `assertCanSetAlarm()`, which\n * `api/actor-state.c++:486` reaches through\n * `IoContext::current().getActorOrThrow()`.\n *\n * `Worker::Actor` lives in `io/worker.h`, the same Bazel target as this file, so\n * naming the members its consumers use is upstream's own layering rather than a\n * new seam. Every context here is an actor context — there is no non-actor\n * request in a Durable Object runtime — so upstream's\n * `kj::Maybe<Worker::Actor&>` and every branch that tests it collapse to this\n * being required, and `getActorOrThrow()` cannot actually throw.\n */\nexport interface Actor {\n getInputGate(): InputGate;\n getOutputGate(): OutputGate;\n /** Abort abandons scheduled writes rather than flushing them (§1.6). */\n shutdownActorCache(reason: unknown): void;\n /**\n * ← `Worker::Actor::assertCanSetAlarm()` (`io/worker.c++:4090`). Every branch\n * of it reads the actor's class-instance lifecycle, which `server/` owns, so\n * `api/actor-state.ts`'s obligation is to call it and the container's is to\n * answer it.\n */\n assertCanSetAlarm(): void;\n}\n\n/** ← `static constexpr int64_t max = 3153600000000; // Milliseconds in 100 years`. */\nconst MAX_TIMEOUT_MS = 3_153_600_000_000;\n\n/** ← `afterLimitTimeout(30 * kj::SECONDS)` in `IoContext::blockConcurrencyWhile`. */\nexport const BLOCK_CONCURRENCY_WHILE_TIMEOUT_MS = 30_000;\n\n/** Copied verbatim: users and upstream tests match on it. */\nexport const BLOCK_CONCURRENCY_WHILE_TIMEOUT_MESSAGE =\n \"A call to blockConcurrencyWhile() in a Durable Object waited for too long. \" +\n \"The call was canceled and the Durable Object was reset.\";\n\n/** ← `jsg::annotateBroken(msg, \"broken.inputGateBroken\")`. */\nconst INPUT_GATE_BROKEN_PREFIX = \"broken.inputGateBroken; \";\n\n/**\n * THE check every storage entry point in `api/` makes before touching the\n * database, and the only place this package throws for a missing input lock.\n *\n * Upstream never faces the question. `IoContext::current()` is a thread-local\n * read with a `KJ_REQUIRE` behind it, and it cannot fail for a storage call\n * because isolate entry is the only way into an actor and it always took a\n * lock. We have no isolate hook (see this file's header), so a continuation\n * that resumed from a raw `setTimeout`, a raw `fetch`, or any other promise the\n * runtime does not own comes back with an empty invocation stack, and its next\n * storage call reaches `ActorSqlite` — which is synchronous, touches no gate,\n * and would happily serve it outside any transaction boundary.\n *\n * So this throws, matching the assert. It is deliberately ONE function called\n * from every entry point rather than a check written at each of them: the\n * policy is a design decision that belongs to the design record, and when it\n * changes it has to change in one place rather than across a surface the\n * vendored consumer reaches from hundreds of call sites. There is no lenient\n * mode, no flag, and no implicit acquire — a lost invocation is loud, and a\n * lost transaction boundary is not.\n */\nexport function requireInputLock(ctx: IoContext, op: string): void {\n if (ctx.hasCurrent()) return;\n throw new Error(`${op}: no input lock available in this context${ctx.describeLostLock()}`);\n}\n\n/**\n * A stack for `noteGateUse`, captured where user frames are still on the stack.\n *\n * The invocation stack's own push and pop are scheduler moments — `#runImpl`\n * runs from a gate resumption and `#exit` from a `MessageChannel` callback — so\n * a trace taken there names only runtime internals. The moments that still see\n * the caller are the synchronous entries into the gate machinery: an `awaitIo`\n * call, an `entry` dispatch, a callback's registration. V8's zero-cost async\n * traces extend those with the awaiting chain, which is usually the frame the\n * reader actually wants.\n *\n * The first slice drops the `Error` header (absent on SpiderMonkey) and the two\n * runtime frames: this helper and the gate entry point that called it.\n */\nexport function captureGateStack(): string | undefined {\n const stack = new Error().stack;\n if (stack === undefined) return undefined;\n const frames = stack.split(\"\\n\");\n const trimmed = frames.slice(frames[0]?.startsWith(\"Error\") ? 3 : 2).join(\"\\n\");\n return trimmed === \"\" ? undefined : trimmed;\n}\n\n/**\n * The end of the microtask checkpoint — the moment `runImpl`'s `KJ_DEFER` fires.\n *\n * Upstream drains the whole checkpoint INSIDE the isolate run: the defer calls\n * `js.runMicrotasks()` and only the outer defer in `runInContextScope` then\n * clears `currentInputLock`. So a continuation that awaits nothing but\n * already-resolved promises stays under the same lock, and a continuation that\n * waits on real I/O does not. In JS the only observable end of a microtask drain\n * is the next macrotask, so that is where a lock leaves the invocation stack.\n *\n * Measured, because the obvious alternatives are wrong in ways nothing catches:\n * releasing synchronously when the invoked function returns, or on the next\n * microtask, both hand the lock back BEFORE the code awaiting the I/O resumes as\n * soon as one promise sits between the two — one `async` wrapper, a `.then`, a\n * `Promise.all` — and `actor-state.ts` is exactly such a wrapper. The gate would\n * then open in the middle of a storage await with no test failing, which is the\n * silent loss of atomicity §1.7.1 names.\n *\n * `MessageChannel` and not `setTimeout`, decided by benchmark rather than by\n * argument, because the two are three orders of magnitude apart on the shapes\n * that wait for a release. A chain of these schedules each hand-off from inside\n * the previous one's callback, so a `setTimeout` chain's nesting level climbs\n * past five and stays there, where browsers clamp it to 4ms. Median per hand-off\n * over 50 chained hops:\n *\n * | | setTimeout | MessageChannel |\n * | node | 1.273 ms | 0.018 ms |\n * | chromium page | 4.96 ms | 0.024 ms |\n * | chromium Worker | 5.542 ms | 0.022 ms |\n *\n * Only two shapes pay it: an `awaitIo` chain, which has to re-acquire the lock\n * its slice gave up, and a queue of events waiting on the holder. A chain of\n * held storage awaits does not — the lock passes from `addRef` to `addRef` and\n * the release is off the critical path. So 50 sequential facet RPCs cost 277ms\n * of pure clamp in a Worker under `setTimeout` and 1.1ms under this.\n *\n * One channel per BATCH, and a batch is an explicit array: `atCheckpointEnd`\n * pushes onto `pendingCheckpointEnds` and only the push that finds it empty opens\n * a channel. Ordering then comes from the array, which the language guarantees,\n * rather than from delivery order across separate channels, which no\n * specification does. That mattered because the storage engine below depends on\n * a commit scheduled inside a slice running before the release scheduled at the\n * end of that slice; separate channels do deliver in post order in Node,\n * measured across 200 of them including one scheduled from inside another's\n * callback, but a browser that chose otherwise would open the gate onto a\n * transaction a previous event left open, and nothing would say so.\n *\n * A callback scheduled DURING a drain lands in the next batch, which is the\n * semantics to want: one hand-off is one checkpoint end, and a slice that begins\n * inside this drain gets its own. The drain runs every callback even if one\n * throws, and rethrows the first exception afterwards, because abandoning the\n * rest of a batch is how a gate wedges with nothing to see.\n *\n * A long-lived shared port was rejected for the reason it always is: it has to be\n * closed on abort and `unref`'d so it cannot hold a test runner's event loop\n * open, and an `unref`'d port can drop a release at exit — a wedged gate. A\n * channel that lives for exactly one message cannot. Batching gets most of what\n * a shared port was worth anyway: a slice that schedules a commit and a release\n * now allocates one channel where it used to allocate two.\n *\n * **Exported because `kj::evalLater()` is this same point.** `ActorSqlite` opens\n * its implicit transaction on the first write and commits it \"on the next turn of\n * the event loop\" (`actor-sqlite.c++:352-357`); upstream's next turn is after the\n * isolate run, which is after `js.runMicrotasks()`, which is after\n * `currentInputLock` is cleared. Upstream's two boundaries are one boundary, and\n * they stay one here only if the commit rides the same primitive as the release.\n * Two consequences the storage engine depends on, both properties of this\n * function rather than of `ActorSqlite`:\n *\n * 1. **Everything that holds the input lock across an await is a microtask\n * chain.** `awaitIoWithInputLock` resumes through `#awaitIoImpl`'s `.then`\n * into `run(func, lock)`, which never waits on the gate. So a whole run of\n * held storage awaits finishes before the next hand-off and its writes are\n * one transaction (§1.7.1 row 1).\n * 2. **Everything that releases it needs at least one hand-off.** `awaitIo`\n * resumes through `gate.wait()`, which cannot resolve until `#exit` runs\n * here. So a timer or outbound await puts the commit between the two writes\n * (§1.7.1 row 2).\n *\n */\nexport function atCheckpointEnd(run: () => void): void {\n pendingCheckpointEnds.push(run);\n if (pendingCheckpointEnds.length > 1) return;\n\n const channel = new MessageChannel();\n // Assigning `onmessage` starts the port; `addEventListener` would need `start()`.\n channel.port1.onmessage = () => {\n channel.port1.close();\n channel.port2.close();\n // A snapshot, so a callback scheduled by one of these belongs to the next batch — and, since\n // the queue is empty while the batch runs, that push opens the next channel by itself.\n const batch = pendingCheckpointEnds.splice(0);\n let failure: { readonly exception: unknown } | undefined;\n for (const callback of batch) {\n try {\n callback();\n } catch (exception) {\n failure ??= { exception };\n }\n }\n if (failure !== undefined) throw failure.exception;\n };\n channel.port2.postMessage(0);\n}\n\n/** The current batch. Its order is the hand-off order every consumer below relies on. */\nconst pendingCheckpointEnds: (() => void)[] = [];\n\n// =======================================================================================\n// `IoContext::current()`, narrowed to what JS can express\n\n/**\n * ← `static thread_local IoContext* threadLocalRequest` (`io-context.c++:25`).\n *\n * **This is NOT an async context and must not become one.** It is set on entry\n * to a slice's SYNCHRONOUS body and restored the instant that body returns —\n * which for an `async` function is its first `await`. It propagates through\n * exactly nothing. The package's \"no async context is required\" property\n * (README, Part 4 mechanic 1) is undisturbed: nothing resolves a lock through\n * this, and deleting it would change no gate behaviour.\n *\n * Upstream's scope is wider and cannot be matched. `runInContextScope` saves the\n * previous context, installs itself, and restores at the end of the isolate run\n * — which drains the whole microtask checkpoint synchronously, so upstream's\n * `current()` covers a slice's continuations too. JS cannot drain a checkpoint\n * synchronously, so covering continuations here would mean holding the value\n * until `atCheckpointEnd`, and by then a SECOND actor's body can have run and\n * left. That is not a hazard upstream has: two actors' slices genuinely overlap\n * in this window (§1.10 gives every facet its own gates, so nothing serialises\n * a parent against its child), and the value would be wrong with nothing to say\n * so.\n *\n * So the port keeps only the half that is exact, and the one consumer is a\n * tripwire that refuses on mismatch and stays quiet on `undefined` — never a\n * resolver. See `requireOwnSlice` in `api/global-scope.ts`.\n */\nlet currentSlice: IoContext | undefined;\n\n/** ← `IoContext::tryCurrent()` (`io-context.c++:1416-1422`), over the narrowed scope above. */\nexport function tryCurrentSlice(): IoContext | undefined {\n return currentSlice;\n}\n\n/**\n * ← the `SuppressIoContextScope` constructor's `threadLocalRequest = this` half\n * (`io-context.c++:1208`), as a function rather than an assignment in `#runImpl`.\n *\n * A function because `currentSlice = this` reads to a linter as a `this` alias — the\n * ES5 `var self = this` habit — when it is the opposite: publishing the running\n * context to a module scope, which is exactly what the C++ does to a thread local.\n */\nfunction enterSlice(context: IoContext | undefined): void {\n currentSlice = context;\n}\n\n/** ← `kj::OneOf<T, kj::Exception>`, the result of `promiseForExceptionOrT()`. */\ntype Outcome<T> =\n | { readonly ok: true; readonly value: T }\n | { readonly ok: false; readonly exception: unknown };\n\n/** ← `promiseForExceptionOrT()`: merge the rejection into the value so it survives the hop. */\nfunction promiseForExceptionOrT<T>(promise: Promise<T>): Promise<Outcome<T>> {\n return promise.then(\n (value): Outcome<T> => ({ ok: true, value }),\n (exception: unknown): Outcome<T> => ({ ok: false, exception }),\n );\n}\n\n/** ← `IdentityFunc<T>`. */\nfunction identity<T>(value: T): T {\n return value;\n}\n\n/**\n * ← the `if (!msg.startsWith(\"broken.\"))` guard in `blockConcurrencyWhile`'s error\n * handler. Upstream rewrites the exception's description in place, so this does\n * too. `jsg`'s exception tunnelling — the `jsg.Error:` / `remote.` prefixes\n * `annotateBroken()` also produces — has no port here, so only the brokenness tag\n * it exists to carry survives.\n */\nfunction annotateInputGateBroken(exception: unknown): void {\n if (!(exception instanceof Error)) return;\n if (exception.message.startsWith(\"broken.\") || exception.message.startsWith(\"remote.broken.\")) {\n return;\n }\n exception.message = INPUT_GATE_BROKEN_PREFIX + exception.message;\n}\n\n/**\n * ← `jsg::isExceptionFromInputGateBroken` (`jsg/exception.c++:168-172`):\n * \"annotateBroken() produces 'broken.inputGateBroken; {message}', optionally\n * prefixed with 'remote.' when crossing RPC boundaries. Strip the remote prefix\n * first, then check the tag.\"\n *\n * Its writer is `annotateInputGateBroken` directly above, which is why the two\n * live together rather than the reader moving to its consumer: `jsg/` has no\n * module here, and a prefix known in two places is a prefix that drifts.\n */\nexport function isExceptionFromInputGateBroken(exception: unknown): boolean {\n if (!(exception instanceof Error)) return false;\n let description = exception.message;\n // \"there are cases where we return a tunneled error through multiple workers, so let's be\n // paranoid and allow for multiple 'remote.' prefixes\" (`jsg/exception.c++:52-57`).\n while (description.startsWith(REMOTE_EXCEPTION_PREFIX)) {\n description = description.slice(REMOTE_EXCEPTION_PREFIX.length);\n }\n return description.startsWith(INPUT_GATE_BROKEN_PREFIX);\n}\n\n/** ← `ERROR_REMOTE_PREFIX` (`jsg/exception.h`). */\nconst REMOTE_EXCEPTION_PREFIX = \"remote.\";\n\n/**\n * ← `jsg::EXCEPTION_IS_USER_ERROR` (`jsg/exception.h:160`), a\n * `kj::Exception::DetailTypeId` attached to an arbitrary exception rather than a\n * type of exception.\n *\n * A symbol-keyed property is the closest JS has: it rides any thrown object,\n * survives a rethrow, and cannot collide with anything an application writes.\n * `Symbol.for` rather than `Symbol()` so the detail is still legible after the\n * exception crosses a realm — the mistake decision 18 records capnweb making.\n */\nexport const EXCEPTION_IS_USER_ERROR = Symbol.for(\"workerd.exceptionIsUserError\");\n\n/** ← `error.setDetail(jsg::EXCEPTION_IS_USER_ERROR, kj::heapArray<byte>(0))`. */\nexport function setUserErrorDetail(exception: unknown): void {\n if (typeof exception !== \"object\" || exception === null) return;\n Object.defineProperty(exception, EXCEPTION_IS_USER_ERROR, {\n value: true,\n enumerable: false,\n configurable: true,\n });\n}\n\n/** ← `e.getDetail(jsg::EXCEPTION_IS_USER_ERROR) != kj::none`. */\nexport function hasUserErrorDetail(exception: unknown): boolean {\n if (typeof exception !== \"object\" || exception === null) return false;\n return (exception as Record<symbol, unknown>)[EXCEPTION_IS_USER_ERROR] === true;\n}\n\n// =======================================================================================\n// Timers\n\n/**\n * ← `TimeoutManager::TimeoutParameters` (`io/io-timers.h:71-80`).\n *\n * `callback` is nullable for upstream's stated reason: \"This is a maybe to allow\n * cancel to clear it and free the reference when it is no longer needed.\"\n */\ntype TimeoutParameters = {\n readonly repeat: boolean;\n readonly msDelay: number;\n callback: (() => void) | undefined;\n};\n\n/** ← `IoContext::TimeoutManagerImpl::TimeoutState` (`io-context.c++:133-148`). */\ntype TimeoutState = {\n readonly params: TimeoutParameters;\n isCanceled: boolean;\n /**\n * ← `kj::Maybe<kj::Promise<void>> maybePromise`, whose presence upstream reads\n * as \"this timeout is armed\". kj cancels by dropping the promise; there is no\n * drop here, so the signal that aborts the underlying `Timer.afterDelay` is\n * carried beside it — the same substitution Section 1 made for every kj\n * cancel-by-drop.\n */\n armed: AbortController | undefined;\n};\n\n/**\n * ← `IoContext::TimeoutManagerImpl` (`io-context.c++:40-140`, `:742-880`).\n *\n * **The one mechanic worth reading twice: a timer is NOT `awaitIo`.** Upstream\n * says why in a comment on the very line (`io-context.c++:756-758`): \"the manual\n * use of run() here (including carrying over the critical section) is kind of\n * ugly, but using awaitIo() doesn't work here because we need the ability to\n * cancel the timer, so we don't want to addTask() it, which awaitIo() does\n * implicitly.\" So the shape is `cs = ctx.getCriticalSection()` captured at the\n * call, then `ctx.run(callback, cs)` when it fires. The captured section is what\n * makes a timer armed inside `blockConcurrencyWhile` run INSIDE that section\n * rather than queueing on the root gate behind it.\n *\n * **Substrate divergence: one `Timer.afterDelay` per timeout, where upstream\n * keeps a sorted `timeoutTimes` map and a single `timerTask` for the nearest.**\n * That structure exists because `kj::TimerChannel::atTime` supports one pending\n * wait, so upstream has to multiplex; `Timer.afterDelay` takes as many\n * concurrent waits as are asked for. The observable properties it produced —\n * timers fire in deadline order, ties broken by arming order — are the ones both\n * lane timers already have, since both are `setTimeout` underneath. Nothing else\n * in `resetTimerTask` is observable, so nothing else is ported.\n *\n * Not ported: `TimeoutId::Generator` and its cross-`ServiceWorkerGlobalScope`\n * assertion (`io-context.c++:52-60`), which exists to catch an IoContext being\n * current for a different V8 context — a confusion with no shape here, since a\n * timeout id is minted by the manager that owns it; `registerPendingEvent`,\n * which needs the isolate's own idea of pending work; and `getNextTimeout`,\n * whose only caller is the limit enforcer.\n */\nclass TimeoutManager {\n readonly #timer: Timer;\n readonly #timeouts = new Map<number, TimeoutState>();\n #nextId = 1;\n\n constructor(timer: Timer) {\n this.#timer = timer;\n }\n\n /** ← `TimeoutManagerImpl::setTimeout` (`io-context.c++:51-67`). */\n setTimeout(ctx: IoContext, params: TimeoutParameters): number {\n const id = this.#nextId++;\n const state: TimeoutState = { params, isCanceled: false, armed: undefined };\n this.#timeouts.set(id, state);\n this.#arm(ctx, id, state);\n return id;\n }\n\n /** ← `TimeoutManagerImpl::clearTimeout` (`io-context.c++:874-883`). */\n clearTimeout(id: number): void {\n const state = this.#timeouts.get(id);\n // \"We can't find this timeout, thus we act as if it was already canceled.\"\n if (state === undefined) return;\n this.#cancel(id, state);\n }\n\n /** ← `TimeoutManagerImpl::getTimeoutCount` (`io-context.c++:71-73`). */\n getTimeoutCount(): number {\n return this.#timeouts.size;\n }\n\n /** ← `TimeoutManagerImpl::cancelAll` (`io-context.c++:83-87`). */\n cancelAll(): void {\n for (const [id, state] of [...this.#timeouts]) this.#cancel(id, state);\n }\n\n /** ← `TimeoutState::cancel` — clear the flag, drop the callback reference, disarm. */\n #cancel(id: number, state: TimeoutState): void {\n state.isCanceled = true;\n state.params.callback = undefined;\n state.armed?.abort();\n state.armed = undefined;\n this.#timeouts.delete(id);\n }\n\n /** ← `TimeoutManagerImpl::setTimeoutImpl` (`io-context.c++:742-853`). */\n #arm(ctx: IoContext, id: number, state: TimeoutState): void {\n // Captured HERE, at the arming call, exactly as upstream captures it into the `.then` lambda.\n // Reading it when the timer fires would give a new external event the running section and\n // `blockConcurrencyWhile` would silently block nothing (Part 4, mechanic 2).\n const criticalSection = ctx.getCriticalSection();\n const wake = new AbortController();\n state.armed = wake;\n\n const fired = this.#timer.afterDelay(state.params.msDelay, wake.signal).then(\n async () => {\n if (state.isCanceled) return;\n state.armed = undefined;\n await this.#fire(ctx, id, state, criticalSection);\n },\n (exception: unknown) => {\n // kj cancels by dropping the promise, which cannot report anything; Section 1 records\n // that the substitution turns every cancel-by-drop into an `AbortSignal` whose waiter\n // rejects with `CanceledError`. A wake THIS manager aborted is that cancellation and not\n // a failure. Anything else is the timer port's and is reported, so a substrate that\n // fails to keep time cannot look like a timer nobody armed.\n if (!state.isCanceled) throw exception;\n },\n );\n\n // ← `context.addWaitUntil(kj::mv(paf.promise))` (`io-context.c++:845-851`): \"Add a wait-until\n // task which resolves when this timer completes. This ensures that `IncomingRequest::drain()`\n // waits until all timers finish.\"\n //\n // Divergence, and it is the fail-closed direction. Upstream additionally swallows the chain's\n // rejection outright (`[](kj::Exception&&) {}`, `io-context.c++:817`) because a JS throw from\n // the callback has already reached the isolate's uncaught-exception path. There is no such path\n // here, so a swallow would lose it entirely; routing the rejection through the waitUntil set\n // records it in `waitUntilStatus()` — the context's existing failure channel — instead of\n // inventing a second one.\n ctx.addWaitUntil(fired);\n }\n\n /** ← the body of the `.then` at `io-context.c++:759-816`, which is one `context.run`. */\n async #fire(\n ctx: IoContext,\n id: number,\n state: TimeoutState,\n criticalSection: CriticalSection | undefined,\n ): Promise<void> {\n await ctx.run(() => {\n // \"We've been canceled before running. Nothing more to do.\"\n if (state.isCanceled) return;\n const callback = state.params.callback;\n if (callback === undefined) return;\n\n // ← \"First, move our timeout promise to the task set so it's safe to call clearInterval()\n // inside the user's callback.\" A non-repeating timeout is done with its entry either way; a\n // repeating one keeps it so `clearInterval` inside the callback still finds it.\n if (!state.params.repeat) {\n state.params.callback = undefined;\n this.#timeouts.delete(id);\n }\n\n // ← the `KJ_DEFER(unwindDetector.catchExceptionsIfUnwinding(...))`: \"The user's callback\n // might throw, but we need to at least attempt to reschedule interval callbacks even if\n // they throw.\"\n try {\n callback();\n } finally {\n if (state.params.repeat && !state.isCanceled) this.#arm(ctx, id, state);\n }\n }, criticalSection);\n }\n}\n\n/**\n * A set of background promises, with the one behaviour `kj::TaskSet` adds over an\n * array: a failing task reports to `taskFailed` instead of becoming an unhandled\n * rejection, and the set can be waited on until empty.\n */\nclass TaskSet {\n readonly #tasks = new Set<Promise<void>>();\n readonly #taskFailed: (exception: unknown) => void;\n\n constructor(taskFailed: (exception: unknown) => void) {\n this.#taskFailed = taskFailed;\n }\n\n add(promise: Promise<void>): void {\n const task = promise.then(\n () => {\n this.#tasks.delete(task);\n },\n (exception: unknown) => {\n this.#tasks.delete(task);\n this.#taskFailed(exception);\n },\n );\n this.#tasks.add(task);\n }\n\n /** ← `kj::TaskSet::onEmpty()`. Re-checks, since a task can add another. */\n async onEmpty(): Promise<void> {\n while (this.#tasks.size > 0) {\n await Promise.all([...this.#tasks]);\n }\n }\n}\n\n// =======================================================================================\n// IoContext\n\nexport class IoContext {\n readonly #actor: Actor;\n readonly #timer: Timer;\n\n /**\n * ← `kj::Maybe<InputGate::Lock> currentInputLock`, made a stack.\n *\n * The top entry is the lock of the slice that is running right now. Entries are\n * removed by identity rather than popped, so `#exit` never depends on the order\n * the overlapping slices happen to leave in.\n */\n readonly #currentInputLocks: Lock[] = [];\n\n /**\n * Where this context's gate was last deliberately engaged, for\n * `describeLostLock`. One slot, overwritten on every engagement — the gate\n * serialises slices, so the latest note is the best available ancestor of\n * whatever continuation is running lockless now.\n */\n #lastGateUse: { readonly what: string; readonly stack: string | undefined; readonly at: number } | undefined;\n #transformGateUses = 0;\n #transformGateStack: string | undefined;\n\n #abortException: { readonly exception: unknown } | undefined;\n readonly #abortPromise: Promise<never>;\n readonly #rejectAbort: (exception: unknown) => void;\n\n /**\n * Two sets, as upstream: `abortWhen()` always uses `tasks` so that a monitor\n * which never completes cannot hold up a drain, while `addTask()` in an actor\n * is a waitUntil task.\n */\n readonly #tasks: TaskSet;\n readonly #waitUntilTasks: TaskSet;\n #addTaskCounter = 0;\n #waitUntilStatus: { readonly exception: unknown } | undefined;\n\n /** ← `kj::Own<TimeoutManager> timeoutManager` (`io-context.h:1043`). */\n readonly #timeouts: TimeoutManager;\n\n constructor(actor: Actor, timer: Timer) {\n this.#actor = actor;\n this.#timer = timer;\n this.#timeouts = new TimeoutManager(timer);\n\n const { promise, reject } = Promise.withResolvers<never>();\n this.#abortPromise = promise;\n this.#rejectAbort = reject;\n // kj's ForkedPromise just holds the exception until someone adds a branch.\n void promise.catch(() => {});\n\n this.#tasks = new TaskSet((exception) => this.#taskFailed(exception));\n this.#waitUntilTasks = new TaskSet((exception) => this.#taskFailed(exception));\n\n // Arrange to complain if the input gate is broken, which indicates a critical section\n // failed and the actor can no longer be used.\n this.abortWhen(actor.getInputGate().onBroken());\n\n // Also complain if the output gate is broken, which indicates a critical storage failure\n // that means we cannot continue execution.\n this.abortWhen(actor.getOutputGate().onBroken());\n }\n\n // -----------------------------------------------------------------\n // The ambient lock\n\n /**\n * Get the current input lock. Throws an exception if no input lock is held (e.g. because\n * this is not an actor request).\n *\n * ← `KJ_ASSERT_NONNULL(currentInputLock, ...).addRef()`. The `addRef` IS the §1.2\n * distinction: it is the only way to hold the gate past the end of this slice.\n */\n getInputLock(): Lock {\n return this.#requireCurrent().addRef();\n }\n\n /** Get the current CriticalSection, if there is one, or returns null if not. */\n getCriticalSection(): CriticalSection | undefined {\n return this.#currentInputLocks.at(-1)?.getCriticalSection();\n }\n\n /** Is a gated slice running? The question `IoContext::hasCurrent()` answers upstream. */\n hasCurrent(): boolean {\n return this.#currentInputLocks.length > 0;\n }\n\n /**\n * ← `IoContext::isCurrent()` (`io-context.c++:1428-1430`), over the narrowed\n * scope `currentSlice` documents: true only while a synchronous body of THIS\n * context is on the JS stack.\n *\n * Distinct from `hasCurrent()` above, which asks whether this context holds a\n * lock at all — true throughout an outstanding held await, and true for a\n * parent whose slice is awaiting a facet while the facet's body runs. This one\n * is the question a shared global has to answer: is the code calling me this\n * actor's?\n */\n isCurrentSlice(): boolean {\n return currentSlice === this;\n }\n\n /**\n * Record that user code just engaged this context's gate — an `awaitIo`, an\n * `entry` dispatch, a re-entry callback firing. No upstream analogue, because\n * upstream cannot lose the lock; here a continuation that awaits a promise\n * the runtime does not own comes back lockless, the throw lands at the next\n * storage call three layers later, and the gap between \"where the code last\n * verifiably ran gated\" and the throw site is exactly where the foreign await\n * hides. This is that first coordinate. Always on: the capture rides calls\n * that already allocate promise machinery, and a stack costs microseconds\n * against the diagnosis it replaces.\n */\n noteGateUse(what: string, stack: string | undefined): void {\n this.#lastGateUse = { what, stack, at: this.now() };\n }\n\n /**\n * The suffix `requireInputLock` appends when the invocation stack is empty:\n * where this context's gate was last engaged, and how long before the throw.\n *\n * \"Last engaged\" is the honest claim, not \"this continuation's ancestor\" —\n * once the offending chain went lockless the gate reopened, so another slice\n * may have run in between and be the note this reports. In practice the loss\n * is discovered within the same event storm and the note is the parent; when\n * it is not, an engagement of this actor moments earlier is still the right\n * neighbourhood to search.\n */\n describeLostLock(): string {\n const use = this.#lastGateUse;\n if (use === undefined) {\n return \" (this context has never held its gate: the call arrived from outside any actor invocation)\";\n }\n const age = Math.round(this.now() - use.at);\n const stackSuffix = use.stack === undefined ? \"\" : `, at:\\n${use.stack}`;\n return ` (an await after the last gated point resumed from a promise the runtime does not own; the gate was last engaged by ${use.what} ${age}ms before this call${stackSuffix})`;\n }\n\n /**\n * ← `IoContext::getActorOrThrow()`. Upstream's throws when the request is not\n * an actor request; there is no such request here, so it is a plain accessor.\n */\n getActorOrThrow(): Actor {\n return this.#actor;\n }\n\n /** ← `IoContext::now()` (`io-context.h:703`), which reads the same timer. */\n now(): number {\n return this.#timer.now();\n }\n\n // -----------------------------------------------------------------\n // Timers\n\n /**\n * ← `IoContext::setTimeoutImpl` (`io-context.c++:885-899`), clamp included.\n *\n * The generator parameter is gone with `TimeoutId::Generator` — see\n * `TimeoutManager`'s header — so the signature is upstream's minus that one\n * argument.\n */\n setTimeoutImpl(repeat: boolean, callback: () => void, msDelay: number): number {\n // \"Clamp the range on timers to [0, 3153600000000] (inclusive). The specs do not indicate a\n // clear maximum range for setTimeout/setInterval so the limit here is fairly arbitrary. 100\n // years max should be plenty safe.\"\n const delay =\n msDelay <= 0 || Number.isNaN(msDelay)\n ? 0\n : msDelay >= MAX_TIMEOUT_MS\n ? MAX_TIMEOUT_MS\n : Math.trunc(msDelay);\n return this.#timeouts.setTimeout(this, { repeat, msDelay: delay, callback });\n }\n\n /** ← `IoContext::clearTimeoutImpl` (`io-context.c++:901-903`). */\n clearTimeoutImpl(id: number): void {\n this.#timeouts.clearTimeout(id);\n }\n\n /** ← `IoContext::getTimeoutCount` (`io-context.c++:905-907`). */\n getTimeoutCount(): number {\n return this.#timeouts.getTimeoutCount();\n }\n\n // -----------------------------------------------------------------\n // The output gate\n\n /**\n * Wait until all outstanding output locks have been unlocked. Does not wait for future\n * output locks, even if they are created before past locks are unlocked.\n */\n waitForOutputLocks(): Promise<void> {\n return this.#actor.getOutputGate().wait();\n }\n\n /**\n * Check if the output gate is currently broken. This indicates that there was a problem\n * with committing storage writes.\n */\n isOutputGateBroken(): boolean {\n return this.#actor.getOutputGate().isBroken();\n }\n\n /** Lock output until the given promise completes. */\n lockOutputWhile<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {\n return this.#actor.getOutputGate().lockWhile(promise, signal);\n }\n\n // -----------------------------------------------------------------\n // Abort\n\n /**\n * Rejects if and when the context should be aborted, e.g. because a gate broke. This\n * promise never resolves, only rejects.\n */\n onAbort(): Promise<never> {\n return this.#abortPromise;\n }\n\n /** Force context abort now. */\n abort(exception: unknown): void {\n if (this.#abortException !== undefined) {\n return;\n }\n this.#abortException = { exception };\n\n // Stop the ActorCache from flushing any scheduled write operations to prevent any\n // unnecessary or unintentional async work.\n this.#actor.shutdownActorCache(exception);\n\n // ← `timeoutManager->cancelAll()` in `~IoContext_IncomingRequest`, plus the\n // `context.abortException == kj::none` guard that stops `setTimeoutImpl` rescheduling into an\n // aborted context (`io-context.c++:832`). Upstream reaches both through destruction; with no\n // destructors, abort is the one event that stands for it. Without this a pending timer wakes\n // into `run()`, which refuses an aborted context, and the refusal lands in `waitUntilStatus()`\n // as a failure nobody caused.\n this.#timeouts.cancelAll();\n\n this.#rejectAbort(exception);\n }\n\n /**\n * Await the given promise and, if it throws, call `abort()` with the exception. The promise\n * given here should just be a monitoring promise, it should not represent any sort of\n * background work beyond monitoring.\n */\n abortWhen(promise: Promise<unknown>): void {\n if (this.#abortException === undefined) {\n this.#tasks.add(\n promise.then(\n () => {},\n (exception: unknown) => {\n this.abort(exception);\n },\n ),\n );\n }\n }\n\n // -----------------------------------------------------------------\n // Task scheduling\n\n /**\n * Arrange for the given promise to execute as part of this request.\n *\n * \"In Actors, we treat all tasks as wait-until tasks, because it's perfectly legit to start\n * a task under one request and then expect some other request to handle it later.\" Every\n * context here is an actor context, so that branch is the only branch.\n */\n addTask(promise: Promise<void>): void {\n ++this.#addTaskCounter;\n this.addWaitUntil(promise);\n }\n\n /**\n * Indicates that the script has requested that it stay active until the given promise\n * resolves. `drainWaitUntil()` waits until all such promises have completed. Touches\n * neither gate (§1.9).\n */\n addWaitUntil(promise: Promise<void>): void {\n this.#waitUntilTasks.add(promise);\n }\n\n /** Returns the number of times addTask() has been called (even if the tasks have completed). */\n taskCount(): number {\n return this.#addTaskCounter;\n }\n\n /**\n * The first exception a background task failed with, if any.\n *\n * ← `waitUntilStatus()`, which returns an `EventOutcome` derived from the exception by\n * `RequestObserver`. There is no observer here, so the exception itself is the status —\n * and keeping it is what stops a failed background task from being swallowed, since\n * upstream's other half of `taskFailed()` is a log this package has no port for.\n */\n waitUntilStatus(): unknown {\n return this.#waitUntilStatus?.exception;\n }\n\n /**\n * ← `IncomingRequest::drain()`, actor branch. \"For actors, all promises are canceled on\n * actor shutdown, not on a fixed timeout, because work doesn't necessarily happen on a\n * per-request basis in actors.\"\n */\n async drainWaitUntil(): Promise<void> {\n await Promise.race([this.#waitUntilTasks.onEmpty(), this.#abortPromise.catch(() => {})]);\n }\n\n // -----------------------------------------------------------------\n // Entry\n\n /**\n * Run the given callback within this context, holding an input lock.\n *\n * ← the two `IoContext::run()` overloads: given a CriticalSection it waits on that, given\n * an already-held Lock it runs under it, and given neither it takes a fresh lock from the\n * gate. The third case is what a new external event does, and it is the reason inheritance\n * cannot be read from gate state — see `makeReentryCallback`.\n */\n async run<T>(\n func: (lock: Lock) => T | PromiseLike<T>,\n ilOrCs?: Lock | CriticalSection,\n ): Promise<T> {\n // Before we try running anything, let's make sure our IoContext hasn't been aborted. If it\n // has been aborted, there's likely not an active request so later operations will fail\n // anyway.\n const aborted = this.#abortException;\n if (aborted !== undefined) {\n throw aborted.exception;\n }\n\n let lock: Lock;\n if (ilOrCs === undefined) {\n lock = await this.#actor.getInputGate().wait();\n } else if (ilOrCs instanceof CriticalSection) {\n lock = await ilOrCs.wait();\n } else {\n lock = ilOrCs;\n }\n\n return await this.#runImpl(func, lock);\n }\n\n /**\n * Make a function which, when called, re-enters this IoContext to run some code.\n *\n * Upstream, on why the critical section travels with the callback at all:\n *\n * > \"What if the call was made within blockConcurrencyWhile()? The callback will be blocked\n * > until the critical section ends, which could lead to deadlock if the critical section\n * > code is waiting on it? ... The callback is allowed to run within the critical section\n * > (blockConcurrencyWhile()) from which it was called.\"\n *\n * The section is read here, at the point of capture, and never on invocation: a new\n * external event that inherited the running section would skip the queue and\n * `blockConcurrencyWhile` would silently block nothing (Part 4, mechanic 2).\n *\n * The returned function can be called multiple times.\n *\n * It does not route through `io-gate.ts`'s `makeReentryCallback`, which is the same idea\n * expressed at the gate. Upstream's `IoContext::makeReentryCallback` is literally\n * `ctx.run(func, cs)`, and going through the gate helper instead would take a lock this\n * file then has to make current a second time. The gate copy stays: it is the shape a\n * consumer holding only a gate needs, and Section 1's tests cover it.\n */\n makeReentryCallback<Args extends unknown[], Result>(\n func: (lock: Lock, ...args: Args) => Result | PromiseLike<Result>,\n ): (...args: Args) => Promise<Result> {\n // A reentry callback is meant for *re-*entry, so should only be created while already\n // inside the IoContext. Initial entry should just use run().\n this.#requireCurrent();\n const criticalSection = this.getCriticalSection();\n // Captured once, here, because the fire is a scheduler moment with no user\n // frames — the registration site is the trace a reader can act on.\n const registrationStack = captureGateStack();\n\n return async (...args: Args): Promise<Result> => {\n this.noteGateUse(\"a re-entry callback registered at the site below\", registrationStack);\n const call = this.run((lock) => func(lock, ...args), criticalSection);\n\n // ← the `addTask()` + `registerPendingEvent()` pair, which keeps the context live while\n // a callback is outstanding. Upstream scopes that to the callback's lifetime via a\n // destructor; with no destructors the closest live analogue is the in-flight call, which\n // is what should stop `drainWaitUntil()` reporting idle. The outcome is swallowed here\n // only because the caller already receives it.\n this.addTask(\n call.then(\n () => {},\n () => {},\n ),\n );\n\n return await call;\n };\n }\n\n // -----------------------------------------------------------------\n // The two await forms\n\n /**\n * Waits for some background I/O to complete, then executes `func` on the result.\n *\n * The input lock is NOT held across the wait: the resumption re-enters through\n * `run(func, criticalSection)` and takes a fresh lock, so it queues behind whatever\n * arrived in the meantime. This is what makes a Durable Object awaiting another Durable\n * Object fully re-entrant (§1.3).\n *\n * `func` is a parameter rather than something the caller chains, for upstream's reason:\n * chaining \"required returning to the KJ event loop between running func() and running\n * whatever JavaScript code was waiting on it\". Here the equivalent cost is a promise hop\n * outside the lock.\n */\n awaitIo<T>(promise: Promise<T>): Promise<T>;\n awaitIo<T, R>(promise: Promise<T>, func: (value: T) => R | PromiseLike<R>): Promise<R>;\n awaitIo<T, R>(\n promise: Promise<T>,\n func: (value: T) => R | PromiseLike<R> = identity as (value: T) => R,\n ): Promise<R> {\n this.noteGateUse(\"awaitIo\", captureGateStack());\n return this.#awaitIoImpl(promise, this.getCriticalSection(), func);\n }\n\n /** Capture the current critical section for one transformed continuation. */\n makeTransformReentryCallback<Args extends unknown[], Result>(\n func: (...args: Args) => Result | PromiseLike<Result>,\n ): (...args: Args) => Promise<Result> {\n this.#requireCurrent();\n const criticalSection = this.getCriticalSection();\n const shouldSample = this.#transformGateUses++ % 64 === 0 || this.#transformGateStack === undefined;\n if (shouldSample) this.#transformGateStack = captureGateStack();\n this.#lastGateUse = {\n what: shouldSample\n ? \"a transformed await sampled at the site below\"\n : \"a transformed await (stack from the most recent sampled transformed await)\",\n stack: this.#transformGateStack,\n at: this.now(),\n };\n\n return (...args: Args): Promise<Result> => {\n const call = this.run(() => func(...args), criticalSection);\n this.addTask(\n call.then(\n () => {},\n () => {},\n ),\n );\n return call;\n };\n }\n\n /**\n * Waits for the given I/O while holding the input lock, so that all other I/O is blocked\n * from completing in the meantime (unless it is also holding the same input lock).\n *\n * This is the whole of §1.2's asymmetry, and per §1.7.1 it is also the implicit-transaction\n * boundary: the four async storage calls take this form, everything else takes `awaitIo`.\n * Calling it outside a gated slice throws rather than inventing a lock — a lost invocation\n * is loud, a lost transaction boundary is not.\n */\n awaitIoWithInputLock<T>(promise: Promise<T>): Promise<T>;\n awaitIoWithInputLock<T, R>(\n promise: Promise<T>,\n func: (value: T) => R | PromiseLike<R>,\n ): Promise<R>;\n awaitIoWithInputLock<T, R>(\n promise: Promise<T>,\n func: (value: T) => R | PromiseLike<R> = identity as (value: T) => R,\n ): Promise<R> {\n let inputLock: Lock;\n try {\n // The exception is unchanged; only its shape is. A method that returns a promise and\n // sometimes throws synchronously instead escapes the caller's `.catch`, and this is the\n // one call every storage method makes.\n inputLock = this.getInputLock();\n } catch (exception) {\n return Promise.reject(exception);\n }\n this.noteGateUse(\"awaitIoWithInputLock\", captureGateStack());\n return this.#awaitIoImpl(promise, inputLock, func);\n }\n\n // -----------------------------------------------------------------\n // blockConcurrencyWhile\n\n /**\n * Runs `callback` within its own critical section, returning its final result. If\n * `callback` throws, the input lock will break, resetting the actor.\n *\n * Three behaviours live here rather than in `io-gate.ts`, which has no timer, and rather\n * than in `api/actor-state.ts`, whose own `blockConcurrencyWhile` is a one-line forward:\n * the 30-second deadline, the brokenness annotation, and the fact that on failure the\n * returned promise is never settled at all.\n */\n blockConcurrencyWhile<T>(callback: (lock: Lock) => T | PromiseLike<T>): Promise<T> {\n const lock = this.getInputLock();\n this.noteGateUse(\"blockConcurrencyWhile\", captureGateStack());\n const criticalSection = lock.startCriticalSection();\n const { promise: result, resolve } = Promise.withResolvers<T>();\n\n this.addTask(\n (async () => {\n try {\n const value = await this.#runCriticalSection(criticalSection, callback);\n\n // Hand the parent lock back and resolve under it, so the caller's continuation runs\n // before any other input arrives.\n await this.#runImpl(() => {\n resolve(value);\n }, criticalSection.succeeded());\n } catch (exception) {\n // Annotate as broken for periodic metrics. If we already set up a brokenness reason,\n // we shouldn't override it.\n annotateInputGateBroken(exception);\n\n // Note that on failure, no further InputLocks will be obtainable and the actor will\n // shut down, so don't worry about holding a lock until we get back to application\n // code -- we won't! In fact, we don't even bother calling resolver.reject() because\n // it's meaningless at this point.\n criticalSection.failed(exception);\n\n throw exception;\n } finally {\n // ← `~CriticalSection`. A no-op after `succeeded()`; on the failure path it is what\n // hands the parent lock back, since `failed()` does not.\n criticalSection.drop();\n }\n })(),\n );\n\n // ← the destruction of `auto lock` at the end of the upstream scope. Everything above is\n // synchronous, so the end of the setup is the end of the scope.\n lock.release();\n\n return result;\n }\n\n // -----------------------------------------------------------------\n\n /**\n * ← `runImpl()` + `runInContextScope()`: check the lock belongs to this actor, make it the\n * current one, run, and let `KJ_DEFER` clear it.\n *\n * The defer fires after the microtask checkpoint, not when `func` returns — see\n * `atCheckpointEnd`. Everything else about the scope is isolate machinery with no port.\n */\n async #runImpl<T>(func: (lock: Lock) => T | PromiseLike<T>, lock: Lock): Promise<T> {\n if (!lock.isFor(this.#actor.getInputGate())) {\n throw new Error(\"IoContext::runImpl() was given a lock belonging to another actor\");\n }\n\n this.#currentInputLocks.push(lock);\n let result: T | PromiseLike<T>;\n // ← `SuppressIoContextScope previousRequest; threadLocalRequest = this;` (`io-context.c++:1208`)\n // and its restoring destructor. Scoped to the synchronous body only — see `currentSlice`.\n const previousSlice = currentSlice;\n enterSlice(this);\n try {\n result = func(lock);\n } finally {\n enterSlice(previousSlice);\n atCheckpointEnd(() => {\n this.#exit(lock);\n });\n }\n return await result;\n }\n\n /**\n * ← `requireCurrent()`. Upstream asks whether this IoContext is the thread's current one;\n * with no thread-local there is only one question left, and it is the one every caller of\n * `requireCurrent()` actually depends on: is a gated slice running?\n */\n #requireCurrent(): Lock {\n const lock = this.#currentInputLocks.at(-1);\n if (lock === undefined) {\n throw new Error(`no input lock available in this context${this.describeLostLock()}`);\n }\n return lock;\n }\n\n /** ← the far side of `runInContextScope`'s `KJ_DEFER`. */\n #exit(lock: Lock): void {\n const at = this.#currentInputLocks.lastIndexOf(lock);\n if (at < 0) {\n throw new Error(\"IoContext invocation stack lost a lock it was holding\");\n }\n this.#currentInputLocks.splice(at, 1);\n lock.release();\n }\n\n /**\n * ← `awaitIoImpl()`.\n *\n * The KJ-side rejection is merged into the value so a single continuation handles both, the\n * continuation re-enters through `run(func, ilOrCs)`, and the whole thing rides `addTask()`.\n * When `ilOrCs` is a Lock this is `awaitIoWithInputLock` and the gate never opened; when it\n * is a CriticalSection or nothing this is `awaitIo` and the resumption queues for a fresh\n * lock like any other event.\n */\n #awaitIoImpl<T, R>(\n promise: Promise<T>,\n ilOrCs: Lock | CriticalSection | undefined,\n func: (value: T) => R | PromiseLike<R>,\n ): Promise<R> {\n const { promise: result, resolve, reject } = Promise.withResolvers<R>();\n\n this.addTask(\n promiseForExceptionOrT(promise).then(async (outcome) => {\n try {\n await this.run((): void => {\n if (outcome.ok) {\n // `func` runs under the lock, which is the guarantee that makes it a parameter.\n try {\n resolve(func(outcome.value));\n } catch (exception) {\n reject(exception);\n }\n } else {\n reject(outcome.exception);\n }\n }, ilOrCs);\n } catch (exception) {\n // `run()` refuses to re-enter an aborted context, and both of its throws happen\n // before the lock reaches the invocation stack. Upstream would destroy the whole\n // continuation here, releasing the held lock with it; with no destructors it has to\n // be handed back by name. `result` is deliberately left unsettled, as upstream\n // leaves it: the actor is being torn down and `onAbort()` is what reports that.\n if (ilOrCs instanceof Lock) ilOrCs.release();\n throw exception;\n }\n }),\n );\n\n return result;\n }\n\n /**\n * ← the first `.then()` of `blockConcurrencyWhile`: start the section, run the callback\n * under its first nested lock, and race the deadline.\n */\n async #runCriticalSection<T>(\n criticalSection: CriticalSection,\n callback: (lock: Lock) => T | PromiseLike<T>,\n ): Promise<T> {\n const inputLock = await criticalSection.wait();\n\n return await this.#runImpl((lock) => {\n // Remember that this can throw synchronously, and it's important that we catch such\n // throws and call cs->failed().\n const running = callback(lock);\n\n // Arrange to time out if the critical section runs more than 30 seconds, so that objects\n // won't be hung forever if they have a critical section that deadlocks.\n const deadline = new AbortController();\n const timeout = this.#timer\n .afterDelay(BLOCK_CONCURRENCY_WHILE_TIMEOUT_MS, deadline.signal)\n .then((): never => {\n throw new Error(BLOCK_CONCURRENCY_WHILE_TIMEOUT_MESSAGE);\n });\n\n // ← `exclusiveJoin`. The loser cannot be cancelled — a recorded divergence — but the\n // timer half of it can, which is what the signal is for: a section that finishes must\n // not leave a live 30-second timer behind.\n return Promise.race([Promise.resolve(running), timeout]).finally(() => {\n deadline.abort();\n });\n }, inputLock);\n }\n\n /** ← `IoContext::taskFailed()`, minus the logging half, which has no port. */\n #taskFailed(exception: unknown): void {\n if (this.#waitUntilStatus === undefined) {\n this.#waitUntilStatus = { exception };\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,IAAa,gBAAb,cAAmC,MAAM;CACvC,OAAyB;AAC3B;;AAGA,SAAS,oBAAmC;CAC1C,OAAO,IAAI,cAAc,8BAA8B;AACzD;;;;;;;AAQA,SAAS,QAAQ,QAAiC,KAAuB;CACvE,IAAI,WAAW,KAAA,GAAW;CAC1B,IAAI,OAAO,SAAS;EAClB,IAAI;EACJ;CACF;CACA,OAAO,iBAAiB,SAAS,KAAK,EAAE,MAAM,KAAK,CAAC;AACtD;;AAgCA,IAAa,2BAA2C;CACtD,kBAAkB,CAAC;CACnB,oBAAoB,CAAC;CACrB,uBAAuB,CAAC;CACxB,yBAAyB,CAAC;AAC5B;;AAGA,IAAM,SAAN,MAAa;;CAEX;CACA;;CAEA,SAAS;CAET;CACA;CAEA,YACE,MACA,eACA,SACA,QACA;EACA,KAAK,OAAO;EACZ,KAAK,gBAAgB;EACrB,KAAKA,WAAW;EAChB,KAAKC,UAAU;EAEf,KAAK,MAAM,qBAAqB;EAChC,IAAI,eACF,KAAK,gBAAgB,KAAK,IAAI;OAE9B,KAAK,QAAQ,KAAK,IAAI;CAE1B;CAEA,SAAe;EACb,IAAI,CAAC,KAAK,QAAQ;EAClB,KAAK,SAAS;EACd,MAAM,OAAO,KAAK,gBAAgB,KAAK,KAAK,kBAAkB,KAAK,KAAK;EACxE,MAAM,QAAQ,KAAK,QAAQ,IAAI;EAC/B,IAAI,QAAQ,GACV,MAAM,IAAI,MAAM,6DAA6D;EAE/E,KAAK,OAAO,OAAO,CAAC;CACtB;CAEA,QAAQ,MAAkB;EACxB,KAAK,OAAO;EACZ,KAAK,KAAK,MAAM,uBAAuB;EACvC,KAAKD,SAAS,IAAI;CACpB;CAEA,OAAO,WAA0B;EAC/B,KAAK,OAAO;EACZ,KAAK,KAAK,MAAM,uBAAuB;EACvC,KAAKC,QAAQ,SAAS;CACxB;AACF;;;;;;;;;AAUA,IAAa,YAAb,MAAuB;CACrB;;;;;;;;;CAUA,YAAY;CAEZ,UAA6B,CAAC;;;;;CAM9B,kBAAqC,CAAC;;CAGtC;CAEA;CAEA,YAAY,QAAwB,0BAA0B;EAC5D,KAAK,QAAQ;EACb,MAAM,EAAE,SAAS,WAAW,QAAQ,cAAqB;EACzD,KAAKC,iBAAiB;EACtB,KAAK,cAAc;GAAE,MAAM;GAAa;EAAO;EAE/C,QAAa,YAAY,CAAC,CAAC;CAC7B;;CAGA,KAAK,QAAqC;EACxC,IAAI,QAAQ,YAAY,MACtB,OAAO,QAAQ,OAAO,kBAAkB,CAAC;OACpC,IAAI,KAAK,YAAY,SAAS,aACnC,OAAO,QAAQ,OAAO,KAAK,YAAY,SAAS;OAC3C,IAAI,KAAK,cAAc,GAC5B,OAAO,QAAQ,QAAQ,IAAI,KAAK,IAAI,CAAC;OAErC,OAAO,KAAK,iBAAiB,OAAO,MAAM;CAE9C;;;;;CAMA,WAA2B;EACzB,IAAI,KAAK,YAAY,SAAS,aAC5B,OAAO,QAAQ,OAAO,KAAK,YAAY,SAAS;OAEhD,OAAO,KAAKA;CAEhB;;CAGA,iBAAiB,eAAwB,QAAqC;EAC5E,MAAM,EAAE,SAAS,SAAS,WAAW,QAAQ,cAAoB;EACjE,MAAM,SAAS,IAAI,OAAO,MAAM,eAAe,SAAS,MAAM;EAC9D,QAAQ,cAAc;GAGpB,IAAI,CAAC,OAAO,QAAQ;GACpB,OAAO,OAAO,kBAAkB,CAAC;EACnC,CAAC;EACD,OAAO;CACT;CAEA,cAAoB;EAClB,IAAI,gBAAgB,mBAAmB,KAAK,UAAU,cAAc;GAKlE,IAAI,KAAK,gBAAgB,WAAW,KAAK,KAAK,QAAQ,WAAW,KAAK,KAAK,cAAc,GACvF,MAAM,IAAI,MAAM,yEAAyE;GAG3F,KAAK,kBAAkB,CAAC,CAAC,YAAY;GACrC;EACF;EAEA,IAAI,KAAK,cAAc,GACrB,MAAM,IAAI,MAAM,oDAAoD;EAEtE,KAAK;EAGL,IAAI,KAAK,cAAc,GAAG;GACxB,KAAK,MAAM,kBAAkB;GAC7B,MAAM,QAAQ,KAAK,gBAAgB;GACnC,IAAI,UAAU,KAAA,GACZ,MAAM,QAAQ,IAAI,KAAK,IAAI,CAAC;QACvB;IACL,MAAM,SAAS,KAAK,QAAQ;IAC5B,IAAI,WAAW,KAAA,GACb,OAAO,QAAQ,IAAI,KAAK,IAAI,CAAC;GAEjC;EACF;CACF;;CAGA,UAAU,WAA0B;EAElC,KAAK,MAAM,UAAU,CAAC,GAAG,KAAK,eAAe,GAAG,OAAO,OAAO,SAAS;EACvE,KAAK,MAAM,UAAU,CAAC,GAAG,KAAK,OAAO,GAAG,OAAO,OAAO,SAAS;EAC/D,IAAI,KAAK,YAAY,SAAS,aAC5B,KAAK,YAAY,OAAO,SAAS;EAEnC,KAAK,cAAc;GAAE,MAAM;GAAa;EAAU;CACpD;AACF;;AAGA,IAAa,OAAb,MAAa,KAAK;;CAEhB;CAEA,YAAY,MAAiB;EAC3B,KAAKC,QAAQ;EAKb,IAAI,aAAwB;EAC5B,IAAI,gBAAgB,mBAAmB,KAAK,UAAU,cACpD,aAAa,KAAK,kBAAkB;EAGtC,IAAI,EAAE,WAAW,cAAc,GAC7B,WAAW,MAAM,gBAAgB;CAErC;;CAGA,UAAgB;EACd,MAAM,OAAO,KAAKA;EAClB,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,MAAM,oCAAoC;EAEtD,KAAKA,QAAQ,KAAA;EACb,KAAK,YAAY;CACnB;;;;;CAMA,SAAe;EACb,OAAO,IAAI,KAAK,KAAKC,aAAa,CAAC;CACrC;;;;;;;;;;CAWA,uBAAwC;EACtC,OAAO,IAAI,gBAAgB,KAAKA,aAAa,CAAC;CAChD;;CAGA,qBAAkD;EAChD,MAAM,OAAO,KAAKA,aAAa;EAC/B,OAAO,gBAAgB,kBAAkB,OAAO,KAAA;CAClD;CAEA,MAAM,WAA+B;EACnC,IAAI,qBAAqB,iBACvB,MAAM,IAAI,MAAM,qEAAqE;EAGvF,IAAI,MAAM,KAAKA,aAAa;EAC5B,OAAO,eAAe,iBACpB,MAAM,IAAI,kBAAkB;EAE9B,OAAO,QAAQ;CACjB;;CAGA,OAAO,OAAsB;EAC3B,OAAO,KAAKA,aAAa,MAAM,MAAMA,aAAa;CACpD;CAEA,eAA0B;EACxB,MAAM,OAAO,KAAKD;EAClB,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,MAAM,wCAAwC;EAE1D,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;AA6BA,IAAa,kBAAb,MAAa,wBAAwB,UAAU;CAC7C,QAA8B;;;;;CAM9B;;;;;CAMA;CAEA,YAAY,QAAmB;EAG7B,MAAM;EACN,KAAKE,UAAU;CACjB;;;;;;;;;;;CAYA,MAAe,KAAK,QAAqC;EAGvD,IAAI,QAAQ,YAAY,MAAM,MAAM,kBAAkB;EAEtD,SACE,QAAQ,KAAK,OAAb;GACE,KAAK,eAAe;IAClB,KAAK,QAAQ;IAEb,MAAM,SAAS,KAAK,kBAAkB;IACtC,IAAI,OAAO,YAAY,SAAS,aAAa;KAE3C,MAAM,YAAY,OAAO,YAAY;KACrC,KAAK,UAAU,SAAS;KACxB,MAAM;IACR;IAGA,IAAI,OAAO,cAAc,GAAG;KAC1B,KAAK,QAAQ;KACb,KAAKC,cAAc,IAAI,KAAK,MAAM;KAClC;IACF,OAAO;KACL,IAAI;KACJ,IAAI;MACF,OAAO,MAAM,OAAO,iBAAiB,MAAM,MAAM;KACnD,SAAS,WAAW;MAGlB,IAAI,qBAAqB,eAAe,MAAM;MAC9C,KAAK,QAAQ;MACb,KAAK,UAAU,SAAS;MACxB,MAAM;KACR;KACA,KAAK,QAAQ;KACb,KAAKA,cAAc;KACnB;IACF;GACF;GACA,KAAK,gBAIH,MAAM,IAAI,MAAM,yDAAyD;GAC3E,KAAK,WAEH,OAAO,MAAM,MAAM,KAAK,MAAM;GAChC,KAAK,cAIH,OAAO,MAAM,KAAKD,QAAQ,KAAK,MAAM;EACzC;CAEJ;;;;;;;;CASA,YAAkB;EAChB,IAAI,KAAK,UAAU,WACjB,MAAM,IAAI,MAAM,kEAAkE;EAKpF,MAAM,aAAa,KAAK,kBAAkB;EAC1C,KAAK,MAAM,UAAU,KAAK,iBAAiB,OAAO,OAAO;EACzD,WAAW,gBAAgB,KAAK,GAAG,KAAK,eAAe;EACvD,KAAK,gBAAgB,SAAS;EAC9B,KAAK,MAAM,UAAU,KAAK,SAAS,OAAO,OAAO;EACjD,WAAW,QAAQ,KAAK,GAAG,KAAK,OAAO;EACvC,KAAK,QAAQ,SAAS;EACtB,WAAW,aAAa,KAAK;EAC7B,KAAK,YAAY;EAEjB,KAAK,QAAQ;EACb,MAAM,SAAS,KAAKC;EACpB,IAAI,WAAW,KAAA,GACb,MAAM,IAAI,MAAM,kDAAkD;EAEpE,KAAKA,cAAc,KAAA;EACnB,OAAO;CACT;;;;;CAMA,OAAO,WAA0B;EAC/B,IAAI,KAAK,YAAY,SAAS,aAE5B;EAGF,KAAK,UAAU,SAAS;EACxB,IAAI,KAAKD,mBAAmB,iBAC1B,KAAKA,QAAQ,OAAO,SAAS;OAE7B,KAAKA,QAAQ,UAAU,SAAS;CAEpC;;CAGA,OAAa;EACX,QAAQ,KAAK,OAAb;GACE,KAAK,eAEH;GACF,KAAK,gBAEH;GACF,KAAK,WACH,KAAK,uBACH,IAAI,MACF,kUAKF,CACF;EAKJ;EAWA,MAAM,aAAa,KAAKC;EACxB,IAAI,eAAe,KAAA,GAAW;GAC5B,KAAKA,cAAc,KAAA;GACnB,WAAW,QAAQ;EACrB;CACF;;CAGA,oBAA+B;EAG7B,IAAI,SAAS,KAAKD;EAClB,SAAS;GACP,IAAI,EAAE,kBAAkB,kBAAkB,OAAO;GACjD,IAAI,OAAO,UAAU,cAAc,OAAO;GAE1C,SAAS,OAAOA;EAClB;CACF;AACF;;AAoEA,IAAa,4BAA6C;;CAExD,0BAA0B,IAAI,cAAqB,CAAC,CAAC;CACrD,mBAAmB,CAAC;CACpB,qBAAqB,CAAC;CACtB,wBAAwB,CAAC;CACzB,0BAA0B,CAAC;AAC7B;;AAUA,SAAS,2BAAkC;CACzC,uBAAO,IAAI,MAAM,4CAA4C;AAC/D;;;;;;;;;AAUA,IAAa,aAAb,MAAwB;CACtB;CACA,oBAAmC,QAAQ,QAAQ;;CAEnD,eAA4B,EAAE,MAAM,OAAO;CAE3C,YAAY,QAAyB,2BAA2B;EAC9D,KAAKE,SAAS;CAChB;;;;;;CAOA,UAAa,SAAqB,QAAkC;EAClE,MAAM,YAAY,KAAKC,MAAM;EAC7B,MAAM,QAAQ,QAAQ,KAAK,CAAC,SAAS,KAAKD,OAAO,mBAAmB,CAAC,CAAC;EAEtE,KAAKA,OAAO,iBAAiB;EAE7B,OAAO,IAAI,SAAY,SAAS,WAAW;GAIzC,QAAQ,cAAc;IAKpB,IAAI,CAAC,UAAU,UAAU,GAAG;IAC5B,KAAKA,OAAO,mBAAmB;IAC/B,MAAM,YAAY,yBAAyB;IAC3C,KAAKE,WAAW,SAAS;IACzB,UAAU,OAAO,SAAS;IAC1B,OAAO,SAAS;GAClB,CAAC;GAED,MAAW,MACR,UAAU;IAET,IAAI,CAAC,UAAU,UAAU,GAAG;IAC5B,UAAU,QAAQ;IAClB,KAAKF,OAAO,mBAAmB;IAC/B,QAAQ,KAAK;GACf,IACC,cAAuB;IACtB,IAAI,CAAC,UAAU,UAAU,GAAG;IAC5B,KAAKE,WAAW,SAAS;IACzB,UAAU,OAAO,SAAS;IAC1B,KAAKF,OAAO,mBAAmB;IAC/B,OAAO,SAAS;GAClB,CACF;EACF,CAAC;CACH;;;;;CAMA,OAAsB;EACpB,KAAKA,OAAO,sBAAsB;EAClC,OAAO,KAAKG,kBAAkB,WACtB;GACJ,KAAKH,OAAO,wBAAwB;EACtC,IACC,cAAuB;GACtB,KAAKA,OAAO,wBAAwB;GACpC,MAAM;EACR,CACF;CACF;;;;;;;CAQA,WAA2B;EACzB,IAAI,KAAKI,aAAa,SAAS,aAC7B,MAAM,IAAI,MAAM,oCAAoC;EAGtD,IAAI,KAAKA,aAAa,SAAS,aAC7B,OAAO,QAAQ,OAAO,KAAKA,aAAa,SAAS;OAC5C;GACL,MAAM,EAAE,SAAS,WAAW,QAAQ,cAAqB;GACzD,KAAKA,eAAe;IAAE,MAAM;IAAa;GAAO;GAChD,QAAa,YAAY,CAAC,CAAC;GAC3B,OAAO;EACT;CACF;CAEA,WAAoB;EAClB,OAAO,KAAKA,aAAa,SAAS;CACpC;CAEA,QAAuB;EACrB,MAAM,EAAE,SAAS,SAAS,WAAW,QAAQ,cAAoB;EAKjE,KAAKC,cACH,QAAQ,WAAW,CAAC,KAAKF,mBAAmB,OAAO,CAAC,CAAC,CAAC,MAAM,YAAY;GACtE,KAAK,MAAM,UAAU,SACnB,IAAI,OAAO,WAAW,YAAY,MAAM,OAAO;EAEnD,CAAC,CACH;EAEA,IAAI,UAAU;EACd,OAAO;GACL,iBAAiB;GACjB,eAAe;IACb,UAAU;IACV,QAAQ;GACV;GACA,SAAS,cAAc;IACrB,UAAU;IACV,OAAO,SAAS;GAClB;EACF;CACF;CAEA,cAAc,SAA8B;EAC1C,KAAKA,oBAAoB;EAEzB,QAAa,YAAY,CAAC,CAAC;CAC7B;CAEA,WAAW,WAA0B;EAGnC,IAAI,KAAKC,aAAa,SAAS,aAC7B,KAAKA,aAAa,OAAO,SAAS;EAEpC,KAAKA,eAAe;GAAE,MAAM;GAAa;EAAU;CACrD;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACvqBA,IAAM,iBAAiB;;AAGvB,IAAa,qCAAqC;;AAGlD,IAAa,0CACX;;AAIF,IAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;AAuBjC,SAAgB,iBAAiB,KAAgB,IAAkB;CACjE,IAAI,IAAI,WAAW,GAAG;CACtB,MAAM,IAAI,MAAM,GAAG,GAAG,2CAA2C,IAAI,iBAAiB,GAAG;AAC3F;;;;;;;;;;;;;;;AAgBA,SAAgB,mBAAuC;CACrD,MAAM,yBAAQ,IAAI,MAAM,EAAA,CAAE;CAC1B,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,MAAM,SAAS,MAAM,MAAM,IAAI;CAC/B,MAAM,UAAU,OAAO,MAAM,OAAO,EAAE,EAAE,WAAW,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI;CAC9E,OAAO,YAAY,KAAK,KAAA,IAAY;AACtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkFA,SAAgB,gBAAgB,KAAuB;CACrD,sBAAsB,KAAK,GAAG;CAC9B,IAAI,sBAAsB,SAAS,GAAG;CAEtC,MAAM,UAAU,IAAI,eAAe;CAEnC,QAAQ,MAAM,kBAAkB;EAC9B,QAAQ,MAAM,MAAM;EACpB,QAAQ,MAAM,MAAM;EAGpB,MAAM,QAAQ,sBAAsB,OAAO,CAAC;EAC5C,IAAI;EACJ,KAAK,MAAM,YAAY,OACrB,IAAI;GACF,SAAS;EACX,SAAS,WAAW;GAClB,YAAY,EAAE,UAAU;EAC1B;EAEF,IAAI,YAAY,KAAA,GAAW,MAAM,QAAQ;CAC3C;CACA,QAAQ,MAAM,YAAY,CAAC;AAC7B;;AAGA,IAAM,wBAAwC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;AA8B/C,IAAI;;AAGJ,SAAgB,kBAAyC;CACvD,OAAO;AACT;;;;;;;;;AAUA,SAAS,WAAW,SAAsC;CACxD,eAAe;AACjB;;AAQA,SAAS,uBAA0B,SAA0C;CAC3E,OAAO,QAAQ,MACZ,WAAuB;EAAE,IAAI;EAAM;CAAM,KACzC,eAAoC;EAAE,IAAI;EAAO;CAAU,EAC9D;AACF;;AAGA,SAAS,SAAY,OAAa;CAChC,OAAO;AACT;;;;;;;;AASA,SAAS,wBAAwB,WAA0B;CACzD,IAAI,EAAE,qBAAqB,QAAQ;CACnC,IAAI,UAAU,QAAQ,WAAW,SAAS,KAAK,UAAU,QAAQ,WAAW,gBAAgB,GAC1F;CAEF,UAAU,UAAU,2BAA2B,UAAU;AAC3D;;;;;;;;;;;AAYA,SAAgB,+BAA+B,WAA6B;CAC1E,IAAI,EAAE,qBAAqB,QAAQ,OAAO;CAC1C,IAAI,cAAc,UAAU;CAG5B,OAAO,YAAY,WAAW,uBAAuB,GACnD,cAAc,YAAY,MAAM,wBAAwB,MAAM;CAEhE,OAAO,YAAY,WAAW,wBAAwB;AACxD;;AAGA,IAAM,0BAA0B;;;;;;;;;;;AAYhC,IAAa,0BAA0B,OAAO,IAAI,8BAA8B;;AAGhF,SAAgB,mBAAmB,WAA0B;CAC3D,IAAI,OAAO,cAAc,YAAY,cAAc,MAAM;CACzD,OAAO,eAAe,WAAW,yBAAyB;EACxD,OAAO;EACP,YAAY;EACZ,cAAc;CAChB,CAAC;AACH;;AAGA,SAAgB,mBAAmB,WAA6B;CAC9D,IAAI,OAAO,cAAc,YAAY,cAAc,MAAM,OAAO;CAChE,OAAQ,UAAsC,6BAA6B;AAC7E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DA,IAAM,iBAAN,MAAqB;CACnB;CACA,4BAAqB,IAAI,IAA0B;CACnD,UAAU;CAEV,YAAY,OAAc;EACxB,KAAKE,SAAS;CAChB;;CAGA,WAAW,KAAgB,QAAmC;EAC5D,MAAM,KAAK,KAAKE;EAChB,MAAM,QAAsB;GAAE;GAAQ,YAAY;GAAO,OAAO,KAAA;EAAU;EAC1E,KAAKD,UAAU,IAAI,IAAI,KAAK;EAC5B,KAAKE,KAAK,KAAK,IAAI,KAAK;EACxB,OAAO;CACT;;CAGA,aAAa,IAAkB;EAC7B,MAAM,QAAQ,KAAKF,UAAU,IAAI,EAAE;EAEnC,IAAI,UAAU,KAAA,GAAW;EACzB,KAAKG,QAAQ,IAAI,KAAK;CACxB;;CAGA,kBAA0B;EACxB,OAAO,KAAKH,UAAU;CACxB;;CAGA,YAAkB;EAChB,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,GAAG,KAAKA,SAAS,GAAG,KAAKG,QAAQ,IAAI,KAAK;CACvE;;CAGA,QAAQ,IAAY,OAA2B;EAC7C,MAAM,aAAa;EACnB,MAAM,OAAO,WAAW,KAAA;EACxB,MAAM,OAAO,MAAM;EACnB,MAAM,QAAQ,KAAA;EACd,KAAKH,UAAU,OAAO,EAAE;CAC1B;;CAGA,KAAK,KAAgB,IAAY,OAA2B;EAI1D,MAAM,kBAAkB,IAAI,mBAAmB;EAC/C,MAAM,OAAO,IAAI,gBAAgB;EACjC,MAAM,QAAQ;EAEd,MAAM,QAAQ,KAAKD,OAAO,WAAW,MAAM,OAAO,SAAS,KAAK,MAAM,CAAC,CAAC,KACtE,YAAY;GACV,IAAI,MAAM,YAAY;GACtB,MAAM,QAAQ,KAAA;GACd,MAAM,KAAKK,MAAM,KAAK,IAAI,OAAO,eAAe;EAClD,IACC,cAAuB;GAMtB,IAAI,CAAC,MAAM,YAAY,MAAM;EAC/B,CACF;EAYA,IAAI,aAAa,KAAK;CACxB;;CAGA,MAAMA,MACJ,KACA,IACA,OACA,iBACe;EACf,MAAM,IAAI,UAAU;GAElB,IAAI,MAAM,YAAY;GACtB,MAAM,WAAW,MAAM,OAAO;GAC9B,IAAI,aAAa,KAAA,GAAW;GAK5B,IAAI,CAAC,MAAM,OAAO,QAAQ;IACxB,MAAM,OAAO,WAAW,KAAA;IACxB,KAAKJ,UAAU,OAAO,EAAE;GAC1B;GAKA,IAAI;IACF,SAAS;GACX,UAAU;IACR,IAAI,MAAM,OAAO,UAAU,CAAC,MAAM,YAAY,KAAKE,KAAK,KAAK,IAAI,KAAK;GACxE;EACF,GAAG,eAAe;CACpB;AACF;;;;;;AAOA,IAAM,UAAN,MAAc;CACZ,yBAAkB,IAAI,IAAmB;CACzC;CAEA,YAAY,YAA0C;EACpD,KAAKI,cAAc;CACrB;CAEA,IAAI,SAA8B;EAChC,MAAM,OAAO,QAAQ,WACb;GACJ,KAAKD,OAAO,OAAO,IAAI;EACzB,IACC,cAAuB;GACtB,KAAKA,OAAO,OAAO,IAAI;GACvB,KAAKC,YAAY,SAAS;EAC5B,CACF;EACA,KAAKD,OAAO,IAAI,IAAI;CACtB;;CAGA,MAAM,UAAyB;EAC7B,OAAO,KAAKA,OAAO,OAAO,GACxB,MAAM,QAAQ,IAAI,CAAC,GAAG,KAAKA,MAAM,CAAC;CAEtC;AACF;AAKA,IAAa,YAAb,MAAuB;CACrB;CACA;;;;;;;;CASA,qBAAsC,CAAC;;;;;;;CAQvC;CACA,qBAAqB;CACrB;CAEA;CACA;CACA;;;;;;CAOA;CACA;CACA,kBAAkB;CAClB;;CAGA;CAEA,YAAY,OAAc,OAAc;EACtC,KAAKE,SAAS;EACd,KAAKR,SAAS;EACd,KAAKC,YAAY,IAAI,eAAe,KAAK;EAEzC,MAAM,EAAE,SAAS,WAAW,QAAQ,cAAqB;EACzD,KAAKS,gBAAgB;EACrB,KAAKC,eAAe;EAEpB,QAAa,YAAY,CAAC,CAAC;EAE3B,KAAKL,SAAS,IAAI,SAAS,cAAc,KAAKC,YAAY,SAAS,CAAC;EACpE,KAAKK,kBAAkB,IAAI,SAAS,cAAc,KAAKL,YAAY,SAAS,CAAC;EAI7E,KAAK,UAAU,MAAM,aAAa,CAAC,CAAC,SAAS,CAAC;EAI9C,KAAK,UAAU,MAAM,cAAc,CAAC,CAAC,SAAS,CAAC;CACjD;;;;;;;;CAYA,eAAqB;EACnB,OAAO,KAAKM,gBAAgB,CAAC,CAAC,OAAO;CACvC;;CAGA,qBAAkD;EAChD,OAAO,KAAKJ,mBAAmB,GAAG,EAAE,CAAC,EAAE,mBAAmB;CAC5D;;CAGA,aAAsB;EACpB,OAAO,KAAKA,mBAAmB,SAAS;CAC1C;;;;;;;;;;;;CAaA,iBAA0B;EACxB,OAAO,iBAAiB;CAC1B;;;;;;;;;;;;CAaA,YAAY,MAAc,OAAiC;EACzD,KAAKK,eAAe;GAAE;GAAM;GAAO,IAAI,KAAK,IAAI;EAAE;CACpD;;;;;;;;;;;;CAaA,mBAA2B;EACzB,MAAM,MAAM,KAAKA;EACjB,IAAI,QAAQ,KAAA,GACV,OAAO;EAET,MAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,IAAI,EAAE;EAC1C,MAAM,cAAc,IAAI,UAAU,KAAA,IAAY,KAAK,UAAU,IAAI;EACjE,OAAO,uHAAuH,IAAI,KAAK,GAAG,IAAI,qBAAqB,YAAY;CACjL;;;;;CAMA,kBAAyB;EACvB,OAAO,KAAKN;CACd;;CAGA,MAAc;EACZ,OAAO,KAAKR,OAAO,IAAI;CACzB;;;;;;;;CAYA,eAAe,QAAiB,UAAsB,SAAyB;EAI7E,MAAM,QACJ,WAAW,KAAK,OAAO,MAAM,OAAO,IAChC,IACA,WAAW,iBACT,iBACA,KAAK,MAAM,OAAO;EAC1B,OAAO,KAAKC,UAAU,WAAW,MAAM;GAAE;GAAQ,SAAS;GAAO;EAAS,CAAC;CAC7E;;CAGA,iBAAiB,IAAkB;EACjC,KAAKA,UAAU,aAAa,EAAE;CAChC;;CAGA,kBAA0B;EACxB,OAAO,KAAKA,UAAU,gBAAgB;CACxC;;;;;CASA,qBAAoC;EAClC,OAAO,KAAKO,OAAO,cAAc,CAAC,CAAC,KAAK;CAC1C;;;;;CAMA,qBAA8B;EAC5B,OAAO,KAAKA,OAAO,cAAc,CAAC,CAAC,SAAS;CAC9C;;CAGA,gBAAmB,SAAqB,QAAkC;EACxE,OAAO,KAAKA,OAAO,cAAc,CAAC,CAAC,UAAU,SAAS,MAAM;CAC9D;;;;;CASA,UAA0B;EACxB,OAAO,KAAKE;CACd;;CAGA,MAAM,WAA0B;EAC9B,IAAI,KAAKK,oBAAoB,KAAA,GAC3B;EAEF,KAAKA,kBAAkB,EAAE,UAAU;EAInC,KAAKP,OAAO,mBAAmB,SAAS;EAQxC,KAAKP,UAAU,UAAU;EAEzB,KAAKU,aAAa,SAAS;CAC7B;;;;;;CAOA,UAAU,SAAiC;EACzC,IAAI,KAAKI,oBAAoB,KAAA,GAC3B,KAAKT,OAAO,IACV,QAAQ,WACA,CAAC,IACN,cAAuB;GACtB,KAAK,MAAM,SAAS;EACtB,CACF,CACF;CAEJ;;;;;;;;CAYA,QAAQ,SAA8B;EACpC,EAAE,KAAKU;EACP,KAAK,aAAa,OAAO;CAC3B;;;;;;CAOA,aAAa,SAA8B;EACzC,KAAKJ,gBAAgB,IAAI,OAAO;CAClC;;CAGA,YAAoB;EAClB,OAAO,KAAKI;CACd;;;;;;;;;CAUA,kBAA2B;EACzB,OAAO,KAAKC,kBAAkB;CAChC;;;;;;CAOA,MAAM,iBAAgC;EACpC,MAAM,QAAQ,KAAK,CAAC,KAAKL,gBAAgB,QAAQ,GAAG,KAAKF,cAAc,YAAY,CAAC,CAAC,CAAC,CAAC;CACzF;;;;;;;;;CAaA,MAAM,IACJ,MACA,QACY;EAIZ,MAAM,UAAU,KAAKK;EACrB,IAAI,YAAY,KAAA,GACd,MAAM,QAAQ;EAGhB,IAAI;EACJ,IAAI,WAAW,KAAA,GACb,OAAO,MAAM,KAAKP,OAAO,aAAa,CAAC,CAAC,KAAK;OACxC,IAAI,kBAAkB,iBAC3B,OAAO,MAAM,OAAO,KAAK;OAEzB,OAAO;EAGT,OAAO,MAAM,KAAKU,SAAS,MAAM,IAAI;CACvC;;;;;;;;;;;;;;;;;;;;;;;CAwBA,oBACE,MACoC;EAGpC,KAAKL,gBAAgB;EACrB,MAAM,kBAAkB,KAAK,mBAAmB;EAGhD,MAAM,oBAAoB,iBAAiB;EAE3C,OAAO,OAAO,GAAG,SAAgC;GAC/C,KAAK,YAAY,oDAAoD,iBAAiB;GACtF,MAAM,OAAO,KAAK,KAAK,SAAS,KAAK,MAAM,GAAG,IAAI,GAAG,eAAe;GAOpE,KAAK,QACH,KAAK,WACG,CAAC,SACD,CAAC,CACT,CACF;GAEA,OAAO,MAAM;EACf;CACF;CAoBA,QACE,SACA,OAAyC,UAC7B;EACZ,KAAK,YAAY,WAAW,iBAAiB,CAAC;EAC9C,OAAO,KAAKM,aAAa,SAAS,KAAK,mBAAmB,GAAG,IAAI;CACnE;;CAGA,6BACE,MACoC;EACpC,KAAKN,gBAAgB;EACrB,MAAM,kBAAkB,KAAK,mBAAmB;EAChD,MAAM,eAAe,KAAKO,uBAAuB,OAAO,KAAK,KAAKC,wBAAwB,KAAA;EAC1F,IAAI,cAAc,KAAKA,sBAAsB,iBAAiB;EAC9D,KAAKP,eAAe;GAClB,MAAM,eACF,kDACA;GACJ,OAAO,KAAKO;GACZ,IAAI,KAAK,IAAI;EACf;EAEA,QAAQ,GAAG,SAAgC;GACzC,MAAM,OAAO,KAAK,UAAU,KAAK,GAAG,IAAI,GAAG,eAAe;GAC1D,KAAK,QACH,KAAK,WACG,CAAC,SACD,CAAC,CACT,CACF;GACA,OAAO;EACT;CACF;CAgBA,qBACE,SACA,OAAyC,UAC7B;EACZ,IAAI;EACJ,IAAI;GAIF,YAAY,KAAK,aAAa;EAChC,SAAS,WAAW;GAClB,OAAO,QAAQ,OAAO,SAAS;EACjC;EACA,KAAK,YAAY,wBAAwB,iBAAiB,CAAC;EAC3D,OAAO,KAAKF,aAAa,SAAS,WAAW,IAAI;CACnD;;;;;;;;;;CAcA,sBAAyB,UAA0D;EACjF,MAAM,OAAO,KAAK,aAAa;EAC/B,KAAK,YAAY,yBAAyB,iBAAiB,CAAC;EAC5D,MAAM,kBAAkB,KAAK,qBAAqB;EAClD,MAAM,EAAE,SAAS,QAAQ,YAAY,QAAQ,cAAiB;EAE9D,KAAK,SACF,YAAY;GACX,IAAI;IACF,MAAM,QAAQ,MAAM,KAAKG,oBAAoB,iBAAiB,QAAQ;IAItE,MAAM,KAAKJ,eAAe;KACxB,QAAQ,KAAK;IACf,GAAG,gBAAgB,UAAU,CAAC;GAChC,SAAS,WAAW;IAGlB,wBAAwB,SAAS;IAMjC,gBAAgB,OAAO,SAAS;IAEhC,MAAM;GACR,UAAU;IAGR,gBAAgB,KAAK;GACvB;EACF,EAAA,CAAG,CACL;EAIA,KAAK,QAAQ;EAEb,OAAO;CACT;;;;;;;;CAWA,MAAMA,SAAY,MAA0C,MAAwB;EAClF,IAAI,CAAC,KAAK,MAAM,KAAKV,OAAO,aAAa,CAAC,GACxC,MAAM,IAAI,MAAM,kEAAkE;EAGpF,KAAKC,mBAAmB,KAAK,IAAI;EACjC,IAAI;EAGJ,MAAM,gBAAgB;EACtB,WAAW,IAAI;EACf,IAAI;GACF,SAAS,KAAK,IAAI;EACpB,UAAU;GACR,WAAW,aAAa;GACxB,sBAAsB;IACpB,KAAKc,MAAM,IAAI;GACjB,CAAC;EACH;EACA,OAAO,MAAM;CACf;;;;;;CAOA,kBAAwB;EACtB,MAAM,OAAO,KAAKd,mBAAmB,GAAG,EAAE;EAC1C,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,MAAM,0CAA0C,KAAK,iBAAiB,GAAG;EAErF,OAAO;CACT;;CAGA,MAAM,MAAkB;EACtB,MAAM,KAAK,KAAKA,mBAAmB,YAAY,IAAI;EACnD,IAAI,KAAK,GACP,MAAM,IAAI,MAAM,uDAAuD;EAEzE,KAAKA,mBAAmB,OAAO,IAAI,CAAC;EACpC,KAAK,QAAQ;CACf;;;;;;;;;;CAWA,aACE,SACA,QACA,MACY;EACZ,MAAM,EAAE,SAAS,QAAQ,SAAS,WAAW,QAAQ,cAAiB;EAEtE,KAAK,QACH,uBAAuB,OAAO,CAAC,CAAC,KAAK,OAAO,YAAY;GACtD,IAAI;IACF,MAAM,KAAK,UAAgB;KACzB,IAAI,QAAQ,IAEV,IAAI;MACF,QAAQ,KAAK,QAAQ,KAAK,CAAC;KAC7B,SAAS,WAAW;MAClB,OAAO,SAAS;KAClB;UAEA,OAAO,QAAQ,SAAS;IAE5B,GAAG,MAAM;GACX,SAAS,WAAW;IAMlB,IAAI,kBAAkB,MAAM,OAAO,QAAQ;IAC3C,MAAM;GACR;EACF,CAAC,CACH;EAEA,OAAO;CACT;;;;;CAMA,MAAMa,oBACJ,iBACA,UACY;EACZ,MAAM,YAAY,MAAM,gBAAgB,KAAK;EAE7C,OAAO,MAAM,KAAKJ,UAAU,SAAS;GAGnC,MAAM,UAAU,SAAS,IAAI;GAI7B,MAAM,WAAW,IAAI,gBAAgB;GACrC,MAAM,UAAU,KAAKlB,OAClB,WAAW,oCAAoC,SAAS,MAAM,CAAC,CAC/D,WAAkB;IACjB,MAAM,IAAI,MAAM,uCAAuC;GACzD,CAAC;GAKH,OAAO,QAAQ,KAAK,CAAC,QAAQ,QAAQ,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,cAAc;IACrE,SAAS,MAAM;GACjB,CAAC;EACH,GAAG,SAAS;CACd;;CAGA,YAAY,WAA0B;EACpC,IAAI,KAAKiB,qBAAqB,KAAA,GAC5B,KAAKA,mBAAmB,EAAE,UAAU;CAExC;AACF"}
package/dist/gate.js ADDED
@@ -0,0 +1,111 @@
1
+ import { c as tryCurrentSlice } from "./chunks/io-context-D89n6HVM.js";
2
+ //#region src/gate.ts
3
+ var continuationContext;
4
+ var publications = [];
5
+ function isThenable(value) {
6
+ return (typeof value === "object" && value !== null || typeof value === "function") && typeof Reflect.get(value, "then") === "function";
7
+ }
8
+ /** Re-enter the actor that owns this transformed await; fail open outside actors. */
9
+ function __gate(value) {
10
+ const context = tryCurrentSlice() ?? continuationContext?.context;
11
+ if (!isThenable(value) && context === void 0) return value;
12
+ if (context === void 0) return value;
13
+ return resumeWithContext(context, Promise.resolve(value));
14
+ }
15
+ /**
16
+ * Resolve one transformed await per task, inside a fresh actor slice. Starting
17
+ * from a task with an empty microtask queue makes its continuation the only code
18
+ * between publishing and clearing the actor identity. A shared queue prevents
19
+ * two actors that settle together from overwriting each other's publication.
20
+ */
21
+ function enqueuePublication(publication) {
22
+ publications.push(publication);
23
+ if (publications.length === 1) schedulePublication();
24
+ }
25
+ function resumeWithContext(context, promise) {
26
+ return new Promise((resolve, reject) => {
27
+ const publish = context.makeTransformReentryCallback((outcome) => {
28
+ const token = {};
29
+ continuationContext = {
30
+ context,
31
+ token
32
+ };
33
+ if (outcome.ok) resolve(outcome.value);
34
+ else reject(outcome.exception);
35
+ queueMicrotask(() => {
36
+ if (continuationContext?.token === token) continuationContext = void 0;
37
+ publications.shift();
38
+ if (publications.length > 0) schedulePublication();
39
+ });
40
+ });
41
+ promise.then((value) => {
42
+ enqueuePublication({
43
+ publish: () => publish({
44
+ ok: true,
45
+ value
46
+ }),
47
+ reject
48
+ });
49
+ }, (exception) => {
50
+ enqueuePublication({
51
+ publish: () => publish({
52
+ ok: false,
53
+ exception
54
+ }),
55
+ reject
56
+ });
57
+ });
58
+ });
59
+ }
60
+ function schedulePublication() {
61
+ const channel = new MessageChannel();
62
+ channel.port1.onmessage = () => {
63
+ channel.port1.close();
64
+ channel.port2.close();
65
+ const publication = publications[0];
66
+ if (publication === void 0) return;
67
+ publication.publish().catch((exception) => {
68
+ publication.reject(exception);
69
+ publications.shift();
70
+ if (publications.length > 0) schedulePublication();
71
+ });
72
+ };
73
+ channel.port2.postMessage(void 0);
74
+ }
75
+ function iteratorFor(iterable) {
76
+ const subject = Object(iterable);
77
+ const asyncIterator = Reflect.get(subject, Symbol.asyncIterator);
78
+ if (typeof asyncIterator === "function") return Reflect.apply(asyncIterator, iterable, []);
79
+ const iterator = Reflect.get(subject, Symbol.iterator);
80
+ if (typeof iterator === "function") return Reflect.apply(iterator, iterable, []);
81
+ throw new TypeError("value is not async iterable or iterable");
82
+ }
83
+ function gatedIterator(iterable) {
84
+ const iterator = iteratorFor(iterable);
85
+ function invoke(methodName, args) {
86
+ const method = Reflect.get(iterator, methodName);
87
+ if (typeof method === "function") return Promise.resolve(__gate(Reflect.apply(method, iterator, args)));
88
+ if (methodName === "throw") return Promise.reject(args[0]);
89
+ return Promise.resolve({
90
+ done: true,
91
+ value: args[0]
92
+ });
93
+ }
94
+ return {
95
+ next: (...args) => invoke("next", args),
96
+ return: (value) => invoke("return", [value]),
97
+ throw: (exception) => invoke("throw", [exception])
98
+ };
99
+ }
100
+ function __gateAsyncIterable(iterable) {
101
+ const wrapper = { [Symbol.asyncIterator]: () => gatedIterator(iterable) };
102
+ if ((typeof iterable !== "object" || iterable === null) && typeof iterable !== "function") return wrapper;
103
+ return new Proxy(iterable, { get(target, property, receiver) {
104
+ if (property === Symbol.asyncIterator) return wrapper[Symbol.asyncIterator];
105
+ return Reflect.get(target, property, receiver);
106
+ } });
107
+ }
108
+ //#endregion
109
+ export { __gate, __gateAsyncIterable };
110
+
111
+ //# sourceMappingURL=gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.js","names":[],"sources":["../src/gate.ts"],"sourcesContent":["/* @do-runtime-gated */\n\nimport { tryCurrentSlice, type IoContext } from \"./io/io-context\";\n\ntype ContinuationContext = {\n readonly context: IoContext;\n readonly token: object;\n};\n\nlet continuationContext: ContinuationContext | undefined;\n\ntype Publication = {\n readonly publish: () => Promise<void>;\n readonly reject: (exception: unknown) => void;\n};\n\ntype Outcome<T> =\n | { readonly ok: true; readonly value: T }\n | { readonly ok: false; readonly exception: unknown };\n\n// ponytail: one module-wide queue; shard by actor only if settled-await contention is measured.\nconst publications: Publication[] = [];\n\nfunction isThenable(value: unknown): value is PromiseLike<unknown> {\n return (\n (typeof value === \"object\" && value !== null) ||\n typeof value === \"function\"\n ) && typeof Reflect.get(value, \"then\") === \"function\";\n}\n\n/** Re-enter the actor that owns this transformed await; fail open outside actors. */\nexport function __gate<T>(value: T): T | Promise<Awaited<T>> {\n const context = tryCurrentSlice() ?? continuationContext?.context;\n if (!isThenable(value) && context === undefined) return value;\n if (context === undefined) return value;\n return resumeWithContext(context, Promise.resolve(value));\n}\n\n/**\n * Resolve one transformed await per task, inside a fresh actor slice. Starting\n * from a task with an empty microtask queue makes its continuation the only code\n * between publishing and clearing the actor identity. A shared queue prevents\n * two actors that settle together from overwriting each other's publication.\n */\nfunction enqueuePublication(publication: Publication): void {\n publications.push(publication);\n if (publications.length === 1) schedulePublication();\n}\n\nfunction resumeWithContext<T>(context: IoContext, promise: Promise<T>): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const publish = context.makeTransformReentryCallback((outcome: Outcome<T>) => {\n const token = {};\n continuationContext = { context, token };\n if (outcome.ok) resolve(outcome.value);\n else reject(outcome.exception);\n queueMicrotask(() => {\n if (continuationContext?.token === token) continuationContext = undefined;\n publications.shift();\n if (publications.length > 0) schedulePublication();\n });\n });\n void promise.then(\n (value) => {\n enqueuePublication({ publish: () => publish({ ok: true, value }), reject });\n },\n (exception: unknown) => {\n enqueuePublication({ publish: () => publish({ ok: false, exception }), reject });\n },\n );\n });\n}\n\nfunction schedulePublication(): void {\n const channel = new MessageChannel();\n channel.port1.onmessage = () => {\n channel.port1.close();\n channel.port2.close();\n const publication = publications[0];\n if (publication === undefined) return;\n\n void publication.publish().catch((exception: unknown) => {\n publication.reject(exception);\n publications.shift();\n if (publications.length > 0) schedulePublication();\n });\n };\n channel.port2.postMessage(undefined);\n}\n\nfunction iteratorFor<T>(iterable: AsyncIterable<T> | Iterable<T>): AsyncIterator<T> | Iterator<T> {\n const subject = Object(iterable);\n const asyncIterator: unknown = Reflect.get(subject, Symbol.asyncIterator);\n if (typeof asyncIterator === \"function\") return Reflect.apply(asyncIterator, iterable, []);\n const iterator: unknown = Reflect.get(subject, Symbol.iterator);\n if (typeof iterator === \"function\") return Reflect.apply(iterator, iterable, []);\n throw new TypeError(\"value is not async iterable or iterable\");\n}\n\nfunction gatedIterator<T>(iterable: AsyncIterable<T> | Iterable<T>): AsyncIterator<T> {\n const iterator = iteratorFor(iterable);\n\n function invoke(methodName: \"next\" | \"return\" | \"throw\", args: unknown[]): Promise<IteratorResult<T>> {\n const method: unknown = Reflect.get(iterator, methodName);\n if (typeof method === \"function\") {\n return Promise.resolve(__gate(Reflect.apply(method, iterator, args)));\n }\n if (methodName === \"throw\") return Promise.reject(args[0]);\n return Promise.resolve({ done: true, value: args[0] });\n }\n\n return {\n next: (...args: [] | [unknown]) => invoke(\"next\", args),\n return: (value?: unknown) => invoke(\"return\", [value]),\n throw: (exception?: unknown) => invoke(\"throw\", [exception]),\n };\n}\n\n/** Gate every operation used by `for await`, including early return and throw. */\nexport function __gateAsyncIterable<T, IterableType extends AsyncIterable<T> | Iterable<T>>(\n iterable: IterableType,\n): IterableType;\nexport function __gateAsyncIterable<T>(\n iterable: AsyncIterable<T> | Iterable<T>,\n): AsyncIterable<T> | Iterable<T> {\n const wrapper: AsyncIterable<T> = {\n [Symbol.asyncIterator]: () => gatedIterator(iterable),\n };\n if ((typeof iterable !== \"object\" || iterable === null) && typeof iterable !== \"function\") {\n return wrapper;\n }\n return new Proxy(iterable, {\n get(target, property, receiver): unknown {\n if (property === Symbol.asyncIterator) return wrapper[Symbol.asyncIterator];\n return Reflect.get(target, property, receiver);\n },\n });\n}\n"],"mappings":"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"}