@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.
- package/DOJO.md +22 -0
- package/dojo.json +50 -0
- package/katas/001-hello-effect/SENSEI.md +72 -0
- package/katas/001-hello-effect/solution.test.ts +35 -0
- package/katas/001-hello-effect/solution.ts +16 -0
- package/katas/002-transform-with-map/SENSEI.md +72 -0
- package/katas/002-transform-with-map/solution.test.ts +33 -0
- package/katas/002-transform-with-map/solution.ts +16 -0
- package/katas/003-generator-pipelines/SENSEI.md +72 -0
- package/katas/003-generator-pipelines/solution.test.ts +40 -0
- package/katas/003-generator-pipelines/solution.ts +29 -0
- package/katas/004-flatmap-and-chaining/SENSEI.md +80 -0
- package/katas/004-flatmap-and-chaining/solution.test.ts +34 -0
- package/katas/004-flatmap-and-chaining/solution.ts +18 -0
- package/katas/005-pipe-composition/SENSEI.md +81 -0
- package/katas/005-pipe-composition/solution.test.ts +41 -0
- package/katas/005-pipe-composition/solution.ts +19 -0
- package/katas/006-handle-errors/SENSEI.md +86 -0
- package/katas/006-handle-errors/solution.test.ts +53 -0
- package/katas/006-handle-errors/solution.ts +30 -0
- package/katas/007-tagged-errors/SENSEI.md +79 -0
- package/katas/007-tagged-errors/solution.test.ts +82 -0
- package/katas/007-tagged-errors/solution.ts +37 -0
- package/katas/008-error-patterns/SENSEI.md +89 -0
- package/katas/008-error-patterns/solution.test.ts +41 -0
- package/katas/008-error-patterns/solution.ts +38 -0
- package/katas/009-option-type/SENSEI.md +96 -0
- package/katas/009-option-type/solution.test.ts +49 -0
- package/katas/009-option-type/solution.ts +26 -0
- package/katas/010-either-and-exit/SENSEI.md +86 -0
- package/katas/010-either-and-exit/solution.test.ts +33 -0
- package/katas/010-either-and-exit/solution.ts +17 -0
- package/katas/011-services-and-context/SENSEI.md +82 -0
- package/katas/011-services-and-context/solution.test.ts +23 -0
- package/katas/011-services-and-context/solution.ts +17 -0
- package/katas/012-layers/SENSEI.md +73 -0
- package/katas/012-layers/solution.test.ts +23 -0
- package/katas/012-layers/solution.ts +26 -0
- package/katas/013-testing-effects/SENSEI.md +88 -0
- package/katas/013-testing-effects/solution.test.ts +41 -0
- package/katas/013-testing-effects/solution.ts +20 -0
- package/katas/014-schema-basics/SENSEI.md +81 -0
- package/katas/014-schema-basics/solution.test.ts +35 -0
- package/katas/014-schema-basics/solution.ts +25 -0
- package/katas/015-domain-modeling/SENSEI.md +85 -0
- package/katas/015-domain-modeling/solution.test.ts +46 -0
- package/katas/015-domain-modeling/solution.ts +42 -0
- package/katas/016-retry-and-schedule/SENSEI.md +72 -0
- package/katas/016-retry-and-schedule/solution.test.ts +26 -0
- package/katas/016-retry-and-schedule/solution.ts +23 -0
- package/katas/017-parallel-effects/SENSEI.md +70 -0
- package/katas/017-parallel-effects/solution.test.ts +33 -0
- package/katas/017-parallel-effects/solution.ts +17 -0
- package/katas/018-race-and-timeout/SENSEI.md +75 -0
- package/katas/018-race-and-timeout/solution.test.ts +30 -0
- package/katas/018-race-and-timeout/solution.ts +27 -0
- package/katas/019-ref-and-state/SENSEI.md +72 -0
- package/katas/019-ref-and-state/solution.test.ts +29 -0
- package/katas/019-ref-and-state/solution.ts +16 -0
- package/katas/020-fibers/SENSEI.md +80 -0
- package/katas/020-fibers/solution.test.ts +23 -0
- package/katas/020-fibers/solution.ts +23 -0
- package/katas/021-acquire-release/SENSEI.md +57 -0
- package/katas/021-acquire-release/solution.test.ts +23 -0
- package/katas/021-acquire-release/solution.ts +22 -0
- package/katas/022-scoped-layers/SENSEI.md +52 -0
- package/katas/022-scoped-layers/solution.test.ts +35 -0
- package/katas/022-scoped-layers/solution.ts +19 -0
- package/katas/023-resource-patterns/SENSEI.md +52 -0
- package/katas/023-resource-patterns/solution.test.ts +20 -0
- package/katas/023-resource-patterns/solution.ts +13 -0
- package/katas/024-streams-basics/SENSEI.md +61 -0
- package/katas/024-streams-basics/solution.test.ts +30 -0
- package/katas/024-streams-basics/solution.ts +16 -0
- package/katas/025-stream-operations/SENSEI.md +59 -0
- package/katas/025-stream-operations/solution.test.ts +26 -0
- package/katas/025-stream-operations/solution.ts +17 -0
- package/katas/026-combining-streams/SENSEI.md +54 -0
- package/katas/026-combining-streams/solution.test.ts +20 -0
- package/katas/026-combining-streams/solution.ts +16 -0
- package/katas/027-data-pipelines/SENSEI.md +58 -0
- package/katas/027-data-pipelines/solution.test.ts +22 -0
- package/katas/027-data-pipelines/solution.ts +16 -0
- package/katas/028-logging-and-spans/SENSEI.md +58 -0
- package/katas/028-logging-and-spans/solution.test.ts +50 -0
- package/katas/028-logging-and-spans/solution.ts +20 -0
- package/katas/029-http-client/SENSEI.md +59 -0
- package/katas/029-http-client/solution.test.ts +49 -0
- package/katas/029-http-client/solution.ts +24 -0
- package/katas/030-capstone/SENSEI.md +63 -0
- package/katas/030-capstone/solution.test.ts +67 -0
- package/katas/030-capstone/solution.ts +55 -0
- package/katas/031-config-and-environment/SENSEI.md +77 -0
- package/katas/031-config-and-environment/solution.test.ts +38 -0
- package/katas/031-config-and-environment/solution.ts +11 -0
- package/katas/032-cause-and-defects/SENSEI.md +90 -0
- package/katas/032-cause-and-defects/solution.test.ts +50 -0
- package/katas/032-cause-and-defects/solution.ts +23 -0
- package/katas/033-pattern-matching/SENSEI.md +86 -0
- package/katas/033-pattern-matching/solution.test.ts +36 -0
- package/katas/033-pattern-matching/solution.ts +28 -0
- package/katas/034-deferred-and-coordination/SENSEI.md +85 -0
- package/katas/034-deferred-and-coordination/solution.test.ts +25 -0
- package/katas/034-deferred-and-coordination/solution.ts +24 -0
- package/katas/035-queue-and-backpressure/SENSEI.md +100 -0
- package/katas/035-queue-and-backpressure/solution.test.ts +25 -0
- package/katas/035-queue-and-backpressure/solution.ts +21 -0
- package/katas/036-schema-advanced/SENSEI.md +81 -0
- package/katas/036-schema-advanced/solution.test.ts +55 -0
- package/katas/036-schema-advanced/solution.ts +19 -0
- package/katas/037-cache-and-memoization/SENSEI.md +73 -0
- package/katas/037-cache-and-memoization/solution.test.ts +47 -0
- package/katas/037-cache-and-memoization/solution.ts +24 -0
- package/katas/038-metrics/SENSEI.md +91 -0
- package/katas/038-metrics/solution.test.ts +39 -0
- package/katas/038-metrics/solution.ts +23 -0
- package/katas/039-managed-runtime/SENSEI.md +75 -0
- package/katas/039-managed-runtime/solution.test.ts +29 -0
- package/katas/039-managed-runtime/solution.ts +19 -0
- package/katas/040-request-batching/SENSEI.md +87 -0
- package/katas/040-request-batching/solution.test.ts +56 -0
- package/katas/040-request-batching/solution.ts +32 -0
- package/package.json +22 -0
- package/skills/effect-patterns-building-apis/SKILL.md +2393 -0
- package/skills/effect-patterns-building-data-pipelines/SKILL.md +1876 -0
- package/skills/effect-patterns-concurrency/SKILL.md +2999 -0
- package/skills/effect-patterns-concurrency-getting-started/SKILL.md +351 -0
- package/skills/effect-patterns-core-concepts/SKILL.md +3199 -0
- package/skills/effect-patterns-domain-modeling/SKILL.md +1385 -0
- package/skills/effect-patterns-error-handling/SKILL.md +1212 -0
- package/skills/effect-patterns-error-handling-resilience/SKILL.md +179 -0
- package/skills/effect-patterns-error-management/SKILL.md +1668 -0
- package/skills/effect-patterns-getting-started/SKILL.md +237 -0
- package/skills/effect-patterns-making-http-requests/SKILL.md +1756 -0
- package/skills/effect-patterns-observability/SKILL.md +1586 -0
- package/skills/effect-patterns-platform/SKILL.md +1195 -0
- package/skills/effect-patterns-platform-getting-started/SKILL.md +179 -0
- package/skills/effect-patterns-project-setup--execution/SKILL.md +233 -0
- package/skills/effect-patterns-resource-management/SKILL.md +827 -0
- package/skills/effect-patterns-scheduling/SKILL.md +451 -0
- package/skills/effect-patterns-scheduling-periodic-tasks/SKILL.md +763 -0
- package/skills/effect-patterns-streams/SKILL.md +2052 -0
- package/skills/effect-patterns-streams-getting-started/SKILL.md +421 -0
- package/skills/effect-patterns-streams-sinks/SKILL.md +1181 -0
- package/skills/effect-patterns-testing/SKILL.md +1632 -0
- package/skills/effect-patterns-tooling-and-debugging/SKILL.md +1125 -0
- package/skills/effect-patterns-value-handling/SKILL.md +676 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +3 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: effect-patterns-platform-getting-started
|
|
3
|
+
description: Effect-TS patterns for Platform Getting Started. Use when working with platform getting started in Effect-TS applications.
|
|
4
|
+
---
|
|
5
|
+
# Effect-TS Patterns: Platform Getting Started
|
|
6
|
+
This skill provides 2 curated Effect-TS patterns for platform getting started.
|
|
7
|
+
Use this skill when working on tasks related to:
|
|
8
|
+
- platform 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 Platform Operation
|
|
17
|
+
|
|
18
|
+
**Rule:** Use @effect/platform for cross-platform system operations with Effect integration.
|
|
19
|
+
|
|
20
|
+
**Good Example:**
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { Effect } from "effect"
|
|
24
|
+
import { FileSystem } from "@effect/platform"
|
|
25
|
+
import { NodeContext, NodeRuntime } from "@effect/platform-node"
|
|
26
|
+
|
|
27
|
+
// Read a file - returns Effect<string, PlatformError>
|
|
28
|
+
const readConfig = Effect.gen(function* () {
|
|
29
|
+
const fs = yield* FileSystem.FileSystem
|
|
30
|
+
|
|
31
|
+
// Read file as UTF-8 string
|
|
32
|
+
const content = yield* fs.readFileString("./config.json")
|
|
33
|
+
|
|
34
|
+
return JSON.parse(content)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// Write a file
|
|
38
|
+
const writeLog = Effect.gen(function* () {
|
|
39
|
+
const fs = yield* FileSystem.FileSystem
|
|
40
|
+
|
|
41
|
+
yield* fs.writeFileString(
|
|
42
|
+
"./app.log",
|
|
43
|
+
`Started at ${new Date().toISOString()}\n`
|
|
44
|
+
)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// Combine operations
|
|
48
|
+
const program = Effect.gen(function* () {
|
|
49
|
+
const config = yield* readConfig
|
|
50
|
+
yield* Effect.log(`Loaded config: ${config.appName}`)
|
|
51
|
+
|
|
52
|
+
yield* writeLog
|
|
53
|
+
yield* Effect.log("Log file created")
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
// Run with Node.js platform
|
|
57
|
+
program.pipe(
|
|
58
|
+
Effect.provide(NodeContext.layer),
|
|
59
|
+
NodeRuntime.runMain
|
|
60
|
+
)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Rationale:**
|
|
64
|
+
|
|
65
|
+
Effect Platform provides type-safe, cross-platform system operations. Use `@effect/platform-node` for Node.js or `@effect/platform-bun` for Bun.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
Platform wraps system operations in Effect, giving you:
|
|
71
|
+
|
|
72
|
+
1. **Type safety** - File operations return `Effect<Content, PlatformError>`
|
|
73
|
+
2. **Resource management** - Files are automatically closed
|
|
74
|
+
3. **Cross-platform** - Same code works on Node.js, Bun, browser
|
|
75
|
+
4. **Composability** - Chain file ops with other effects
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
### Access Environment Variables
|
|
82
|
+
|
|
83
|
+
**Rule:** Use Effect to access environment variables with proper error handling.
|
|
84
|
+
|
|
85
|
+
**Good Example:**
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { Effect, Config, Option } from "effect"
|
|
89
|
+
|
|
90
|
+
// ============================================
|
|
91
|
+
// BASIC: Read required variable
|
|
92
|
+
// ============================================
|
|
93
|
+
|
|
94
|
+
const getApiKey = Config.string("API_KEY")
|
|
95
|
+
|
|
96
|
+
const program1 = Effect.gen(function* () {
|
|
97
|
+
const apiKey = yield* getApiKey
|
|
98
|
+
yield* Effect.log(`API Key: ${apiKey.slice(0, 4)}...`)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
// ============================================
|
|
102
|
+
// OPTIONAL: With default value
|
|
103
|
+
// ============================================
|
|
104
|
+
|
|
105
|
+
const getPort = Config.number("PORT").pipe(
|
|
106
|
+
Config.withDefault(3000)
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
const program2 = Effect.gen(function* () {
|
|
110
|
+
const port = yield* getPort
|
|
111
|
+
yield* Effect.log(`Server will run on port ${port}`)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
// ============================================
|
|
115
|
+
// OPTIONAL: Return Option instead of failing
|
|
116
|
+
// ============================================
|
|
117
|
+
|
|
118
|
+
const getOptionalFeature = Config.string("FEATURE_FLAG").pipe(
|
|
119
|
+
Config.option
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
const program3 = Effect.gen(function* () {
|
|
123
|
+
const feature = yield* getOptionalFeature
|
|
124
|
+
|
|
125
|
+
if (Option.isSome(feature)) {
|
|
126
|
+
yield* Effect.log(`Feature enabled: ${feature.value}`)
|
|
127
|
+
} else {
|
|
128
|
+
yield* Effect.log("Feature flag not set")
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
// ============================================
|
|
133
|
+
// COMBINED: Multiple variables as config object
|
|
134
|
+
// ============================================
|
|
135
|
+
|
|
136
|
+
const AppConfig = Config.all({
|
|
137
|
+
apiKey: Config.string("API_KEY"),
|
|
138
|
+
apiUrl: Config.string("API_URL"),
|
|
139
|
+
port: Config.number("PORT").pipe(Config.withDefault(3000)),
|
|
140
|
+
debug: Config.boolean("DEBUG").pipe(Config.withDefault(false)),
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
const program4 = Effect.gen(function* () {
|
|
144
|
+
const config = yield* AppConfig
|
|
145
|
+
|
|
146
|
+
yield* Effect.log(`API URL: ${config.apiUrl}`)
|
|
147
|
+
yield* Effect.log(`Port: ${config.port}`)
|
|
148
|
+
yield* Effect.log(`Debug: ${config.debug}`)
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
// ============================================
|
|
152
|
+
// RUN: Will fail if required vars missing
|
|
153
|
+
// ============================================
|
|
154
|
+
|
|
155
|
+
Effect.runPromise(program4).catch((error) => {
|
|
156
|
+
console.error("Missing required environment variables")
|
|
157
|
+
console.error(error)
|
|
158
|
+
})
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Rationale:**
|
|
162
|
+
|
|
163
|
+
Access environment variables using Effect's built-in functions or Platform's environment service for type-safe configuration.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
Environment variables can be missing or malformed. Effect helps you:
|
|
169
|
+
|
|
170
|
+
1. **Handle missing vars** - Return `Option` or fail with typed error
|
|
171
|
+
2. **Validate values** - Parse and validate with Schema
|
|
172
|
+
3. **Provide defaults** - Fallback values when vars are missing
|
|
173
|
+
4. **Document requirements** - Types show what's needed
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: effect-patterns-project-setup--execution
|
|
3
|
+
description: Effect-TS patterns for Project Setup Execution. Use when working with project setup execution in Effect-TS applications.
|
|
4
|
+
---
|
|
5
|
+
# Effect-TS Patterns: Project Setup Execution
|
|
6
|
+
This skill provides 4 curated Effect-TS patterns for project setup execution.
|
|
7
|
+
Use this skill when working on tasks related to:
|
|
8
|
+
- project setup execution
|
|
9
|
+
- Best practices in Effect-TS applications
|
|
10
|
+
- Real-world patterns and solutions
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 🟢 Beginner Patterns
|
|
15
|
+
|
|
16
|
+
### Execute Synchronous Effects with Effect.runSync
|
|
17
|
+
|
|
18
|
+
**Rule:** Execute synchronous effects with Effect.runSync.
|
|
19
|
+
|
|
20
|
+
**Good Example:**
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { Effect } from "effect";
|
|
24
|
+
|
|
25
|
+
// Simple synchronous program
|
|
26
|
+
const program1 = Effect.gen(function* () {
|
|
27
|
+
const n = 10;
|
|
28
|
+
const result = n * 2;
|
|
29
|
+
yield* Effect.log(`Simple program result: ${result}`);
|
|
30
|
+
return result;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Run simple program
|
|
34
|
+
Effect.runSync(program1);
|
|
35
|
+
|
|
36
|
+
// Program with logging
|
|
37
|
+
const program2 = Effect.gen(function* () {
|
|
38
|
+
yield* Effect.logInfo("Starting calculation...");
|
|
39
|
+
const n = yield* Effect.sync(() => 10);
|
|
40
|
+
yield* Effect.logInfo(`Got number: ${n}`);
|
|
41
|
+
const result = yield* Effect.sync(() => n * 2);
|
|
42
|
+
yield* Effect.logInfo(`Result: ${result}`);
|
|
43
|
+
return result;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Run with logging
|
|
47
|
+
Effect.runSync(program2);
|
|
48
|
+
|
|
49
|
+
// Program with error handling
|
|
50
|
+
const program3 = Effect.gen(function* () {
|
|
51
|
+
yield* Effect.logInfo("Starting division...");
|
|
52
|
+
const n = yield* Effect.sync(() => 10);
|
|
53
|
+
const divisor = yield* Effect.sync(() => 0);
|
|
54
|
+
|
|
55
|
+
yield* Effect.logInfo(`Attempting to divide ${n} by ${divisor}...`);
|
|
56
|
+
return yield* Effect.try({
|
|
57
|
+
try: () => {
|
|
58
|
+
if (divisor === 0) throw new Error("Cannot divide by zero");
|
|
59
|
+
return n / divisor;
|
|
60
|
+
},
|
|
61
|
+
catch: (error) => {
|
|
62
|
+
if (error instanceof Error) {
|
|
63
|
+
return error;
|
|
64
|
+
}
|
|
65
|
+
return new Error("Unknown error occurred");
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}).pipe(
|
|
69
|
+
Effect.catchAll((error) => Effect.logInfo(`Error occurred: ${error.message}`))
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Run with error handling
|
|
73
|
+
Effect.runSync(program3);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Explanation:**
|
|
77
|
+
Use `runSync` only for Effects that are fully synchronous. If the Effect
|
|
78
|
+
contains async code, use `runPromise` instead.
|
|
79
|
+
|
|
80
|
+
**Anti-Pattern:**
|
|
81
|
+
|
|
82
|
+
Do not use `runSync` on an Effect that contains asynchronous operations like
|
|
83
|
+
`Effect.delay` or `Effect.promise`. This will result in a runtime error.
|
|
84
|
+
|
|
85
|
+
**Rationale:**
|
|
86
|
+
|
|
87
|
+
To execute an `Effect` that is guaranteed to be synchronous, use
|
|
88
|
+
`Effect.runSync`. This will return the success value directly or throw the
|
|
89
|
+
error.
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
`Effect.runSync` is an optimized runner for Effects that don't involve any
|
|
93
|
+
asynchronous operations. If the Effect contains any async operations,
|
|
94
|
+
`runSync` will throw an error.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
### Execute Asynchronous Effects with Effect.runPromise
|
|
99
|
+
|
|
100
|
+
**Rule:** Execute asynchronous effects with Effect.runPromise.
|
|
101
|
+
|
|
102
|
+
**Good Example:**
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { Effect } from "effect";
|
|
106
|
+
|
|
107
|
+
const program = Effect.succeed("Hello, World!").pipe(Effect.delay("1 second"));
|
|
108
|
+
|
|
109
|
+
const promise = Effect.runPromise(program);
|
|
110
|
+
|
|
111
|
+
const programWithLogging = Effect.gen(function* () {
|
|
112
|
+
const result = yield* program;
|
|
113
|
+
yield* Effect.log(result); // Logs "Hello, World!" after 1 second.
|
|
114
|
+
return result;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
Effect.runPromise(programWithLogging);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Explanation:**
|
|
121
|
+
`Effect.runPromise` executes your effect and returns a Promise, making it
|
|
122
|
+
easy to integrate with existing JavaScript async workflows.
|
|
123
|
+
|
|
124
|
+
**Anti-Pattern:**
|
|
125
|
+
|
|
126
|
+
Never call `runPromise` inside another `Effect` composition. Effects are
|
|
127
|
+
meant to be composed together _before_ being run once at the end.
|
|
128
|
+
|
|
129
|
+
**Rationale:**
|
|
130
|
+
|
|
131
|
+
To execute an `Effect` that may be asynchronous and retrieve its result, use
|
|
132
|
+
`Effect.runPromise`. This should only be done at the outermost layer of your
|
|
133
|
+
application.
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
`Effect.runPromise` is the bridge from the Effect world to the Promise-based
|
|
137
|
+
world of Node.js and browsers. If the Effect succeeds, the Promise resolves;
|
|
138
|
+
if it fails, the Promise rejects.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
### Set Up a New Effect Project
|
|
143
|
+
|
|
144
|
+
**Rule:** Set up a new Effect project.
|
|
145
|
+
|
|
146
|
+
**Good Example:**
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
// 1. Init project (e.g., `npm init -y`)
|
|
150
|
+
// 2. Install deps (e.g., `npm install effect`, `npm install -D typescript tsx`)
|
|
151
|
+
// 3. Create tsconfig.json with `"strict": true`
|
|
152
|
+
// 4. Create src/index.ts
|
|
153
|
+
import { Effect } from "effect";
|
|
154
|
+
|
|
155
|
+
const program = Effect.log("Hello, World!");
|
|
156
|
+
|
|
157
|
+
Effect.runSync(program);
|
|
158
|
+
|
|
159
|
+
// 5. Run the program (e.g., `npx tsx src/index.ts`)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Explanation:**
|
|
163
|
+
This setup ensures you have TypeScript and Effect ready to go, with strict
|
|
164
|
+
type-checking for maximum safety and correctness.
|
|
165
|
+
|
|
166
|
+
**Anti-Pattern:**
|
|
167
|
+
|
|
168
|
+
Avoid disabling `strict` mode in your `tsconfig.json`. Running with
|
|
169
|
+
`"strict": false` will cause you to lose many of the type-safety guarantees
|
|
170
|
+
that make Effect so powerful.
|
|
171
|
+
|
|
172
|
+
**Rationale:**
|
|
173
|
+
|
|
174
|
+
To start a new Effect project, initialize a standard Node.js project, add
|
|
175
|
+
`effect` and `typescript` as dependencies, and create a `tsconfig.json` file
|
|
176
|
+
with strict mode enabled.
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
A proper setup is crucial for leveraging Effect's powerful type-safety
|
|
180
|
+
features. Using TypeScript's `strict` mode is non-negotiable.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
## 🟠Advanced Patterns
|
|
186
|
+
|
|
187
|
+
### Create a Reusable Runtime from Layers
|
|
188
|
+
|
|
189
|
+
**Rule:** Create a reusable runtime from layers.
|
|
190
|
+
|
|
191
|
+
**Good Example:**
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
import { Effect, Layer, Runtime } from "effect";
|
|
195
|
+
|
|
196
|
+
class GreeterService extends Effect.Service<GreeterService>()("Greeter", {
|
|
197
|
+
sync: () => ({
|
|
198
|
+
greet: (name: string) => Effect.sync(() => `Hello ${name}`),
|
|
199
|
+
}),
|
|
200
|
+
}) {}
|
|
201
|
+
|
|
202
|
+
const runtime = Effect.runSync(
|
|
203
|
+
Layer.toRuntime(GreeterService.Default).pipe(Effect.scoped)
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
// In a server, you would reuse `run` for every request.
|
|
207
|
+
Runtime.runPromise(runtime)(Effect.log("Hello"));
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Explanation:**
|
|
211
|
+
By compiling your layers into a Runtime once, you avoid rebuilding the
|
|
212
|
+
dependency graph for every effect execution.
|
|
213
|
+
|
|
214
|
+
**Anti-Pattern:**
|
|
215
|
+
|
|
216
|
+
For a long-running application, avoid providing layers and running an effect
|
|
217
|
+
in a single operation. This forces Effect to rebuild the dependency graph on
|
|
218
|
+
every execution.
|
|
219
|
+
|
|
220
|
+
**Rationale:**
|
|
221
|
+
|
|
222
|
+
For applications that need to run multiple effects (e.g., a web server), use
|
|
223
|
+
`Layer.toRuntime(appLayer)` to compile your dependency graph into a single,
|
|
224
|
+
reusable `Runtime` object.
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
Building the dependency graph from layers has a one-time cost. Creating a
|
|
228
|
+
`Runtime` once when your application starts is highly efficient for
|
|
229
|
+
long-running applications.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
|