@dojocho/effect-ts 0.0.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.
Files changed (149) hide show
  1. package/DOJO.md +22 -0
  2. package/dojo.json +50 -0
  3. package/katas/001-hello-effect/SENSEI.md +72 -0
  4. package/katas/001-hello-effect/solution.test.ts +35 -0
  5. package/katas/001-hello-effect/solution.ts +16 -0
  6. package/katas/002-transform-with-map/SENSEI.md +72 -0
  7. package/katas/002-transform-with-map/solution.test.ts +33 -0
  8. package/katas/002-transform-with-map/solution.ts +16 -0
  9. package/katas/003-generator-pipelines/SENSEI.md +72 -0
  10. package/katas/003-generator-pipelines/solution.test.ts +40 -0
  11. package/katas/003-generator-pipelines/solution.ts +29 -0
  12. package/katas/004-flatmap-and-chaining/SENSEI.md +80 -0
  13. package/katas/004-flatmap-and-chaining/solution.test.ts +34 -0
  14. package/katas/004-flatmap-and-chaining/solution.ts +18 -0
  15. package/katas/005-pipe-composition/SENSEI.md +81 -0
  16. package/katas/005-pipe-composition/solution.test.ts +41 -0
  17. package/katas/005-pipe-composition/solution.ts +19 -0
  18. package/katas/006-handle-errors/SENSEI.md +86 -0
  19. package/katas/006-handle-errors/solution.test.ts +53 -0
  20. package/katas/006-handle-errors/solution.ts +30 -0
  21. package/katas/007-tagged-errors/SENSEI.md +79 -0
  22. package/katas/007-tagged-errors/solution.test.ts +82 -0
  23. package/katas/007-tagged-errors/solution.ts +37 -0
  24. package/katas/008-error-patterns/SENSEI.md +89 -0
  25. package/katas/008-error-patterns/solution.test.ts +41 -0
  26. package/katas/008-error-patterns/solution.ts +38 -0
  27. package/katas/009-option-type/SENSEI.md +96 -0
  28. package/katas/009-option-type/solution.test.ts +49 -0
  29. package/katas/009-option-type/solution.ts +26 -0
  30. package/katas/010-either-and-exit/SENSEI.md +86 -0
  31. package/katas/010-either-and-exit/solution.test.ts +33 -0
  32. package/katas/010-either-and-exit/solution.ts +17 -0
  33. package/katas/011-services-and-context/SENSEI.md +82 -0
  34. package/katas/011-services-and-context/solution.test.ts +23 -0
  35. package/katas/011-services-and-context/solution.ts +17 -0
  36. package/katas/012-layers/SENSEI.md +73 -0
  37. package/katas/012-layers/solution.test.ts +23 -0
  38. package/katas/012-layers/solution.ts +26 -0
  39. package/katas/013-testing-effects/SENSEI.md +88 -0
  40. package/katas/013-testing-effects/solution.test.ts +41 -0
  41. package/katas/013-testing-effects/solution.ts +20 -0
  42. package/katas/014-schema-basics/SENSEI.md +81 -0
  43. package/katas/014-schema-basics/solution.test.ts +35 -0
  44. package/katas/014-schema-basics/solution.ts +25 -0
  45. package/katas/015-domain-modeling/SENSEI.md +85 -0
  46. package/katas/015-domain-modeling/solution.test.ts +46 -0
  47. package/katas/015-domain-modeling/solution.ts +42 -0
  48. package/katas/016-retry-and-schedule/SENSEI.md +72 -0
  49. package/katas/016-retry-and-schedule/solution.test.ts +26 -0
  50. package/katas/016-retry-and-schedule/solution.ts +23 -0
  51. package/katas/017-parallel-effects/SENSEI.md +70 -0
  52. package/katas/017-parallel-effects/solution.test.ts +33 -0
  53. package/katas/017-parallel-effects/solution.ts +17 -0
  54. package/katas/018-race-and-timeout/SENSEI.md +75 -0
  55. package/katas/018-race-and-timeout/solution.test.ts +30 -0
  56. package/katas/018-race-and-timeout/solution.ts +27 -0
  57. package/katas/019-ref-and-state/SENSEI.md +72 -0
  58. package/katas/019-ref-and-state/solution.test.ts +29 -0
  59. package/katas/019-ref-and-state/solution.ts +16 -0
  60. package/katas/020-fibers/SENSEI.md +80 -0
  61. package/katas/020-fibers/solution.test.ts +23 -0
  62. package/katas/020-fibers/solution.ts +23 -0
  63. package/katas/021-acquire-release/SENSEI.md +57 -0
  64. package/katas/021-acquire-release/solution.test.ts +23 -0
  65. package/katas/021-acquire-release/solution.ts +22 -0
  66. package/katas/022-scoped-layers/SENSEI.md +52 -0
  67. package/katas/022-scoped-layers/solution.test.ts +35 -0
  68. package/katas/022-scoped-layers/solution.ts +19 -0
  69. package/katas/023-resource-patterns/SENSEI.md +52 -0
  70. package/katas/023-resource-patterns/solution.test.ts +20 -0
  71. package/katas/023-resource-patterns/solution.ts +13 -0
  72. package/katas/024-streams-basics/SENSEI.md +61 -0
  73. package/katas/024-streams-basics/solution.test.ts +30 -0
  74. package/katas/024-streams-basics/solution.ts +16 -0
  75. package/katas/025-stream-operations/SENSEI.md +59 -0
  76. package/katas/025-stream-operations/solution.test.ts +26 -0
  77. package/katas/025-stream-operations/solution.ts +17 -0
  78. package/katas/026-combining-streams/SENSEI.md +54 -0
  79. package/katas/026-combining-streams/solution.test.ts +20 -0
  80. package/katas/026-combining-streams/solution.ts +16 -0
  81. package/katas/027-data-pipelines/SENSEI.md +58 -0
  82. package/katas/027-data-pipelines/solution.test.ts +22 -0
  83. package/katas/027-data-pipelines/solution.ts +16 -0
  84. package/katas/028-logging-and-spans/SENSEI.md +58 -0
  85. package/katas/028-logging-and-spans/solution.test.ts +50 -0
  86. package/katas/028-logging-and-spans/solution.ts +20 -0
  87. package/katas/029-http-client/SENSEI.md +59 -0
  88. package/katas/029-http-client/solution.test.ts +49 -0
  89. package/katas/029-http-client/solution.ts +24 -0
  90. package/katas/030-capstone/SENSEI.md +63 -0
  91. package/katas/030-capstone/solution.test.ts +67 -0
  92. package/katas/030-capstone/solution.ts +55 -0
  93. package/katas/031-config-and-environment/SENSEI.md +77 -0
  94. package/katas/031-config-and-environment/solution.test.ts +38 -0
  95. package/katas/031-config-and-environment/solution.ts +11 -0
  96. package/katas/032-cause-and-defects/SENSEI.md +90 -0
  97. package/katas/032-cause-and-defects/solution.test.ts +50 -0
  98. package/katas/032-cause-and-defects/solution.ts +23 -0
  99. package/katas/033-pattern-matching/SENSEI.md +86 -0
  100. package/katas/033-pattern-matching/solution.test.ts +36 -0
  101. package/katas/033-pattern-matching/solution.ts +28 -0
  102. package/katas/034-deferred-and-coordination/SENSEI.md +85 -0
  103. package/katas/034-deferred-and-coordination/solution.test.ts +25 -0
  104. package/katas/034-deferred-and-coordination/solution.ts +24 -0
  105. package/katas/035-queue-and-backpressure/SENSEI.md +100 -0
  106. package/katas/035-queue-and-backpressure/solution.test.ts +25 -0
  107. package/katas/035-queue-and-backpressure/solution.ts +21 -0
  108. package/katas/036-schema-advanced/SENSEI.md +81 -0
  109. package/katas/036-schema-advanced/solution.test.ts +55 -0
  110. package/katas/036-schema-advanced/solution.ts +19 -0
  111. package/katas/037-cache-and-memoization/SENSEI.md +73 -0
  112. package/katas/037-cache-and-memoization/solution.test.ts +47 -0
  113. package/katas/037-cache-and-memoization/solution.ts +24 -0
  114. package/katas/038-metrics/SENSEI.md +91 -0
  115. package/katas/038-metrics/solution.test.ts +39 -0
  116. package/katas/038-metrics/solution.ts +23 -0
  117. package/katas/039-managed-runtime/SENSEI.md +75 -0
  118. package/katas/039-managed-runtime/solution.test.ts +29 -0
  119. package/katas/039-managed-runtime/solution.ts +19 -0
  120. package/katas/040-request-batching/SENSEI.md +87 -0
  121. package/katas/040-request-batching/solution.test.ts +56 -0
  122. package/katas/040-request-batching/solution.ts +32 -0
  123. package/package.json +22 -0
  124. package/skills/effect-patterns-building-apis/SKILL.md +2393 -0
  125. package/skills/effect-patterns-building-data-pipelines/SKILL.md +1876 -0
  126. package/skills/effect-patterns-concurrency/SKILL.md +2999 -0
  127. package/skills/effect-patterns-concurrency-getting-started/SKILL.md +351 -0
  128. package/skills/effect-patterns-core-concepts/SKILL.md +3199 -0
  129. package/skills/effect-patterns-domain-modeling/SKILL.md +1385 -0
  130. package/skills/effect-patterns-error-handling/SKILL.md +1212 -0
  131. package/skills/effect-patterns-error-handling-resilience/SKILL.md +179 -0
  132. package/skills/effect-patterns-error-management/SKILL.md +1668 -0
  133. package/skills/effect-patterns-getting-started/SKILL.md +237 -0
  134. package/skills/effect-patterns-making-http-requests/SKILL.md +1756 -0
  135. package/skills/effect-patterns-observability/SKILL.md +1586 -0
  136. package/skills/effect-patterns-platform/SKILL.md +1195 -0
  137. package/skills/effect-patterns-platform-getting-started/SKILL.md +179 -0
  138. package/skills/effect-patterns-project-setup--execution/SKILL.md +233 -0
  139. package/skills/effect-patterns-resource-management/SKILL.md +827 -0
  140. package/skills/effect-patterns-scheduling/SKILL.md +451 -0
  141. package/skills/effect-patterns-scheduling-periodic-tasks/SKILL.md +763 -0
  142. package/skills/effect-patterns-streams/SKILL.md +2052 -0
  143. package/skills/effect-patterns-streams-getting-started/SKILL.md +421 -0
  144. package/skills/effect-patterns-streams-sinks/SKILL.md +1181 -0
  145. package/skills/effect-patterns-testing/SKILL.md +1632 -0
  146. package/skills/effect-patterns-tooling-and-debugging/SKILL.md +1125 -0
  147. package/skills/effect-patterns-value-handling/SKILL.md +676 -0
  148. package/tsconfig.json +20 -0
  149. package/vitest.config.ts +3 -0
