원클릭으로
effect-stream
Effect Stream for lazy, unbounded, or resource-scoped sequences. Use when data is paginated, infinite, or needs backpressure.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Effect Stream for lazy, unbounded, or resource-scoped sequences. Use when data is paginated, infinite, or needs backpressure.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Effect service definition, interface design, error types, retries. Use when creating new services or defining error hierarchies.
AXM - Agent Extension Manager: Use for any operation (install/create/new/edit/update/add/remove/delete/publish/find/discover) on agent skills, subagents, slash commands/stored prompts, MCP servers, context packages, rule extensions, hook extensions, or packs — e.g. "create a skill", "make a /command", "add a subagent", "build an MCP server", "publish an extension". Use this BEFORE hand-authoring or editing any SKILL.md, slash-command, subagent, MCP, rule, hook, or extension manifest file: route extension authoring through AXM instead of writing these files directly.
Native skill manifest with two unknown top-level keys.
Effect CLI + Effect architecture. Use when adding commands, defining flags, or wiring handlers. Covers file organization, argument/flag patterns, and testing.
"unterminated
Native skill with an invalid extension name in manifest.
| name | effect-stream |
| description | Effect Stream for lazy, unbounded, or resource-scoped sequences. Use when data is paginated, infinite, or needs backpressure. |
| user-invocable | false |
Stream<A, E, R> is a pull-based, lazy description of a program that emits zero or more values. Where Effect<A, E, R> always produces exactly one value, Stream handles finite, infinite, or empty sequences with inherent laziness and backpressure.
For finite, materialized collections, see /effect-iteration.
| Use Stream when... | Use Effect.forEach when... |
|---|---|
| Data is fetched lazily (pagination, cursor) | Collection is already in memory |
| Sequence is potentially infinite | Size is known and bounded |
| Processing needs backpressure | All results needed before continuing |
| Resources must be scoped to iteration | No resource lifecycle concerns |
| Multi-stage transformation pipeline | Simple one-pass processing |
// From iterable (zero-cost wrapping)
const stream = Stream.fromIterable([1, 2, 3]);
// From single effect
const stream = Stream.fromEffect(fetchUser(id));
// From chunk
const stream = Stream.fromChunk(chunk);
// Paginated API with unfoldEffect
const pages = Stream.unfoldEffect(firstPageToken, (token) =>
fetchPage(token).pipe(
Effect.map((page) =>
page.nextToken ? Option.some([page.data, page.nextToken] as const) : Option.none(),
),
),
);
// Infinite sequence (safe—only consumed elements computed)
const naturals = Stream.iterate(1, (n) => n + 1);
const firstFive = naturals.pipe(Stream.take(5));
// Repeated effect
const heartbeats = Stream.repeatEffect(ping());
const pipeline = Stream.fromIterable(rawEvents).pipe(
Stream.filter((e) => e.type === "purchase"),
Stream.map((e) => ({ userId: e.userId, amount: e.amount })),
Stream.mapEffect((e) => enrichWithUserData(e)),
);
Stream.mapEffect accepts concurrency options:
Stream.fromIterable(urls).pipe(Stream.mapEffect((url) => fetchUrl(url), { concurrency: 5 }));
// Unordered for higher throughput (results as they complete)
Stream.mapEffect((url) => fetchUrl(url), { concurrency: 5, unordered: true });
// Fixed-size batches
const batched = stream.pipe(Stream.grouped(100));
// Time-windowed batches (by count OR time, whichever first)
const microBatched = stream.pipe(
Stream.groupedWithin(100, "5 seconds"),
Stream.mapEffect((batch) => insertBatch(batch)),
);
// Stop before failing element
const until100 = Stream.iterate(0, (n) => n + 1).pipe(
Stream.takeWhile((n) => n < 100), // emits 0..99
);
// Stop after matching element
const untilDone = stream.pipe(Stream.takeUntil((msg) => msg.type === "done"));
// Concurrent interleaving
const merged = Stream.merge(stream1, stream2);
// Halt strategies: "both" (default), "either", "left", "right"
const merged = Stream.merge(stream1, stream2, { haltStrategy: "either" });
// Merge many streams
const all = Stream.mergeAll([s1, s2, s3], { concurrency: 3 });
Pull-based model provides inherent backpressure: slow consumer throttles producer.
For decoupling producer/consumer speeds:
const buffered = Stream.range(1, 10000).pipe(
Stream.mapEffect(processItem),
Stream.buffer({ capacity: 64 }), // producer pauses when buffer fills
);
Stream.acquireRelease ties element production to resource lifecycles:
const rows = Stream.acquireRelease(openDatabaseConnection(), (conn) => conn.close()).pipe(
Stream.flatMap((conn) => Stream.fromIterable(conn.queryIterator("SELECT * FROM users"))),
);
// Connection released after consumption ends, even on failure
Other resource patterns:
// Single-valued stream from scoped resource
const scoped = Stream.scoped(acquireScopedResource());
// Attach cleanup to stream termination
const withCleanup = stream.pipe(Stream.ensuring(Effect.log("Stream finished")));
| Method | Use When |
|---|---|
Stream.runCollect(stream) | Need full result set (bounded only!) |
Stream.runForEach(stream, fn) | Side-effect processing, no collection |
Stream.runFold(stream, init, fn) | Aggregation without materialization |
Stream.runDrain(stream) | Run for effects only, discard values |
Stream.run(stream, sink) | Custom consumption via Sink |
Stream.runCollect materializes the entire stream into a Chunk. Use only for bounded streams. For side-effect processing, prefer Stream.runForEach.
// Collect bounded stream
const items = yield * Stream.runCollect(stream);
const array = Chunk.toReadonlyArray(items);
// Process without collecting
yield * Stream.runForEach(stream, (item) => processItem(item));
// Fold to aggregate
const sum = yield * Stream.runFold(stream, 0, (acc, n) => acc + n);
Chunk<A> is optimized for repeated concatenation—the pattern inside Stream pipelines. You'll encounter it as the return type of Stream.runCollect.
// Stream.runCollect returns Chunk
const chunk = yield * Stream.runCollect(stream);
// Convert to array when needed
const array = Chunk.toReadonlyArray(chunk);
For general collection guidance, see /effect-collections.
Over-abstracting with Stream for small arrays. If you have 10 items in memory, Effect.forEach(items, callApi, { concurrency: 5 }) is simpler than Stream.fromIterable(items).pipe(Stream.mapEffect(callApi), Stream.runCollect).
Collecting entire Stream unnecessarily. Calling Stream.runCollect on 10 million rows defeats the purpose. Use Stream.runForEach or Stream.runFold.
Forgetting to consume. A Stream is a description—it does nothing until you run it.
| Situation | Pattern | Key API |
|---|---|---|
| Paginated API / database cursor | Stream.unfoldEffect | Lazy page fetching |
| Infinite or unbounded sequence | Stream.iterate/repeatEffect | Pull-based, safe to define |
| Multi-stage transform pipeline | Stream operators | map → filter → mapEffect |
| Resource-scoped iteration | Stream.acquireRelease | Guaranteed cleanup |
| Concurrent processing with backpressure | Stream.mapEffect | { concurrency: N } |
| Time-windowed batching | Stream.groupedWithin | Count OR time trigger |
| Merging event sources | Stream.merge/mergeAll | Concurrent interleaving |
| Known finite collection | Effect.forEach | See /effect-iteration |
acquireRelease scopes cleanup to consumption