@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.
- package/README.md +111 -78
- package/dev-kit.example.jsonc +0 -4
- package/package.json +10 -6
- package/schema/dev-kit.schema.json +1 -46
- package/skills/build-effect-apis/SKILL.md +77 -0
- package/skills/build-effect-apis/agents/openai.yaml +4 -0
- package/skills/build-effect-apis/references/cloudflare-workers.md +71 -0
- package/skills/build-effect-apis/references/effect-atom-client.md +161 -0
- package/skills/build-effect-apis/references/effect-atom-lifecycle.md +78 -0
- package/skills/build-effect-apis/references/effect-atom-testing.md +74 -0
- package/skills/build-effect-apis/references/runtime-assembly.md +56 -0
- package/skills/build-effect-apis/references/server-and-middleware.md +174 -0
- package/skills/build-effect-apis/references/shared-contracts.md +108 -0
- package/skills/build-effect-apis/references/tanstack-start.md +86 -0
- package/skills/build-effect-apis/references/verification.md +50 -0
- package/skills/build-effect-clis/SKILL.md +61 -0
- package/skills/build-effect-clis/agents/openai.yaml +4 -0
- package/skills/build-effect-clis/references/command-design.md +93 -0
- package/skills/build-effect-clis/references/entrypoints-and-testing.md +59 -0
- package/skills/build-effect-clis/references/processes-and-platform.md +73 -0
- package/skills/dev-kit/SKILL.md +77 -48
- package/skills/effect-architecture-audit/SKILL.md +26 -0
- package/skills/effect-architecture-audit/agents/openai.yaml +4 -0
- package/skills/effect-architecture-audit/references/service-and-boundary-audit.md +150 -0
- package/skills/effect-ts/SKILL.md +21 -256
- package/skills/effect-ts/agents/openai.yaml +3 -3
- package/skills/testing/SKILL.md +5 -0
- package/src/catalog-manager.ts +16 -17
- package/src/catalog.ts +71 -16
- package/src/effect-source.ts +46 -24
- package/src/effect-tsgo.ts +86 -24
- package/src/gitignore.ts +5 -5
- package/src/index.ts +3 -6
- package/src/manifest.ts +0 -34
- package/src/node-symbolic-link.ts +2 -2
- package/src/oxfmt.js +5 -0
- package/src/oxfmt.ts +5 -0
- package/src/oxlint.js +5 -0
- package/src/oxlint.ts +5 -0
- package/src/package-skill-source.ts +51 -59
- package/src/path-digest.ts +7 -7
- package/src/project-package.ts +8 -7
- package/src/project-process-lock.ts +17 -12
- package/src/project-state.ts +1 -1
- package/src/skill-manager.ts +18 -16
- package/src/skill-selector.ts +12 -0
- package/src/sync.ts +181 -131
- package/src/tool-ignore-patterns.js +9 -0
- package/src/tool-ignore-patterns.ts +15 -0
- package/src/vendor.ts +67 -61
- package/src/vite-plus-dependency.ts +10 -11
- package/src/vite-plus-hooks.ts +24 -14
- package/src/vite-plus-quality.ts +21 -172
- package/src/vite-plus.js +81 -0
- package/src/vite-plus.ts +102 -0
- package/templates/AGENTS.md +1 -1
- package/skills/effect-ts/UPSTREAM.md +0 -28
- package/skills/effect-ts/references/atom-cache-lifecycle.md +0 -78
- package/skills/effect-ts/references/atom-http-and-invalidation.md +0 -97
- package/skills/effect-ts/references/atom-tanstack-start.md +0 -69
- package/skills/effect-ts/references/atom-testing.md +0 -67
- package/skills/effect-ts/references/audit-services.md +0 -144
- package/skills/effect-ts/references/features.md +0 -525
- package/skills/effect-ts/references/guide-atom-data-fetching.md +0 -44
- package/skills/effect-ts/references/guide-cli.md +0 -107
- package/skills/effect-ts/references/guide-datetime.md +0 -72
- package/skills/effect-ts/references/guide-effect.md +0 -440
- package/skills/effect-ts/references/guide-error-handling.md +0 -565
- package/skills/effect-ts/references/guide-http-boundaries.md +0 -55
- package/skills/effect-ts/references/guide-layers.md +0 -989
- package/skills/effect-ts/references/guide-observability.md +0 -746
- package/skills/effect-ts/references/guide-retries.md +0 -434
- package/skills/effect-ts/references/guide-schedule.md +0 -343
- package/skills/effect-ts/references/guide-schema.md +0 -664
- package/skills/effect-ts/references/guide-sql.md +0 -536
- package/skills/effect-ts/references/guide-testing.md +0 -532
- package/skills/effect-ts/references/guide-type-safety-and-boundaries.md +0 -131
- package/skills/effect-ts/references/version-and-source.md +0 -86
- 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`
|