feat(napi-derive): add #[napi(async_iterator)] macro attribute#3072
feat(napi-derive): add #[napi(async_iterator)] macro attribute#3072Brooooooklyn merged 4 commits intomainfrom
Conversation
Add macro support for `#[napi(async_iterator)]` to mirror the existing
`#[napi(iterator)]` API, leveraging the `AsyncGenerator` trait and
`create_async_iterator()` runtime function.
Key changes:
- Add `async_iterator` attribute to attrs.rs
- Add async iterator fields to AST (NapiStruct, NapiClass, NapiImpl, NapiFn)
- Parse `async_iterator` and extract `AsyncGenerator` trait types
- Add mutual exclusivity check with `#[napi(iterator)]`
- Generate `[Symbol.asyncIterator]()` method in TypeScript
- Add `construct_async_generator` and `async_generator_factory` methods
- Export `create_async_iterator` in `__private` module
TypeScript generates proper async iterable type:
```typescript
export declare class AsyncFib {
[Symbol.asyncIterator](): AsyncGenerator<number, void, number | undefined>
}
```
TNext is `T | undefined` to allow `for await...of` loops which call
`next()` with no arguments.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. 📝 WalkthroughWalkthroughAdds end-to-end async generator/async-iterator support: AST flags/types, macro parsing for #[napi(async_iterator)], codegen paths for async-generator constructors/factories, runtime helpers and AsyncGenerator runtime module (tokio_rt-gated), TypeScript typings, and three async-iterable example classes with tests and exports. Changes
Sequence Diagram(s)sequenceDiagram
participant Macro as Macro Parser
participant AST as AST
participant Codegen as Code Generator
participant Callback as CallbackInfo
participant Runtime as AsyncIterator Runtime
participant JS as JavaScript
Note over Macro,AST: Compile-time parsing & AST population
Macro->>AST: parse #[napi(async_iterator)] and impl async_iterator types
AST->>Codegen: emit Symbol.asyncIterator sig & gen_async_iterator_property()
Note over Codegen,Callback: emit runtime constructor/factory calls
JS->>Callback: call constructor/factory
Callback->>Callback: construct_async_generator / async_generator_factory
Callback->>Runtime: create_async_iterator(instance)
Runtime->>JS: return wrapped instance with Symbol.asyncIterator
JS->>Runtime: iterator.next() -> returns Promise
Runtime->>Runtime: poll associated Future (async next)
Runtime-->>JS: Promise resolves with { value, done }
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used🧠 Learnings (1)📓 Common learnings⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (41)
🔇 Additional comments (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
There was a problem hiding this comment.
💡 Codex Review
napi-rs/crates/napi/src/bindgen_runtime/async_iterator.rs
Lines 341 to 344 in c240a4c
In the async generator return path the resolved promise sets done to false whenever complete() returns a value (value is Some), only marking completion when complete() returns None. According to the async iterator protocol, return() must always resolve to { done: true, value }, even if a final value is provided. With the current logic, any AsyncGenerator override of complete() that wants to return a final value will signal done: false, causing consumers (including for await...of) to keep iterating and invoke next() again, contrary to spec and likely to produce duplicate work.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
This PR adds support for #[napi(async_iterator)] macro attribute, enabling Rust structs to implement JavaScript's async iterable protocol. This mirrors the existing #[napi(iterator)] functionality but for asynchronous iteration patterns.
Key Changes:
- Added
AsyncGeneratortrait withnext(),complete(), andcatch()methods returning futures - Implemented macro parsing and code generation for
#[napi(async_iterator)]attribute with mutual exclusivity check against#[napi(iterator)] - Generated TypeScript definitions with
[Symbol.asyncIterator](): AsyncGenerator<TYield, TReturn, TNext>signature whereTNextincludesundefinedforfor await...ofcompatibility
Reviewed changes
Copilot reviewed 16 out of 18 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.json | Updated TypeScript target from ES2022 to ES2024 for async generator support |
| examples/napi/src/generator.rs | Added three example async iterator implementations: AsyncFib (basic), DelayedCounter (with delays), and AsyncDataSource (factory pattern) |
| examples/napi/index.d.cts | Generated TypeScript declarations for async iterator classes with JSDoc explaining async iterable protocol |
| examples/napi/index.cjs | Added exports for AsyncDataSource, AsyncFib, and DelayedCounter classes |
| examples/napi/example.wasi.cjs | Added WASI exports for new async iterator classes |
| examples/napi/example.wasi-browser.js | Added browser WASI exports for new async iterator classes |
| examples/napi/tests/generator.spec.ts | Added comprehensive test coverage for async generators including for-await-of loops, next(), return(), completion, and concurrency tests |
| examples/napi/tests/snapshots/values.spec.ts.snap | Binary snapshot file updated with new TypeScript definitions |
| examples/napi/tests/snapshots/values.spec.ts.md | Markdown snapshot updated showing generated TypeScript for async iterator classes |
| crates/napi/src/lib.rs | Exported create_async_iterator in __private module with tokio_rt feature gate |
| crates/napi/src/bindgen_runtime/mod.rs | Exported AsyncGenerator trait with tokio_rt feature gate |
| crates/napi/src/bindgen_runtime/callback_info.rs | Added construct_async_generator and async_generator_factory methods mirroring sync generator equivalents |
| crates/macro/src/parser/mod.rs | Added parsing logic for async_iterator attribute, mutual exclusivity validation, and tracking of async generator types |
| crates/macro/src/parser/attrs.rs | Added async_iterator attribute definition to the macro attribute system |
| crates/backend/src/typegen/struct.rs | Added TypeScript type generation for async iterators with proper TNext handling (includes undefined) |
| crates/backend/src/codegen/struct.rs | Added gen_async_iterator_property for calling create_async_iterator in generated code |
| crates/backend/src/codegen/fn.rs | Added async generator handling in constructor and factory method code generation |
| crates/backend/src/ast.rs | Added async_iterator fields to NapiFn, NapiStruct, NapiClass, and NapiImpl AST nodes |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…rrency test WASI environments in CI can be slower, causing the 150ms tolerance to fail. Increase to 300ms which still validates concurrency while accommodating slower CI environments. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Improve comments in AsyncFib example to clarify the pattern - Expand comment explaining why create_async_iterator is not unsafe - Improve error message for iterator/async_iterator mutual exclusivity - Add throw() test for async generators - Add idempotency check for completed async generator state - Make skip messages consistent (mention tokio_rt feature) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
Per the async iterator protocol, return() must ALWAYS resolve to
{ done: true, value } even when complete() returns a final value.
The previous implementation incorrectly set done: false when
complete() returned Some(value).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
|
Fixed the bug reported by Codex Review: |

Add macro support for
#[napi(async_iterator)]to mirror the existing#[napi(iterator)]API, leveraging theAsyncGeneratortrait andcreate_async_iterator()runtime function.Key changes:
async_iteratorattribute to attrs.rsasync_iteratorand extractAsyncGeneratortrait types#[napi(iterator)][Symbol.asyncIterator]()method in TypeScriptconstruct_async_generatorandasync_generator_factorymethodscreate_async_iteratorin__privatemoduleTypeScript generates proper async iterable type:
TNext is
T | undefinedto allowfor await...ofloops which callnext()with no arguments.🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.5 [email protected]
Note
Adds first-class async iterator support parallel to sync iterators.
#[napi(async_iterator)]attribute, mutual exclusivity withiterator, and new flags/types onNapiFn,NapiStruct,NapiClass,NapiImpl(async iterator Yield/Next/Return)construct_async_generator,async_generator_factory); auto-attach async iterator on instancescreate_async_iterator; ensurereturn()setsdone: true; plumb async generator trait undertokio_rt[Symbol.asyncIterator](): AsyncGenerator<Y, R, N>withTNext | undefinedforfor await...ofAsyncFib,DelayedCounter,AsyncDataSourcewith comprehensive async-iterator tests and module re-exportsES2024Written by Cursor Bugbot for commit c5b3706. This will update automatically on new commits. Configure here.
Summary by CodeRabbit
New Features
Examples
Tests
Chores
✏️ Tip: You can customize this high-level summary in your review settings.