@danieljvdm/dev-kit 0.11.3 → 0.13.0

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 (79) hide show
  1. package/README.md +111 -78
  2. package/dev-kit.example.jsonc +0 -4
  3. package/package.json +10 -6
  4. package/schema/dev-kit.schema.json +1 -46
  5. package/skills/build-effect-apis/SKILL.md +77 -0
  6. package/skills/build-effect-apis/agents/openai.yaml +4 -0
  7. package/skills/build-effect-apis/references/cloudflare-workers.md +71 -0
  8. package/skills/build-effect-apis/references/effect-atom-client.md +161 -0
  9. package/skills/build-effect-apis/references/effect-atom-lifecycle.md +78 -0
  10. package/skills/build-effect-apis/references/effect-atom-testing.md +74 -0
  11. package/skills/build-effect-apis/references/runtime-assembly.md +56 -0
  12. package/skills/build-effect-apis/references/server-and-middleware.md +174 -0
  13. package/skills/build-effect-apis/references/shared-contracts.md +108 -0
  14. package/skills/build-effect-apis/references/tanstack-start.md +86 -0
  15. package/skills/build-effect-apis/references/verification.md +50 -0
  16. package/skills/build-effect-clis/SKILL.md +61 -0
  17. package/skills/build-effect-clis/agents/openai.yaml +4 -0
  18. package/skills/build-effect-clis/references/command-design.md +93 -0
  19. package/skills/build-effect-clis/references/entrypoints-and-testing.md +59 -0
  20. package/skills/build-effect-clis/references/processes-and-platform.md +73 -0
  21. package/skills/dev-kit/SKILL.md +77 -48
  22. package/skills/effect-architecture-audit/SKILL.md +26 -0
  23. package/skills/effect-architecture-audit/agents/openai.yaml +4 -0
  24. package/skills/effect-architecture-audit/references/service-and-boundary-audit.md +150 -0
  25. package/skills/effect-ts/SKILL.md +21 -256
  26. package/skills/effect-ts/agents/openai.yaml +3 -3
  27. package/skills/testing/SKILL.md +5 -0
  28. package/src/catalog-manager.ts +16 -17
  29. package/src/catalog.ts +71 -16
  30. package/src/effect-source.ts +46 -24
  31. package/src/effect-tsgo.ts +86 -24
  32. package/src/gitignore.ts +5 -5
  33. package/src/index.ts +3 -6
  34. package/src/manifest.ts +0 -34
  35. package/src/node-symbolic-link.ts +2 -2
  36. package/src/oxfmt.js +5 -0
  37. package/src/oxfmt.ts +5 -0
  38. package/src/oxlint.js +5 -0
  39. package/src/oxlint.ts +5 -0
  40. package/src/package-skill-source.ts +51 -59
  41. package/src/path-digest.ts +7 -7
  42. package/src/project-package.ts +8 -7
  43. package/src/project-process-lock.ts +17 -12
  44. package/src/project-state.ts +1 -1
  45. package/src/skill-manager.ts +18 -16
  46. package/src/skill-selector.ts +12 -0
  47. package/src/sync.ts +181 -131
  48. package/src/tool-ignore-patterns.js +9 -0
  49. package/src/tool-ignore-patterns.ts +15 -0
  50. package/src/vendor.ts +67 -61
  51. package/src/vite-plus-dependency.ts +10 -11
  52. package/src/vite-plus-hooks.ts +24 -14
  53. package/src/vite-plus-quality.ts +21 -172
  54. package/src/vite-plus.js +81 -0
  55. package/src/vite-plus.ts +102 -0
  56. package/templates/AGENTS.md +1 -1
  57. package/skills/effect-ts/UPSTREAM.md +0 -28
  58. package/skills/effect-ts/references/atom-cache-lifecycle.md +0 -78
  59. package/skills/effect-ts/references/atom-http-and-invalidation.md +0 -97
  60. package/skills/effect-ts/references/atom-tanstack-start.md +0 -69
  61. package/skills/effect-ts/references/atom-testing.md +0 -67
  62. package/skills/effect-ts/references/audit-services.md +0 -144
  63. package/skills/effect-ts/references/features.md +0 -525
  64. package/skills/effect-ts/references/guide-atom-data-fetching.md +0 -44
  65. package/skills/effect-ts/references/guide-cli.md +0 -107
  66. package/skills/effect-ts/references/guide-datetime.md +0 -72
  67. package/skills/effect-ts/references/guide-effect.md +0 -440
  68. package/skills/effect-ts/references/guide-error-handling.md +0 -565
  69. package/skills/effect-ts/references/guide-http-boundaries.md +0 -55
  70. package/skills/effect-ts/references/guide-layers.md +0 -989
  71. package/skills/effect-ts/references/guide-observability.md +0 -746
  72. package/skills/effect-ts/references/guide-retries.md +0 -434
  73. package/skills/effect-ts/references/guide-schedule.md +0 -343
  74. package/skills/effect-ts/references/guide-schema.md +0 -664
  75. package/skills/effect-ts/references/guide-sql.md +0 -536
  76. package/skills/effect-ts/references/guide-testing.md +0 -532
  77. package/skills/effect-ts/references/guide-type-safety-and-boundaries.md +0 -131
  78. package/skills/effect-ts/references/version-and-source.md +0 -86
  79. package/templates/vite-plus/vite.config.ts +0 -22
