Conversation
Every async method now accepts an optional `{ signal }` and forwards
it to the underlying driver call:
- find / findOne — { signal } added to find options
- exists / count / distinct — new options arg with signal
- save — signal forwarded to both insertOne and replaceOne paths
- saveAll — signal forwarded to insertMany
- update — new options arg with signal (plays nicely with existing
third-arg surface)
- remove / removeById — new options arg with signal
When the signal is already aborted (or fires mid-flight), the driver
throws AbortError, which the existing try/catch collapses into the
method's empty default + _emit. The fail-silent contract is unchanged.
Callers that need to distinguish "aborted" from "empty result" check
`signal.aborted` after the await — same pattern Node uses for
fs/promises and other AbortController-aware APIs.
findById is intentionally not extended — its second positional arg is
already overloaded (fields shortcut). Callers that need signal-aware
id lookup use findOne({ id }, { signal }) directly; the `id` alias is
recognised by prepare().
5dd54c7 to
c4db8ef
Compare
`Collection.each(query, options?)` returns an inline AsyncIterable
that also implements Symbol.asyncDispose — so it composes with both
`for await` and `await using`, and exposes nothing else:
for await (const doc of coll.each({ status: 'active' }, { signal })) {
// ...
}
await using docs = coll.each({});
for await (const doc of docs) {
// ...
}
The native cursor opens lazily on first iteration and closes
automatically when iteration ends — naturally, by `break`, or when
leaving an `await using` scope. Errors during open or iteration
collapse the loop to an early end and route through `_emit`; no
exception escapes `for await`.
No exported class, no separate file, no public `close()`, no visible
internal state. The returned object literally has only the two well-
known symbols.
This is the single softening of the "no async-iterator variants"
rule recorded in CLAUDE.md. It lets storage adapters drop their
manual escape hatch (`client.open(name)` + `native.find().stream()`)
for paginated reads over large collections.
Sits on top of the AbortSignal commit — `signal` flows through the
find options the iterator was constructed with.
c4db8ef to
211ffdd
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Collection.each(query, options?)returns an inlineAsyncIterable<doc>that also implementsSymbol.asyncDispose— it composes with bothfor awaitandawait using, and exposes nothing else:```js
for await (const doc of coll.each({ status: 'active' }, { signal })) {
// ...
}
await using docs = coll.each({});
for await (const doc of docs) {
// ...
}
```
The native cursor opens lazily on first iteration and closes automatically when iteration ends — naturally, by `break`, or when leaving an `await using` scope. Errors during open or iteration collapse the loop to an early end and route through `_emit`; no exception escapes `for await`.
No exported class, no separate file, no public
close(), no visible internal state. The returned object literally has only the two well-known symbols.This is the single softening of the "no async-iterator variants" rule recorded in
CLAUDE.md. It lets storage adapters drop their manual escape hatch (`client.open(name)` + `native.find().stream()`) for paginated reads over large collections.API surface
```ts
each(query?, options?): {
Symbol.asyncIterator: AsyncIterator;
Symbol.asyncDispose: Promise;
}
```
That's the entire public API. `options` is the same shape as `find` (limit, skip, sort, fields, projection, signal).
Dependencies
Built on top of #18 (AbortSignal). Merge #18 first; then this branch rebases cleanly onto main. The
signaloption flows through the find options the iterator was constructed with.Test plan
Context
Part of the yamb wishlist landing — see `plan/yamb-architecture-decisions.md` on `feature/yamb-request`. Final PR in the v6.x batch.