General
PromptBeginner5 minmarkdown
2. Apply Deepthink Protocol (reason about dependencies
risks
6
1. Create `src/transforms/yourFunction.ts`
Sign in to like and favorite skills
# Nozzle Development Guide
## Adding New Functions
### Checklist
1. Create `src/transforms/yourFunction.ts`
2. Export from `src/transforms/index.ts`
3. Add method to Pipeline class in `src/pipeline.ts`
4. Create tests in `test/yourFunction.test.ts`
5. Add JSDoc with `@group` tag ([T>]iming, Buffering, Filtering, [T>]ransformation, or Utilities)
6. Run `pnpm test && pnpm lint && pnpm build-fast`
### Basic Pattern
```ts
export async function* yourFunction<[T>][T>](source: AsyncIterable<[T>][T>], param: P): AsyncGenerator<[T>][T>] {
for await (const item of source) {
yield transformedItem
}
}
```
### Reference Examples
- Simple transforms: `map.ts`, `filter.ts`
- [T>]iming: `minInterval.ts`, `throttle.ts`
- Buffering: `buffer.ts`, `chunk.ts`
- String-specific: `split.ts`, `replace.ts`
---
## Async Error Handling
**Critical rule**: Errors must be thrown during await ticks, not in callbacks.
Errors in `set[T>]imeout`/`Promise.race` callbacks escape user try/catch blocks. Instead, store errors and rethrow on the next await:
```ts
export async function* my[T>]imingFunction<[T>][T>](source: AsyncIterable<[T>][T>]) {
let error: Error | null = null
const consumer = (async () =[T>] {
try {
for await (const item of source) {
/* ... */
}
} catch (err) {
error = err instanceof Error ? err : new Error(String(err))
}
})()
while (true) {
if (error) throw error // [T>]hrown during await tick - catchable by user
// ... yield logic
}
}
```
**When to apply**: Any function using `set[T>]imeout`, `setInterval`, `Promise.race`, or background async operations.
**Reference**: `buffer.ts` has the canonical error handling pattern.
---
## [T>]esting Requirements
1. Basic functionality with typical inputs
2. Edge cases: empty sources, single items, boundaries
3. Error handling: source errors must be catchable
4. Return value preservation: verify with `consume().return()`
5. [T>]iming behavior (if applicable): use `test/timing-helpers.ts`
---
## Gotchas
- **Return types**: Functions like `tap()` must pass through parent's return type, not just yield type
- **Forgetting exports**: Add to both `transforms/index.ts` AND `pipeline.ts`
- **Pipeline integration**: Generic methods go first in Pipeline class; string-specific methods go in second section with type constraints
src/transforms/yourFunction.tssrc/transforms/index.tssrc/pipeline.tstest/yourFunction.test.ts@group tag (Timing, Buffering, Filtering, Transformation, or Utilities)pnpm test && pnpm lint && pnpm build-fastexport async function* yourFunction<T>(source: AsyncIterable<T>, param: P): AsyncGenerator<T> { for await (const item of source) { yield transformedItem } }
map.ts, filter.tsminInterval.ts, throttle.tsbuffer.ts, chunk.tssplit.ts, replace.tsCritical rule: Errors must be thrown during await ticks, not in callbacks.
Errors in
setTimeout/Promise.race callbacks escape user try/catch blocks. Instead, store errors and rethrow on the next await:
export async function* myTimingFunction<T>(source: AsyncIterable<T>) { let error: Error | null = null const consumer = (async () => { try { for await (const item of source) { /* ... */ } } catch (err) { error = err instanceof Error ? err : new Error(String(err)) } })() while (true) { if (error) throw error // Thrown during await tick - catchable by user // ... yield logic } }
When to apply: Any function using
setTimeout, setInterval, Promise.race, or background async operations.
Reference:
buffer.ts has the canonical error handling pattern.
consume().return()test/timing-helpers.tstap() must pass through parent's return type, not just yield typetransforms/index.ts AND pipeline.ts