@@ -1,107 +0,0 @@
1
- # Effect CLI
2
-
3
- ## Overview
4
-
5
- Build scripts as small Effect programs with typed errors, platform services, and `effect/unstable/cli`. Read nearby scripts first and follow local conventions for imports, package scripts, and validation commands.
6
-
7
- Use the matching general and local guides from `effect-ts` when the CLI also
8
- includes domain models, reusable services, layers, typed domain errors,
9
- structured logging, tests, or HTTP/runtime boundary code.
10
-
11
- ## Workflow
12
-
13
- 1. Read nearby scripts before editing. Prefer the repo's current imports, error classes, command helper names, and package-script style.
14
- 2. Use `effect/unstable/cli` for command shape, descriptions, help, and arguments/options.
15
- 3. Use platform services for effects:
16
- - `@effect/platform-node`: `NodeRuntime`, `NodeServices`
17
- - `@effect/platform-bun`: Bun equivalent only when the project already uses it
18
- - `FileSystem`, `Path`, `Console`, `Terminal`, `ChildProcess`, `Stream` from Effect/platform packages where available
19
- 4. Keep direct `node:*`, Bun globals, `process.argv`, `process.env`, `console.*`, `fs`, `path`, and `child_process` calls inside explicit runtime adapters. Use Effect platform services throughout script logic.
20
- 5. Model expected failures as `Schema.TaggedErrorClass` with useful `message` overrides.
21
- 6. Keep shell execution deterministic: pass command and args as arrays to `ChildProcess.make`, set `cwd` explicitly, capture output, and fail on non-zero exit.
22
- 7. Make interactive flows explicit and pleasant: show a short title, summarize detected state, prompt before ambiguous choices, and print the final command/action before running it.
23
- 8. Wire the command with `CliCommand.run(command, { version: "1.0.0" }).pipe(Effect.scoped, Effect.provide(NodeServices.layer))`, then `NodeRuntime.runMain(program, { disableErrorReporting: true })`.
24
- 9. Update `package.json` scripts to call the TypeScript script through the repo's established runner (`tsx`/`bun`) and keep runner choice consistent across scripts.
25
- 10. Include every executable TypeScript script in one checked TypeScript project. Validate with `bun run check` or the narrow package check plus a direct `--help`/dry-run path for the new command.
26
-
27
- ## Script Skeleton
28
-
29
- Use this shape unless local scripts have a better established variant:
30
-
31
- ```ts
32
- import { NodeRuntime, NodeServices } from "@effect/platform-node";
33
- import { Console, Effect, Path, Schema as S, Stream } from "effect";
34
- import { Command as CliCommand } from "effect/unstable/cli";
35
- import { ChildProcess } from "effect/unstable/process";
36
-
37
- class CommandError extends S.TaggedErrorClass<CommandError>()("CommandError", {
38
- command: S.String,
39
- exitCode: S.Int,
40
- output: S.String,
41
- }) {
42
- override get message() {
43
- return this.output.length > 0
44
- ? `${this.command} exited with code ${this.exitCode}: ${this.output}`
45
- : `${this.command} exited with code ${this.exitCode}`;
46
- }
47
- }
48
-
49
- const runCommand = Effect.fn("runCommand")(function* (
50
- cwd: string,
51
- command: string,
52
- args: ReadonlyArray<string>,
53
- ) {
54
- const formatted = [command, ...args].join(" ");
55
- const child = yield* ChildProcess.make(command, args, { cwd, stderr: "pipe", stdout: "pipe" });
56
- const [output, exitCode] = yield* Effect.all([
57
- Stream.mkString(Stream.decodeText(child.all)),
58
- child.exitCode,
59
- ]);
60
- const trimmed = output.trim();
61
-
62
- if (exitCode !== 0) {
63
- return yield* new CommandError({ command: formatted, exitCode, output: trimmed });
64
- }
65
-
66
- return trimmed;
67
- });
68
-
69
- const main = Effect.gen(function* () {
70
- yield* Console.log("Doing work...");
71
- });
72
-
73
- const command = CliCommand.make("script-name", {}, () => main).pipe(
74
- CliCommand.withDescription("Describe what the script does."),
75
- );
76
-
77
- const program = CliCommand.run(command, { version: "1.0.0" }).pipe(
78
- Effect.scoped,
79
- Effect.provide(NodeServices.layer),
80
- );
81
-
82
- NodeRuntime.runMain(program, { disableErrorReporting: true });
83
- ```
84
-
85
- ## CLI Design Rules
86
-
87
- - Prefer named options over environment variables for normal user input. Keep environment variables only for CI or stable machine-local overrides.
88
- - Prefer deterministic auto-detection when there is exactly one valid candidate; prompt or fail clearly when there are zero or many.
89
- - Print normal interactive UI/status lines to stdout with `Console.log`; use stderr for real errors or for commands that intentionally reserve stdout for machine-readable output.
90
- - For visually nicer output, use simple ASCII structure that degrades well in CI: title lines, aligned labels, and short bullet lists. Avoid decorative Unicode unless the file already uses it.
91
- - Use `CliCommand.withDescription` and ensure `--help` explains the happy path and escape hatches.
92
- - Add a dry-run or help path for scripts that would start long-running servers or device builds.
93
- - Decode structured JSON input with `Schema.fromJsonString(Model)` so parsing
94
- and validation share one schema-owned boundary.
95
-
96
- ## Validation
97
-
98
- Run the narrowest meaningful checks:
99
-
100
- - Confirm that every TypeScript entrypoint named by `package.json` belongs to
101
- one checked TypeScript project.
102
- - The repository's root `check` or typecheck script when the entrypoint belongs
103
- to the root project.
104
- - Package-level `bun run check` when the script lives under an app/package and is included in that TS config.
105
- - The command's `--help` path.
106
- - A dry-run or harmless invalid-input path that loads the entrypoint and
107
- exercises parsing and environment discovery.
@@ -1,72 +0,0 @@
1
- # Effect DateTime
2
-
3
- Prefer Effect `DateTime` over vanilla JavaScript `Date` for application logic. Keep `Date` as an interoperability type at external boundaries, not the domain model.
4
-
5
- ## Workflow
6
-
7
- 1. Inspect the installed `effect` version and `DateTime` source before relying on exact signatures. DateTime APIs are version-sensitive.
8
- 2. Identify whether each value represents an absolute instant, a wall-clock time in a named zone, or only a calendar date. Do not silently collapse these meanings.
9
- 3. Decode external strings, numbers, and `Date` objects at the boundary. Use `Schema.DateTimeUtc`, `Schema.DateTimeUtcFromString`, `Schema.DateTimeUtcFromMillis`, `Schema.DateTimeUtcFromDate`, or `Schema.DateTimeZoned` as appropriate.
10
- 4. Keep domain values as `DateTime.Utc` by default. Use `DateTime.Zoned` when calendar operations or presentation must retain an IANA time zone.
11
- 5. Use `DateTime.now` inside Effect programs so the current time comes from the `Clock` service. Reserve `DateTime.nowUnsafe()` and `Date.now()` for explicit synchronous host boundaries.
12
- 6. Perform comparisons, arithmetic, rounding, and formatting with `DateTime` operations, then convert to `Date` or epoch milliseconds only when an external API requires them.
13
- 7. Test time-dependent behavior with `it.effect` and `TestClock`; advance virtual time instead of sleeping or consulting the live clock.
14
-
15
- ## Construction and boundaries
16
-
17
- - Prefer safe decoding for untrusted input. `DateTime.make` and zoned constructors return `Option`; Schema decoders produce structured parse failures.
18
- - Use `DateTime.makeUnsafe` only for literals and values already validated by the program. Do not turn user input into defects.
19
- - Prefer explicit ISO 8601 strings with offsets at wire boundaries. Avoid implementation-dependent or locale-formatted date strings.
20
- - Do not invent a midnight instant for a genuinely date-only value such as a birthday. Preserve a validated `YYYY-MM-DD` or a domain-specific year/month/day structure until an actual time and zone are chosen.
21
- - Use `DateTime.makeZonedFromString` or `Schema.DateTimeZoned` when a serialized value must preserve its zone.
22
- - Use `DateTime.toDateUtc`, `DateTime.toEpochMillis`, and `DateTime.fromDateUnsafe` only at interop boundaries.
23
-
24
- ```ts
25
- import { DateTime, Effect, Schema } from "effect";
26
-
27
- const Timestamp = Schema.DateTimeUtcFromString;
28
-
29
- const expiresAt = DateTime.makeUnsafe("2030-01-01T00:00:00Z");
30
-
31
- const isExpired = Effect.gen(function* () {
32
- const now = yield* DateTime.now;
33
- return DateTime.isLessThanOrEqualTo(expiresAt, now);
34
- });
35
- ```
36
-
37
- ## Instants, zones, and arithmetic
38
-
39
- - Treat `DateTime.Utc` as an instant without retained zone information. Treat `DateTime.Zoned` as the same kind of instant plus a zone used for wall-clock parts, formatting, and zone-aware transformations.
40
- - Use `DateTime.setZone` or `setZoneNamed` to view the same instant in another zone. When constructing from local wall-clock parts, use `makeZoned` with `adjustForTimeZone: true` and choose a deliberate `disambiguation` policy for DST gaps and repeated times.
41
- - Use `DateTime.addDuration` and `subtractDuration` for elapsed time. Use `DateTime.add` and `subtract` for calendar arithmetic such as days, months, and years; these operations account for a zoned value's calendar rules.
42
- - Use `DateTime.startOf`, `endOf`, or `nearest` rather than hand-editing fields. State `weekStartsOn` when week boundaries are domain-sensitive.
43
- - Use `DateTime.Order`, `Equivalence`, `min`, `max`, `between`, and comparison helpers rather than comparing formatted strings or mutable `Date` objects.
44
- - Use `formatIso` for UTC interchange, `formatIsoZoned` when preserving a zone, and `format` / `formatIntl` for presentation. Do not persist locale-formatted output.
45
-
46
- ## Current time and tests
47
-
48
- Code that asks what time it is must remain clock-driven:
49
-
50
- ```ts
51
- import { assert, it } from "@effect/vitest";
52
- import { DateTime, Duration, Effect } from "effect";
53
- import { TestClock } from "effect/testing";
54
-
55
- it.effect("expires after an hour", () =>
56
- Effect.gen(function* () {
57
- yield* TestClock.setTime(DateTime.toEpochMillis(DateTime.makeUnsafe("2030-01-01T00:00:00Z")));
58
- const startedAt = yield* DateTime.now;
59
-
60
- yield* TestClock.adjust("1 hour");
61
- const now = yield* DateTime.now;
62
-
63
- assert.strictEqual(Duration.toMillis(DateTime.distance(startedAt, now)), 60 * 60 * 1000);
64
- }),
65
- );
66
- ```
67
-
68
- `@effect/vitest` supplies `TestClock` to `it.effect`. Fork work that sleeps, times out, retries, or follows a schedule before advancing the clock. Use `it.live` only when a test intentionally needs real time.
69
-
70
- ## Completion check
71
-
72
- Confirm that domain code uses `DateTime`, untrusted inputs are decoded, UTC versus zoned intent is explicit, DST behavior is deliberate, elapsed and calendar arithmetic are not confused, serialization is stable, and time-dependent tests use `TestClock` without wall-clock sleeps.
@@ -1,440 +0,0 @@
1
- # Effect Guide
2
-
3
- This guide covers general Effect usage, including reusable function boundaries,
4
- Promise interop, composition, provisioning, and runtime execution.
5
-
6
- Key source areas:
7
-
8
- - `packages/effect/src/Effect.ts`
9
- - `packages/tools/`
10
- - `packages/platform-*`
11
- - `packages/opentelemetry/`
12
- - `packages/vitest/`
13
-
14
- ## Mental Model
15
-
16
- `Effect<A, E, R>` is the default way to represent application work.
17
-
18
- It describes a computation that:
19
-
20
- - succeeds with `A`
21
- - fails with `E`
22
- - requires services `R`
23
-
24
- The repo consistently uses `Effect` as the main abstraction for:
25
-
26
- - business workflows
27
- - service methods
28
- - platform integrations
29
- - resource lifecycles
30
- - tests
31
-
32
- ## Most Common Patterns In The Repo
33
-
34
- The dominant usage pattern is:
35
-
36
- 1. use `Effect.gen` for workflows and orchestration
37
- 2. use `Effect.fn` for reusable effectful functions
38
- 3. use precise constructors such as `succeed`, `fail`, `sync`, `try`, and `tryPromise`
39
- 4. use `map`, `flatMap`, and `tap` for local transformations
40
- 5. access services in implementations and `provide*` only at edges
41
- 6. use `acquireRelease` and `scoped` for owned resources
42
- 7. use `catchTag` and `match` for typed recovery
43
- 8. use `run*` only at runtime boundaries
44
-
45
- ## Prefer `Effect.fn` For Reusable Operations
46
-
47
- For reusable effectful operations, prefer `Effect.fn`.
48
-
49
- ```ts
50
- import { Effect } from "effect";
51
-
52
- const loadUser = Effect.fn("loadUser")(function* (userId: string) {
53
- return { id: userId, name: "Ada" };
54
- });
55
- ```
56
-
57
- Use `Effect.fn` when:
58
-
59
- - the operation is reusable
60
- - the operation takes parameters
61
- - the operation is part of business logic or a module API
62
- - you want consistent tracing and stack frames
63
-
64
- Do not treat `Effect.fnUntraced` as the default. If you do not want an explicit named span, use `Effect.fn` without a span name.
65
-
66
- Repo examples:
67
-
68
- - `packages/tools/utils/src/Codegen.ts`
69
- - `packages/tools/openapi-generator/src/OpenApiPatch.ts`
70
-
71
- ## Use `Effect.gen` For Workflows
72
-
73
- Use `Effect.gen` for orchestration and sequential workflows, especially when there are multiple `yield*` steps.
74
-
75
- ```ts
76
- const program = Effect.gen(function* () {
77
- const config = yield* Config;
78
- const repo = yield* UserRepo;
79
- const user = yield* repo.getById("u_123");
80
- return { config, user };
81
- });
82
- ```
83
-
84
- Use `Effect.gen` when:
85
-
86
- - the body is a workflow
87
- - you are reading multiple services
88
- - you have branching or multiple sequential steps
89
- - you are implementing a layer, handler, or orchestration
90
-
91
- Repo examples:
92
-
93
- - `packages/opentelemetry/src/NodeSdk.ts`
94
- - `packages/tools/openapi-generator/src/OpenApiGenerator.ts`
95
-
96
- ## `Effect.fn` vs `Effect.gen`
97
-
98
- Use this rule:
99
-
100
- - reusable operation: `Effect.fn`
101
- - inline workflow block: `Effect.gen`
102
-
103
- Good split:
104
-
105
- ```ts
106
- const loadUser = Effect.fn("loadUser")(function* (userId: string) {
107
- const repo = yield* UserRepo;
108
- return yield* repo.getById(userId);
109
- });
110
-
111
- const program = Effect.gen(function* () {
112
- const user = yield* loadUser("u_123");
113
- yield* Effect.logInfo("loaded user", user);
114
- });
115
- ```
116
-
117
- ## `Effect.fnUntraced` Is An Escape Hatch
118
-
119
- For application and business code, `Effect.fnUntraced` is not the default.
120
-
121
- Use it only when:
122
-
123
- - the function is an internal low-level helper
124
- - observability is intentionally being traded away
125
- - there is a concrete performance or tracing reason
126
-
127
- If the only goal is to avoid an explicit named span, prefer:
128
-
129
- ```ts
130
- const normalizeUser = Effect.fn(function* (input: string) {
131
- return input.trim().toLowerCase();
132
- });
133
- ```
134
-
135
- Instead of:
136
-
137
- ```ts
138
- const normalizeUser = Effect.fnUntraced(function* (input: string) {
139
- return input.trim().toLowerCase();
140
- });
141
- ```
142
-
143
- ## Constructor Functions
144
-
145
- The repo uses constructor functions very deliberately.
146
-
147
- ### `Effect.succeed`
148
-
149
- Use for pure successful values.
150
-
151
- ```ts
152
- const ok = Effect.succeed(42);
153
- ```
154
-
155
- ### `Effect.fail`
156
-
157
- Use for expected typed failures.
158
-
159
- ```ts
160
- const notFound = Effect.fail(UserNotFound.make({ userId: "u_123" }));
161
- ```
162
-
163
- ### `Effect.sync`
164
-
165
- Use for synchronous side effects or pure synchronous construction that should live inside `Effect`.
166
-
167
- ```ts
168
- const buildConfig = Effect.sync(() => ({ retries: 3 }));
169
- ```
170
-
171
- ### `Effect.try`
172
-
173
- Use for synchronous code that may throw.
174
-
175
- ```ts
176
- import { Effect, Schema } from "effect";
177
-
178
- class ParseError extends Schema.TaggedErrorClass<ParseError>()("ParseError", {
179
- cause: Schema.Defect(),
180
- }) {}
181
-
182
- const parseJson = (input: string) =>
183
- Effect.try({
184
- try: () => JSON.parse(input),
185
- catch: (cause) => ParseError.make({ cause }),
186
- });
187
- ```
188
-
189
- ### `Effect.tryPromise`
190
-
191
- Use for external Promise-returning APIs. Translate recoverable rejections into
192
- the concrete typed error owned by that integration boundary, preserving an
193
- opaque cause when it is diagnostically useful. Compose Effect-native APIs
194
- directly instead of converting them through Promise.
195
-
196
- ```ts
197
- import { Effect, Schema } from "effect";
198
-
199
- class FetchError extends Schema.TaggedErrorClass<FetchError>()("FetchError", {
200
- cause: Schema.Defect(),
201
- }) {}
202
-
203
- const fetchText = (url: string) =>
204
- Effect.tryPromise({
205
- try: () => fetch(url).then((response) => response.text()),
206
- catch: (cause) => FetchError.make({ cause }),
207
- });
208
- ```
209
-
210
- Preferred rule:
211
-
212
- - pure value: `succeed`
213
- - expected failure: `fail`
214
- - synchronous non-throwing effect: `sync`
215
- - synchronous throwing boundary: `try`
216
- - Promise boundary: `tryPromise`
217
-
218
- ## Local Composition
219
-
220
- The repo uses `map`, `flatMap`, and `tap` constantly for small local transformations.
221
-
222
- ### `Effect.map`
223
-
224
- Use to transform successful values.
225
-
226
- ```ts
227
- const userName = loadUser("u_123").pipe(Effect.map((user) => user.name));
228
- ```
229
-
230
- ### `Effect.flatMap`
231
-
232
- Use when the next step returns another `Effect`.
233
-
234
- ```ts
235
- const result = loadUser("u_123").pipe(Effect.flatMap((user) => saveAudit(user.id)));
236
- ```
237
-
238
- ### `Effect.tap`
239
-
240
- Use for side effects that should preserve the main value.
241
-
242
- ```ts
243
- const result = loadUser("u_123").pipe(
244
- Effect.tap((user) => Effect.logDebug("loaded user", { userId: user.id })),
245
- );
246
- ```
247
-
248
- Preferred rule:
249
-
250
- - outer workflow: `Effect.gen`
251
- - local transformation: `map`, `flatMap`, `tap`
252
-
253
- ## Services And Provisioning
254
-
255
- Repo style is:
256
-
257
- - access services in implementation code
258
- - provide them at boundaries
259
-
260
- ### Access services in implementations
261
-
262
- ```ts
263
- const loadUser = Effect.fn("loadUser")(function* (userId: string) {
264
- const repo = yield* UserRepo;
265
- return yield* repo.getById(userId);
266
- });
267
- ```
268
-
269
- or:
270
-
271
- ```ts
272
- const loadUser = (userId: string) =>
273
- Effect.service(UserRepo).pipe(Effect.flatMap((repo) => repo.getById(userId)));
274
- ```
275
-
276
- ### Provide at the edge
277
-
278
- ```ts
279
- const program = loadUser("u_123").pipe(Effect.provide(AppLayer));
280
- ```
281
-
282
- Use `provideService` and `provideServiceEffect` for targeted overrides, especially in tests or framework boundaries.
283
-
284
- Do not default to exporting thin accessor functions that just fetch a service and forward to one service method. Prefer real business operations or direct service usage within the owning workflow.
285
-
286
- Repo examples:
287
-
288
- - `packages/tools/utils/src/bin.ts`
289
- - `packages/tools/openapi-generator/test/`
290
-
291
- ## Error Handling
292
-
293
- Common repo patterns:
294
-
295
- - `catchTag` for expected tagged errors
296
- - `match` for totalizing an effect into a value
297
- - `catchCause` for full-cause infra handling
298
-
299
- ### `Effect.catchTag`
300
-
301
- Use for targeted typed recovery.
302
-
303
- ```ts
304
- const safe = loadUser("u_123").pipe(Effect.catchTag("UserNotFound", () => Effect.succeed(null)));
305
- ```
306
-
307
- ### `Effect.match`
308
-
309
- Use when the caller wants a value either way.
310
-
311
- ```ts
312
- const result = loadUser("u_123").pipe(
313
- Effect.match({
314
- onFailure: () => null,
315
- onSuccess: (user) => user,
316
- }),
317
- );
318
- ```
319
-
320
- For deeper guidance, see `./references/guide-error-handling.md`.
321
-
322
- ## Resource Management
323
-
324
- One of the strongest repo patterns is explicit resource ownership.
325
-
326
- ### `Effect.acquireRelease`
327
-
328
- Use for resources that must be cleaned up.
329
-
330
- ```ts
331
- const connection = Effect.acquireRelease(openConnection, (conn) => closeConnection(conn));
332
- ```
333
-
334
- ### `Effect.scoped`
335
-
336
- Use when a workflow consumes scoped resources and should tie cleanup to scope lifetime.
337
-
338
- ```ts
339
- const program = Effect.scoped(
340
- Effect.gen(function* () {
341
- const conn = yield* connection;
342
- return yield* conn.query("select 1");
343
- }),
344
- );
345
- ```
346
-
347
- Repo examples:
348
-
349
- - `packages/platform-node/`
350
- - `packages/opentelemetry/src/NodeSdk.ts`
351
-
352
- ## SQL And Runtime Integrations
353
-
354
- When Effect already provides a domain module for a capability, prefer that module over direct raw runtime client usage in business code.
355
-
356
- Important example:
357
-
358
- - prefer Effect SQL modules from `effect/unstable/sql/*` over embedding a native SQL driver directly in domain services
359
-
360
- Why:
361
-
362
- - transactions, spans, and typed errors stay inside the Effect model
363
- - layering stays cleaner
364
- - migrations and query conventions stay consistent
365
-
366
- For SQL-specific guidance, see `./references/guide-sql.md`.
367
-
368
- ## Observability
369
-
370
- The repo uses observability around meaningful boundaries, not every tiny helper.
371
-
372
- Common patterns:
373
-
374
- - `Effect.fn` for named operations
375
- - `Effect.withSpan` for nested span boundaries
376
- - `Effect.log*` for operational events
377
- - `Effect.track` for metrics
378
-
379
- For detailed guidance, see `./references/guide-observability.md`.
380
-
381
- ## Runtime Boundaries
382
-
383
- The repo keeps `run*` APIs at true runtime boundaries.
384
-
385
- ### `Effect.runPromise`
386
-
387
- Use when leaving Effect world into Promise-based hosts.
388
-
389
- ### `Effect.runFork`
390
-
391
- Use for background fibers or long-running integration hooks.
392
-
393
- ### `Effect.runSync`
394
-
395
- Use sparingly, mostly in specialized internals where synchrony is guaranteed.
396
-
397
- Preferred rule:
398
-
399
- - library/business code should return `Effect`
400
- - entrypoints and integration boundaries should run `Effect`
401
- - `Effect.runPromise` belongs only where a Promise-based host takes ownership
402
- of the Effect program
403
-
404
- If you have multiple external entrypoints, prefer `ManagedRuntime`.
405
-
406
- ## Commonly Used Effect APIs In This Repo
407
-
408
- These are the most practically important `Effect` functions to know first:
409
-
410
- - `Effect.fn`
411
- - `Effect.gen`
412
- - `Effect.succeed`
413
- - `Effect.fail`
414
- - `Effect.sync`
415
- - `Effect.try`
416
- - `Effect.tryPromise`
417
- - `Effect.map`
418
- - `Effect.flatMap`
419
- - `Effect.tap`
420
- - `Effect.service`
421
- - `Effect.provide`
422
- - `Effect.provideService`
423
- - `Effect.catchTag`
424
- - `Effect.match`
425
- - `Effect.acquireRelease`
426
- - `Effect.scoped`
427
- - `Effect.withSpan`
428
- - `Effect.logInfo`
429
- - `Effect.logDebug`
430
- - `Effect.runPromise`
431
-
432
- ## Good Repo Examples To Study
433
-
434
- - `packages/tools/utils/src/Codegen.ts`
435
- - `packages/tools/openapi-generator/src/OpenApiPatch.ts`
436
- - `packages/tools/openapi-generator/src/OpenApiGenerator.ts`
437
- - `packages/opentelemetry/src/NodeSdk.ts`
438
- - `packages/opentelemetry/src/OtelTracer.ts`
439
- - `packages/platform-node/`
440
- - `packages/vitest/src/index.ts`