@@ -0,0 +1,421 @@
1
+ ---
2
+ name: effect-patterns-streams-getting-started
3
+ description: Effect-TS patterns for Streams Getting Started. Use when working with streams getting started in Effect-TS applications.
4
+ ---
5
+ # Effect-TS Patterns: Streams Getting Started
6
+ This skill provides 4 curated Effect-TS patterns for streams getting started.
7
+ Use this skill when working on tasks related to:
8
+ - streams getting started
9
+ - Best practices in Effect-TS applications
10
+ - Real-world patterns and solutions
11
+
12
+ ---
13
+
14
+ ## 🟢 Beginner Patterns
15
+
16
+ ### Your First Stream
17
+
18
+ **Rule:** Use Stream to process sequences of data lazily and efficiently.
19
+
20
+ **Good Example:**
21
+
22
+ ```typescript
23
+ import { Effect, Stream } from "effect"
24
+
25
+ // Create a stream from explicit values
26
+ const numbers = Stream.make(1, 2, 3, 4, 5)
27
+
28
+ // Create a stream from an array
29
+ const fromArray = Stream.fromIterable([10, 20, 30])
30
+
31
+ // Create a single-value stream
32
+ const single = Stream.succeed("hello")
33
+
34
+ // Transform and run the stream
35
+ const program = numbers.pipe(
36
+ Stream.map((n) => n * 2), // Double each number
37
+ Stream.filter((n) => n > 4), // Keep only > 4
38
+ Stream.runCollect // Collect results
39
+ )
40
+
41
+ Effect.runPromise(program).then((chunk) => {
42
+ console.log([...chunk]) // [6, 8, 10]
43
+ })
44
+ ```
45
+
46
+ **Anti-Pattern:**
47
+
48
+ Don't use regular arrays when you need lazy processing or async operations:
49
+
50
+ ```typescript
51
+ // Anti-pattern: Eager processing, all in memory
52
+ const numbers = [1, 2, 3, 4, 5]
53
+ const doubled = numbers.map((n) => n * 2)
54
+ const filtered = doubled.filter((n) => n > 4)
55
+ ```
56
+
57
+ This loads everything into memory immediately. Use Stream when:
58
+ - Data is large or potentially infinite
59
+ - Data arrives asynchronously
60
+ - You need backpressure or resource management
61
+
62
+ **Rationale:**
63
+
64
+ A Stream is a lazy sequence of values that can be processed one at a time. Create streams with `Stream.make`, `Stream.fromIterable`, or `Stream.succeed`.
65
+
66
+ ---
67
+
68
+
69
+ Streams are Effect's answer to processing sequences of data. Unlike arrays which hold all values in memory at once, streams produce values on demand. This makes them ideal for:
70
+
71
+ 1. **Large datasets** - Process millions of records without loading everything into memory
72
+ 2. **Async data** - Handle data that arrives over time (files, APIs, events)
73
+ 3. **Composable pipelines** - Chain transformations that work element by element
74
+
75
+ ---
76
+
77
+ ---
78
+
79
+ ### Stream vs Effect - When to Use Which
80
+
81
+ **Rule:** Use Effect for single values, Stream for sequences of values.
82
+
83
+ **Good Example:**
84
+
85
+ ```typescript
86
+ import { Effect, Stream } from "effect"
87
+
88
+ // ============================================
89
+ // EFFECT: Single result operations
90
+ // ============================================
91
+
92
+ // Fetch one user - returns Effect<User>
93
+ const fetchUser = (id: string) =>
94
+ Effect.tryPromise(() =>
95
+ fetch(`/api/users/${id}`).then((r) => r.json())
96
+ )
97
+
98
+ // Read entire config - returns Effect<Config>
99
+ const loadConfig = Effect.tryPromise(() =>
100
+ fetch("/config.json").then((r) => r.json())
101
+ )
102
+
103
+ // ============================================
104
+ // STREAM: Multiple values operations
105
+ // ============================================
106
+
107
+ // Process file line by line - returns Stream<string>
108
+ const fileLines = Stream.fromIterable([
109
+ "line 1",
110
+ "line 2",
111
+ "line 3",
112
+ ])
113
+
114
+ // Generate events over time - returns Stream<Event>
115
+ const events = Stream.make(
116
+ { type: "click", x: 10 },
117
+ { type: "click", x: 20 },
118
+ { type: "scroll", y: 100 },
119
+ )
120
+
121
+ // ============================================
122
+ // CONVERTING BETWEEN THEM
123
+ // ============================================
124
+
125
+ // Effect → Stream (single value becomes 1-element stream)
126
+ const effectToStream = Stream.fromEffect(fetchUser("123"))
127
+
128
+ // Stream → Effect (collect all values into array)
129
+ const streamToEffect = Stream.runCollect(fileLines)
130
+
131
+ // Stream → Effect (process each value for side effects)
132
+ const processAll = fileLines.pipe(
133
+ Stream.runForEach((line) => Effect.log(`Processing: ${line}`))
134
+ )
135
+
136
+ // ============================================
137
+ // DECISION GUIDE
138
+ // ============================================
139
+
140
+ // Use Effect when:
141
+ // - Fetching a single resource
142
+ // - Computing a single result
143
+ // - Performing one action
144
+
145
+ // Use Stream when:
146
+ // - Reading files line by line
147
+ // - Processing paginated API results
148
+ // - Handling real-time events
149
+ // - Processing large datasets
150
+ // - Building data pipelines
151
+ ```
152
+
153
+ **Rationale:**
154
+
155
+ Use `Effect` when your operation produces a single result. Use `Stream` when your operation produces multiple values over time.
156
+
157
+ ---
158
+
159
+
160
+ Both Effect and Stream are lazy and composable, but they serve different purposes:
161
+
162
+ | Aspect | Effect | Stream |
163
+ |--------|--------|--------|
164
+ | **Produces** | One value | Zero or more values |
165
+ | **Memory** | Holds one result | Processes incrementally |
166
+ | **Use case** | API call, DB query | File lines, events, batches |
167
+
168
+ ---
169
+
170
+ ---
171
+
172
+ ### Running and Collecting Stream Results
173
+
174
+ **Rule:** Choose the right Stream.run* method based on what you need from the results.
175
+
176
+ **Good Example:**
177
+
178
+ ```typescript
179
+ import { Effect, Stream, Option } from "effect"
180
+
181
+ const numbers = Stream.make(1, 2, 3, 4, 5)
182
+
183
+ // ============================================
184
+ // runCollect - Get all results as a Chunk
185
+ // ============================================
186
+
187
+ const collectAll = numbers.pipe(
188
+ Stream.map((n) => n * 10),
189
+ Stream.runCollect
190
+ )
191
+
192
+ Effect.runPromise(collectAll).then((chunk) => {
193
+ console.log([...chunk]) // [10, 20, 30, 40, 50]
194
+ })
195
+
196
+ // ============================================
197
+ // runForEach - Process each item
198
+ // ============================================
199
+
200
+ const processEach = numbers.pipe(
201
+ Stream.runForEach((n) =>
202
+ Effect.log(`Processing: ${n}`)
203
+ )
204
+ )
205
+
206
+ Effect.runPromise(processEach)
207
+ // Logs: Processing: 1, Processing: 2, etc.
208
+
209
+ // ============================================
210
+ // runDrain - Run for side effects only
211
+ // ============================================
212
+
213
+ const withSideEffects = numbers.pipe(
214
+ Stream.tap((n) => Effect.log(`Saw: ${n}`)),
215
+ Stream.runDrain // Discard values, just run
216
+ )
217
+
218
+ // ============================================
219
+ // runHead - Get first value only
220
+ // ============================================
221
+
222
+ const getFirst = numbers.pipe(
223
+ Stream.runHead
224
+ )
225
+
226
+ Effect.runPromise(getFirst).then((option) => {
227
+ if (Option.isSome(option)) {
228
+ console.log(`First: ${option.value}`) // First: 1
229
+ }
230
+ })
231
+
232
+ // ============================================
233
+ // runLast - Get last value only
234
+ // ============================================
235
+
236
+ const getLast = numbers.pipe(
237
+ Stream.runLast
238
+ )
239
+
240
+ Effect.runPromise(getLast).then((option) => {
241
+ if (Option.isSome(option)) {
242
+ console.log(`Last: ${option.value}`) // Last: 5
243
+ }
244
+ })
245
+
246
+ // ============================================
247
+ // runFold - Accumulate into single result
248
+ // ============================================
249
+
250
+ const sum = numbers.pipe(
251
+ Stream.runFold(0, (acc, n) => acc + n)
252
+ )
253
+
254
+ Effect.runPromise(sum).then((total) => {
255
+ console.log(`Sum: ${total}`) // Sum: 15
256
+ })
257
+
258
+ // ============================================
259
+ // runCount - Count elements
260
+ // ============================================
261
+
262
+ const count = numbers.pipe(Stream.runCount)
263
+
264
+ Effect.runPromise(count).then((n) => {
265
+ console.log(`Count: ${n}`) // Count: 5
266
+ })
267
+ ```
268
+
269
+ **Rationale:**
270
+
271
+ Streams are lazy - nothing happens until you run them. Choose your run method based on what you need: all results, per-item effects, or just completion.
272
+
273
+ ---
274
+
275
+
276
+ Effect provides several ways to consume a stream, each optimized for different use cases:
277
+
278
+ | Method | Returns | Use When |
279
+ |--------|---------|----------|
280
+ | **runCollect** | `Chunk<A>` | Need all results in memory |
281
+ | **runForEach** | `void` | Process each item for side effects |
282
+ | **runDrain** | `void` | Run for side effects, ignore values |
283
+ | **runHead** | `Option<A>` | Only need first value |
284
+ | **runLast** | `Option<A>` | Only need last value |
285
+ | **runFold** | `S` | Accumulate into single result |
286
+
287
+ ---
288
+
289
+ ---
290
+
291
+ ### Take and Drop Stream Elements
292
+
293
+ **Rule:** Use take/drop to control stream size, takeWhile/dropWhile for conditional limits.
294
+
295
+ **Good Example:**
296
+
297
+ ```typescript
298
+ import { Effect, Stream } from "effect"
299
+
300
+ const numbers = Stream.make(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
301
+
302
+ // ============================================
303
+ // take - Get first N elements
304
+ // ============================================
305
+
306
+ const firstThree = numbers.pipe(
307
+ Stream.take(3),
308
+ Stream.runCollect
309
+ )
310
+
311
+ Effect.runPromise(firstThree).then((chunk) => {
312
+ console.log([...chunk]) // [1, 2, 3]
313
+ })
314
+
315
+ // ============================================
316
+ // drop - Skip first N elements
317
+ // ============================================
318
+
319
+ const skipThree = numbers.pipe(
320
+ Stream.drop(3),
321
+ Stream.runCollect
322
+ )
323
+
324
+ Effect.runPromise(skipThree).then((chunk) => {
325
+ console.log([...chunk]) // [4, 5, 6, 7, 8, 9, 10]
326
+ })
327
+
328
+ // ============================================
329
+ // Combine for pagination (skip + limit)
330
+ // ============================================
331
+
332
+ const page2 = numbers.pipe(
333
+ Stream.drop(3), // Skip first page
334
+ Stream.take(3), // Take second page
335
+ Stream.runCollect
336
+ )
337
+
338
+ Effect.runPromise(page2).then((chunk) => {
339
+ console.log([...chunk]) // [4, 5, 6]
340
+ })
341
+
342
+ // ============================================
343
+ // takeWhile - Take while condition is true
344
+ // ============================================
345
+
346
+ const untilFive = numbers.pipe(
347
+ Stream.takeWhile((n) => n < 5),
348
+ Stream.runCollect
349
+ )
350
+
351
+ Effect.runPromise(untilFive).then((chunk) => {
352
+ console.log([...chunk]) // [1, 2, 3, 4]
353
+ })
354
+
355
+ // ============================================
356
+ // dropWhile - Skip while condition is true
357
+ // ============================================
358
+
359
+ const afterFive = numbers.pipe(
360
+ Stream.dropWhile((n) => n < 5),
361
+ Stream.runCollect
362
+ )
363
+
364
+ Effect.runPromise(afterFive).then((chunk) => {
365
+ console.log([...chunk]) // [5, 6, 7, 8, 9, 10]
366
+ })
367
+
368
+ // ============================================
369
+ // takeUntil - Take until condition becomes true
370
+ // ============================================
371
+
372
+ const untilSix = numbers.pipe(
373
+ Stream.takeUntil((n) => n === 6),
374
+ Stream.runCollect
375
+ )
376
+
377
+ Effect.runPromise(untilSix).then((chunk) => {
378
+ console.log([...chunk]) // [1, 2, 3, 4, 5, 6]
379
+ })
380
+
381
+ // ============================================
382
+ // Practical: Process file with header
383
+ // ============================================
384
+
385
+ const fileLines = Stream.make(
386
+ "# Header",
387
+ "# Comment",
388
+ "data1",
389
+ "data2",
390
+ "data3"
391
+ )
392
+
393
+ const dataOnly = fileLines.pipe(
394
+ Stream.dropWhile((line) => line.startsWith("#")),
395
+ Stream.runCollect
396
+ )
397
+
398
+ Effect.runPromise(dataOnly).then((chunk) => {
399
+ console.log([...chunk]) // ["data1", "data2", "data3"]
400
+ })
401
+ ```
402
+
403
+ **Rationale:**
404
+
405
+ Use `take` to limit how many elements to process. Use `drop` to skip elements. Add `While` variants for condition-based limits.
406
+
407
+ ---
408
+
409
+
410
+ Streams can be infinite or very large. These operators let you:
411
+
412
+ 1. **Limit processing** - Only take what you need
413
+ 2. **Skip headers** - Drop first N elements
414
+ 3. **Conditional limits** - Take/drop based on predicates
415
+ 4. **Pagination** - Implement skip/limit patterns
416
+
417
+ ---
418
+
419
+ ---
420
+
421
+