opencode-effect-enforcer 0.2.5 → 0.2.6
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 +42 -140
- package/docs/effect-4.0.0-rc.116-changelog.md +2654 -0
- package/docs/effect-4.0.0-rc.116.md +102 -0
- package/guidance/effect-first-development.md +23 -14
- package/guidance/progressive-disclosure-guidance.md +3 -3
- package/package.json +2 -2
- package/patterns/avoid-direct-tag-checks.md +1 -1
- package/patterns/avoid-process-env.md +4 -4
- package/patterns/context-tag-extends.md +4 -4
- package/patterns/prefer-redacted-config.md +10 -10
- package/patterns/require-effect-concurrency.md +1 -1
- package/skills/effect-ai-chat/SKILL.md +2 -2
- package/skills/effect-ai-language-model/SKILL.md +36 -4
- package/skills/effect-ai-prompt/SKILL.md +1 -1
- package/skills/effect-ai-provider/SKILL.md +23 -12
- package/skills/effect-ai-tool/SKILL.md +13 -0
- package/skills/effect-atom-rpc/SKILL.md +7 -1
- package/skills/effect-atom-state/SKILL.md +8 -2
- package/skills/effect-cache/SKILL.md +10 -1
- package/skills/effect-cli/SKILL.md +105 -94
- package/skills/effect-command-executor/SKILL.md +7 -1
- package/skills/effect-config/SKILL.md +67 -44
- package/skills/effect-domain-modeling/SKILL.md +3 -3
- package/skills/effect-error-handling/SKILL.md +2 -2
- package/skills/effect-fiber/SKILL.md +2 -2
- package/skills/effect-filesystem/SKILL.md +34 -4
- package/skills/effect-http-api/SKILL.md +17 -2
- package/skills/effect-http-client/SKILL.md +11 -2
- package/skills/effect-http-server/SKILL.md +28 -11
- package/skills/effect-layer-design/SKILL.md +6 -2
- package/skills/effect-mcp-server/SKILL.md +21 -4
- package/skills/effect-observability/SKILL.md +2 -2
- package/skills/effect-optics/SKILL.md +1 -1
- package/skills/effect-parallelization/SKILL.md +2 -2
- package/skills/effect-pattern-matching/SKILL.md +1 -1
- package/skills/effect-platform-abstraction/SKILL.md +3 -3
- package/skills/effect-rpc-api/SKILL.md +3 -3
- package/skills/effect-rpc-client/SKILL.md +14 -13
- package/skills/effect-rpc-cluster/SKILL.md +15 -12
- package/skills/effect-rpc-server/SKILL.md +11 -13
- package/skills/effect-scheduling/SKILL.md +7 -0
- package/skills/effect-schema-composition/SKILL.md +12 -4
- package/skills/effect-schema-v4/SKILL.md +75 -20
- package/skills/effect-scope/SKILL.md +4 -4
- package/skills/effect-socket/SKILL.md +161 -658
- package/skills/effect-sql/SKILL.md +50 -12
- package/skills/effect-stream/SKILL.md +19 -24
- package/skills/effect-testing/SKILL.md +35 -22
- package/skills/effect-workflow/SKILL.md +13 -2
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Effect 4.0.0-rc.112 → 4.0.0-rc.116
|
|
2
|
+
|
|
3
|
+
## Provenance and release scope
|
|
4
|
+
|
|
5
|
+
- Previous dependency: `effect@4.0.0-rc.112`.
|
|
6
|
+
- Target: `effect@4.0.0-rc.116`, npm's `rc` dist-tag checked on 2026-09-18.
|
|
7
|
+
- npm's `latest` tag is the separate v3 line (`3.22.2`).
|
|
8
|
+
- Exact source: [`effect@4.0.0-rc.116`](https://github.com/Effect-TS/effect/tree/effect%404.0.0-rc.116), commit `d62dd0d65252e5d3635538f0e41adc7c08aa9beb`.
|
|
9
|
+
- [Complete source comparison](https://github.com/Effect-TS/effect/compare/effect%404.0.0-rc.112...effect%404.0.0-rc.116).
|
|
10
|
+
- [Full upstream release notes](effect-4.0.0-rc.116-changelog.md): 121 complete release sections across 31 packages, including dependency-only entries.
|
|
11
|
+
|
|
12
|
+
The interval includes rc.113, rc.114, rc.115, and rc.116. The changelog collection
|
|
13
|
+
was checked section-by-section against every package `CHANGELOG.md` at the target
|
|
14
|
+
tag. No upstream entry in that interval was omitted. Later releases supersede
|
|
15
|
+
earlier changes; notably PostgreSQL timestamp decoding and unknown-OID behavior
|
|
16
|
+
are documented using the final rc.116 contract.
|
|
17
|
+
|
|
18
|
+
## Changes applied
|
|
19
|
+
|
|
20
|
+
| Area | Alignment |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| Dependency and baseline | Pin Effect and lockfile to rc.116; update README and privileged baseline references. |
|
|
23
|
+
| Config and CLI | PascalCase scalar constructors, explicit Boolean defaults, typed arrays/records and ByteSize configuration. Update secret-config detector and fixtures. |
|
|
24
|
+
| Schema | Effectful transformations, standalone Getter combinators, native Arbitrary, parse options, class identity, revivers, AST contracts, template literals, JSON Schema and compilation. |
|
|
25
|
+
| Socket | Scoped pull readers, batched values, Writer objects, typed closure, backpressure, TLS/STARTTLS, addresses, framing, and reconnect ownership. |
|
|
26
|
+
| RPC and cluster | Replace MessagePack recommendations with SchemaBinary; preserve transport framing rules and codec selection; use cluster option `serialization: 'binary'`. |
|
|
27
|
+
| Filesystem and HTTP | ByteSize counts versus numeric buffer lengths and bigint seek offsets; HTTP schemas in Schema; NetAddress, eager web-handler construction, file/range responses, upgrades, parsing and SSE options. |
|
|
28
|
+
| PostgreSQL | Native PgConnection/PgPool, explicit JSON binding, preparation, registry codecs, final row representations, scoped notification queues, startup and TLS behavior. |
|
|
29
|
+
| AI and MCP | Branded service interfaces, opaque/encoded parameter modes, failure-result codecs/origins, DecisionModel, protocol adapters, strict tools, instructions, prompt titles, and stderr logging. |
|
|
30
|
+
| Streams and atoms | Lazy scan seeds, partition ordering/capacity, mapBoth names, targeted error handling, middleware errors, zero TTL, reactivity branding and dehydration. |
|
|
31
|
+
| Lifetimes and scheduling | Cache TTL by Exit, LayerMap acquisition errors, observer error types, process cleanup, timeout fallback lifetime, repeat metadata, rate-limiter store contract, durable queue policy. |
|
|
32
|
+
| Testing | Native Arbitrary sampling/property checks; adapter peer/runtime requirements; compiler-checked migration examples. |
|
|
33
|
+
| Writing | Remove release-qualified behavior from live skills, patterns, and guidance; retain version history here and in upstream release notes. |
|
|
34
|
+
|
|
35
|
+
## Content audit inventory
|
|
36
|
+
|
|
37
|
+
The audit covered all 53 bundled skill directories (54 Markdown files), all 45
|
|
38
|
+
pattern definitions, all four guidance documents, and the runtime source.
|
|
39
|
+
Removed public exports were compared between the exact old and new tags and
|
|
40
|
+
searched across the complete live content tree. Targeted scans also covered
|
|
41
|
+
lowercase Config/CLI constructors, callback sockets, MessagePack, fast-check,
|
|
42
|
+
service interface names, Stream scans, and filesystem sizes.
|
|
43
|
+
|
|
44
|
+
The following grouping records every skill directory; unchanged skills were
|
|
45
|
+
included in the compatibility scans rather than edited merely to mark an audit.
|
|
46
|
+
|
|
47
|
+
| Skill directories (`effect-` prefix omitted) | Review focus |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| schema-v4, schema-composition, domain-modeling, domain-predicates, pattern-matching, optics | Schema syntax, transformations, predicates, tagged unions and issue contracts. |
|
|
50
|
+
| config, cli | Constructor renames, defaults, prompts, collection configuration and secrets. |
|
|
51
|
+
| socket, filesystem, path, platform-abstraction, platform-layers, command-executor | Platform boundaries, scoped I/O, byte counts, address types and process lifetime. |
|
|
52
|
+
| http-api, http-client, http-server | HTTP schemas, parsing, server construction, transport errors, files, SSE and WebSocket upgrades. |
|
|
53
|
+
| rpc-api, rpc-client, rpc-server, rpc-cluster | Schema-aware serialization, framing, cluster defaults, storage and peer compatibility. |
|
|
54
|
+
| ai-chat, ai-language-model, ai-prompt, ai-provider, ai-streaming, ai-tool, mcp-server | Service types, provider responses, tool failures, decisions and MCP contracts. |
|
|
55
|
+
| atom-rpc, atom-state, react-composition | RPC/HTTP error channels, TTL, stream scan, React peer and reactive service contracts. |
|
|
56
|
+
| cache, batching, layer-design, scope, service-implementation, context-witness, managed-runtime, incremental-migration | Cached acquisition, service/layer patterns, resource ownership and runtime boundaries. |
|
|
57
|
+
| fiber, parallelization, concurrency-testing, pubsub-event-bus, stream, scheduling | Structured concurrency, stream operators, timing, retry and test synchronization. |
|
|
58
|
+
| testing, error-handling, observability, wide-events | Native property generation, error surfaces, logger configuration and instrumentation. |
|
|
59
|
+
| sql, workflow, graph, typeclass-design | Native database adapters, durable processing, graph and dual-helper compatibility. |
|
|
60
|
+
|
|
61
|
+
Patterns requiring content changes were `avoid-process-env`,
|
|
62
|
+
`prefer-redacted-config`, `avoid-direct-tag-checks`, `context-tag-extends`, and
|
|
63
|
+
`require-effect-concurrency`. The secret-config detector recognizes
|
|
64
|
+
`Config.String` and `Config.NonEmptyString`; its examples and regression cases
|
|
65
|
+
use the supported constructors. Bidirectional pattern/test inventory and README
|
|
66
|
+
catalog coverage remain required checks.
|
|
67
|
+
|
|
68
|
+
The active guidance baseline and examples were updated. Historical essays and
|
|
69
|
+
the earlier rc.112 audit retain their historical context.
|
|
70
|
+
|
|
71
|
+
## Compatibility decisions
|
|
72
|
+
|
|
73
|
+
- Keep directly consumed Effect-family packages on compatible release versions.
|
|
74
|
+
This package directly depends only on `effect`; companion adapters are teaching
|
|
75
|
+
material, not additional runtime dependencies.
|
|
76
|
+
- `@effect/vitest` requires Vitest `>=5.0.0 <6.0.0` and Node
|
|
77
|
+
`^22.12.0 || ^24.0.0 || >=26.0.0`. This repository uses plain Vitest 3 and does
|
|
78
|
+
not install that adapter; its own runner therefore remains unchanged.
|
|
79
|
+
- SchemaBinary is a wire/persistence format change, not a transparent replacement
|
|
80
|
+
for existing MessagePack bytes. Coordinate peers and stored-data migration.
|
|
81
|
+
- Native PostgreSQL is a driver change. Validate row decoding, custom OIDs,
|
|
82
|
+
preparation/pooler settings and notification recovery at consuming boundaries.
|
|
83
|
+
- The source reference's moving main branch is not the baseline. The exact tag
|
|
84
|
+
takes precedence when source and prose disagree.
|
|
85
|
+
|
|
86
|
+
## Verification
|
|
87
|
+
|
|
88
|
+
- Changelog completeness: all 121 selected upstream sections matched the collected
|
|
89
|
+
text across 31 package changelogs.
|
|
90
|
+
- Removed-export scan: no old-only qualified public Effect symbols remain in
|
|
91
|
+
skills, patterns, guidance or runtime source.
|
|
92
|
+
- `bun run check`: passed formatting, lint (zero warnings/errors) and TypeScript
|
|
93
|
+
compilation.
|
|
94
|
+
- `bun run test`: passed all 912 tests in 53 files, including detector cases,
|
|
95
|
+
inventory/catalog coverage, runtime tests and marked Markdown example
|
|
96
|
+
compilation against installed rc.116.
|
|
97
|
+
- `git diff --check`: passed.
|
|
98
|
+
|
|
99
|
+
The documentation compiler checks explicitly marked, independent TypeScript
|
|
100
|
+
fences. It does not execute every illustrative fragment or verify integrations
|
|
101
|
+
against live databases/providers. Companion-package examples were reviewed
|
|
102
|
+
against tagged source rather than adding all companion dependencies here.
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
This document defines the working model behind Effect-first code using the Effect v4 ecosystem.
|
|
4
4
|
|
|
5
|
-
Bundled baseline: **Effect 4.0.0-rc.
|
|
5
|
+
Bundled baseline: **Effect 4.0.0-rc.116**. Check the consuming project's installed
|
|
6
6
|
version and inspect the matching upstream release tag before using newer APIs.
|
|
7
|
-
The release changelog and migration audit are in `docs/effect-4.0.0-rc.
|
|
7
|
+
The release changelog and migration audit are in `docs/effect-4.0.0-rc.116.md`.
|
|
8
8
|
Source paths below are relative to the Effect source reference; locate symbols
|
|
9
9
|
by name rather than relying on line numbers or a particular tool name.
|
|
10
10
|
|
|
@@ -296,7 +296,7 @@ export type Tenant = typeof Tenant.Type;
|
|
|
296
296
|
- Prefer `Schema.Class` for tagged union member schemas.
|
|
297
297
|
- Use `Schema.TaggedUnion` only for canonical `_tag` object-union construction.
|
|
298
298
|
- Reference: `packages/effect/SCHEMA.md`, sections `TaggedUnion` and `toTaggedUnion`.
|
|
299
|
-
-
|
|
299
|
+
- Both union forms offer `matchOrElse` for partial matching with a typed fallback. Prefer `match` when every variant must be handled separately. `toTaggedUnion` narrows fallback input to unmatched variants; direct `Schema.TaggedUnion` types it as the full union.
|
|
300
300
|
|
|
301
301
|
Example:
|
|
302
302
|
|
|
@@ -420,6 +420,12 @@ const pollInterval = Duration.millis(250);
|
|
|
420
420
|
const program = Effect.sleep(pollInterval).pipe(Effect.timeout(timeout));
|
|
421
421
|
```
|
|
422
422
|
|
|
423
|
+
### EF-16b: Byte counts use `ByteSize`
|
|
424
|
+
|
|
425
|
+
Use `ByteSize` and `ByteSize.Input` for byte counts and storage/network limits.
|
|
426
|
+
Keep signed seek offsets as `bigint`. Parse external size strings before passing
|
|
427
|
+
them to APIs, and preserve exact counts rather than coercing them to numbers.
|
|
428
|
+
|
|
423
429
|
### EF-17: Nullable/nullish schema fields should decode to `Option`
|
|
424
430
|
|
|
425
431
|
- Use dedicated schema helpers for optional/null conversions:
|
|
@@ -467,7 +473,7 @@ const b = pipe('value', addPrefix('p:'));
|
|
|
467
473
|
|
|
468
474
|
### EF-19: JSON parse/stringify must use Schema
|
|
469
475
|
|
|
470
|
-
- Use `Schema.fromJsonString(Schema.Unknown)` for unknown JSON payloads.
|
|
476
|
+
- Use `Schema.fromJsonString(Schema.Unknown)` for unknown JSON payloads.
|
|
471
477
|
- Use `Schema.fromJsonString(MySchema)` for typed JSON string boundaries.
|
|
472
478
|
- Avoid direct `JSON.parse` / `JSON.stringify` in Effect-first code.
|
|
473
479
|
- Reference: [`fromJsonString`](packages/effect/src/Schema.ts) in the Effect v4 source.
|
|
@@ -540,7 +546,7 @@ const readSdkValue = (client: ExternalSdk) =>
|
|
|
540
546
|
- Prefer `Effect.scoped` for helper composition that allocates resources.
|
|
541
547
|
- Do not manually open resources without an explicit finalization strategy.
|
|
542
548
|
- Reference: `acquireUseRelease` and `scoped` in `packages/effect/src/Effect.ts`.
|
|
543
|
-
- For pool checkouts
|
|
549
|
+
- For pool checkouts use `Pool.use(pool, callback)` to return the item on every exit without adding a caller `Scope` requirement. Do not return an item from `Effect.scoped(Pool.get(pool))` and then use it after release.
|
|
544
550
|
|
|
545
551
|
Example:
|
|
546
552
|
|
|
@@ -615,7 +621,7 @@ const runWithHeartbeat = Effect.fn('Worker.run')(function* () {
|
|
|
615
621
|
- For non-trivial fan-out, set concurrency in `Effect.forEach`, `Effect.all`, or `Effect.validate`.
|
|
616
622
|
- Avoid implicit unbounded parallelism on large collections.
|
|
617
623
|
- Concurrency should be part of API intent for throughput-sensitive paths.
|
|
618
|
-
- Use an explicit number or `"unbounded"
|
|
624
|
+
- Use an explicit number or `"unbounded"`; pass concurrency policy directly to each combinator.
|
|
619
625
|
- Reference: `Effect.forEach`, `Effect.all`, and `Types.Concurrency` in the Effect v4 source.
|
|
620
626
|
|
|
621
627
|
Example:
|
|
@@ -640,16 +646,16 @@ Example:
|
|
|
640
646
|
import { Config, Effect } from 'effect';
|
|
641
647
|
|
|
642
648
|
const loadPort = Effect.fn('Config.loadPort')(function* () {
|
|
643
|
-
return yield* Config.
|
|
649
|
+
return yield* Config.Int('PORT');
|
|
644
650
|
});
|
|
645
651
|
```
|
|
646
652
|
|
|
647
653
|
### EF-29: Secrets must stay redacted
|
|
648
654
|
|
|
649
|
-
- Use `Config.
|
|
655
|
+
- Use `Config.Redacted` for secret config values.
|
|
650
656
|
- Use `Redacted.make` for sensitive values coming from non-config sources.
|
|
651
657
|
- Never log secret values after unwrapping.
|
|
652
|
-
- Reference: `Config.
|
|
658
|
+
- Reference: `Config.Redacted` in `packages/effect/src/Config.ts` and `packages/effect/src/Redacted.ts`.
|
|
653
659
|
|
|
654
660
|
Example:
|
|
655
661
|
|
|
@@ -657,7 +663,7 @@ Example:
|
|
|
657
663
|
import { Config, Effect } from 'effect';
|
|
658
664
|
|
|
659
665
|
const loadApiKey = Effect.fn('Config.loadApiKey')(function* () {
|
|
660
|
-
const apiKey = yield* Config.
|
|
666
|
+
const apiKey = yield* Config.Redacted('API_KEY');
|
|
661
667
|
yield* Effect.logDebug(`apiKey=${String(apiKey)}`);
|
|
662
668
|
return apiKey;
|
|
663
669
|
});
|
|
@@ -764,6 +770,8 @@ export class CreateOrderInput extends Schema.Class<CreateOrderInput>(
|
|
|
764
770
|
- Use `Schema.withDecodingDefault` / `Schema.withDecodingDefaultKey` for decode-time defaults.
|
|
765
771
|
- Constructor defaults may fail with `SchemaIssue.Issue`. Likewise, `MySchema.makeEffect(fields)` returns validation failures directly as `SchemaIssue.Issue`, not wrapped in `Schema.SchemaError`.
|
|
766
772
|
- Constructor defaults (including `Schema.tag`) do not automatically apply during boundary decoding. Unknown input uses `Schema.decodeUnknownEffect`; `decodeSync` is not a constructor replacement.
|
|
773
|
+
- Class `make`, `makeOption`, and `makeEffect` preserve an existing instance. Use `new` when a distinct instance is intended.
|
|
774
|
+
- Pass parsing options to the decoder/encoder/constructor adapter. Model retained extra properties with `Record` or `StructWithRest`; use `onExcessProperty: "error"` for closed boundaries.
|
|
767
775
|
|
|
768
776
|
Example:
|
|
769
777
|
|
|
@@ -925,6 +933,7 @@ const arraysEqual = (
|
|
|
925
933
|
|
|
926
934
|
- If conversion is deterministic and type-shaping (path normalization, filename conversion, tagged-string normalization), model it with `Schema.decodeTo(..., SchemaTransformation.transform(...))`.
|
|
927
935
|
- Prefer schema transformation helpers over ad-hoc conversion functions.
|
|
936
|
+
- Use `SchemaGetter.transformEffect` / `SchemaTransformation.transformEffect` for effectful conversion. Compose getters with standalone `SchemaGetter.compose`, and transformation pairs with `SchemaTransformation.composeTransformation`.
|
|
928
937
|
|
|
929
938
|
Example:
|
|
930
939
|
|
|
@@ -994,7 +1003,7 @@ const UnknownToString = Schema.Unknown.pipe(
|
|
|
994
1003
|
- For invalidatable caches, use `Effect.cachedInvalidateWithTTL(effect, Duration.infinity)` which returns a `[cachedEffect, invalidate]` tuple. Call `yield* invalidate` to force re-computation on next access.
|
|
995
1004
|
- For time-based caches, use `Effect.cachedWithTTL(effect, duration)`.
|
|
996
1005
|
- Prefer `Effect.cachedInvalidateWithTTL` with `Duration.infinity` over mutable `let` rebinding of cached effects.
|
|
997
|
-
- For keyed scoped resources,
|
|
1006
|
+
- For keyed scoped resources, use `RcMap.getOption` and `LayerMap.contextEffectOption` to atomically retain already-cached entries and propagate acquisition failures; `None` means missing/closed, not failed. A `has` check followed by `get` is not equivalent.
|
|
998
1007
|
|
|
999
1008
|
Example:
|
|
1000
1009
|
|
|
@@ -1191,8 +1200,8 @@ export const resilientTask = task.pipe(
|
|
|
1191
1200
|
import { Config, Effect } from 'effect';
|
|
1192
1201
|
|
|
1193
1202
|
export const loadConfig = Effect.fn('Config.load')(function* () {
|
|
1194
|
-
const port = yield* Config.
|
|
1195
|
-
const apiKey = yield* Config.
|
|
1203
|
+
const port = yield* Config.Int('PORT');
|
|
1204
|
+
const apiKey = yield* Config.Redacted('API_KEY');
|
|
1196
1205
|
return { port, apiKey };
|
|
1197
1206
|
});
|
|
1198
1207
|
```
|
|
@@ -1237,7 +1246,7 @@ Use this before submitting code:
|
|
|
1237
1246
|
24. Forking intent is explicit (`forkChild` default; `forkDetach` justified).
|
|
1238
1247
|
25. Large fan-out operations specify concurrency deliberately.
|
|
1239
1248
|
26. Config values come from `Config` / `ConfigProvider`, not direct `process.env` in domain logic.
|
|
1240
|
-
27. Secrets are `Redacted` (`Config.
|
|
1249
|
+
27. Secrets are `Redacted` (`Config.Redacted` / `Redacted.make`) and not logged raw.
|
|
1241
1250
|
28. Recovery uses `catchTag` / `catchFilter` for targeted cases.
|
|
1242
1251
|
29. Expected failures use `Effect.fail`; defects are reserved for invariants and discarding irrelevant upstream error types via `orDie`.
|
|
1243
1252
|
30. Isolation-sensitive layer provisioning uses `{ local: true }` or `Layer.fresh`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agent Rules
|
|
2
2
|
|
|
3
|
-
The bundled guidance targets **Effect 4.0.0-rc.
|
|
3
|
+
The bundled guidance targets **Effect 4.0.0-rc.116**. Read the consuming project's
|
|
4
4
|
version before applying an API: stable v3, older prereleases, and unreleased main
|
|
5
5
|
can have different contracts. Keep directly used Effect-family packages on
|
|
6
6
|
compatible release versions.
|
|
@@ -10,8 +10,8 @@ Load all relevant skills before writing or planning any code. Effect is a massiv
|
|
|
10
10
|
When skills leave any ambiguity, or when you encounter unfamiliar APIs during implementation, read the OpenCode `effect` reference at `~/.local/share/opencode/repos/github.com/Effect-TS/effect@main/`. Treat this reference as the source of truth over `node_modules`, stale external docs, or memory.
|
|
11
11
|
|
|
12
12
|
Check the reference revision too. For this baseline, inspect the
|
|
13
|
-
`effect@4.0.0-rc.
|
|
14
|
-
effect@4.0.0-rc.
|
|
13
|
+
`effect@4.0.0-rc.116` tag (for example with `git show
|
|
14
|
+
effect@4.0.0-rc.116:packages/effect/src/Schema.ts`) when main has moved ahead.
|
|
15
15
|
Source symbols and signatures at that tag take precedence over stale prose or
|
|
16
16
|
line-number links. Public exports marked `@internal` in source are not application APIs.
|
|
17
17
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://www.schemastore.org/package.json",
|
|
3
3
|
"name": "opencode-effect-enforcer",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.6",
|
|
5
5
|
"description": "OpenCode V2 plugin for Effect v4 skills, guidance, and pattern enforcement",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"opencode",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@ast-grep/napi": "^0.42.3",
|
|
47
47
|
"@opencode/plugin": "2.0.8",
|
|
48
48
|
"diff": "^9.0.0",
|
|
49
|
-
"effect": "4.0.0-rc.
|
|
49
|
+
"effect": "4.0.0-rc.116",
|
|
50
50
|
"picomatch": "^4.0.3",
|
|
51
51
|
"yaml": "^2.8.1"
|
|
52
52
|
},
|
|
@@ -55,6 +55,6 @@ TypeScript correctly narrows literal `_tag` checks. Prefer exported guards and
|
|
|
55
55
|
matching helpers for consistent semantics and exhaustiveness as variants evolve.
|
|
56
56
|
For schema-first models use union `.guards`, `.match`, or `Schema.is`; class
|
|
57
57
|
variants can also use `instanceof`. `Schema.toTaggedUnion` supports discriminator
|
|
58
|
-
keys beyond `_tag`.
|
|
58
|
+
keys beyond `_tag`. `.matchOrElse` provides partial matching with a typed
|
|
59
59
|
fallback. For trusted `Data.taggedEnum` values use `$is` / `$match`; `$is` checks
|
|
60
60
|
only the tag and is not structural validation of unknown input.
|
|
@@ -19,9 +19,9 @@ suggestSkills:
|
|
|
19
19
|
processEnv :: String -> IO (Maybe String) -- side effect, untyped, untestable
|
|
20
20
|
|
|
21
21
|
-- Instead
|
|
22
|
-
Config.
|
|
22
|
+
Config.String :: String -> Config String -- typed, composable, testable
|
|
23
23
|
Config.withDefault :: a -> Config a -> Config a
|
|
24
|
-
Config.
|
|
24
|
+
Config.Redacted :: String -> Config Redacted -- for sensitive values
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
```haskell
|
|
@@ -30,10 +30,10 @@ bad :: Effect String
|
|
|
30
30
|
bad = Effect.sync \_ -> process.env.API_KEY -- raw side effect
|
|
31
31
|
|
|
32
32
|
good :: Effect String ConfigError
|
|
33
|
-
good = Config.
|
|
33
|
+
good = Config.Redacted "API_KEY" -- typed, redacted
|
|
34
34
|
|
|
35
35
|
better :: Effect String ConfigError
|
|
36
|
-
better = Config.
|
|
36
|
+
better = Config.Int("PORT")
|
|
37
37
|
& Config.withDefault "3000"
|
|
38
38
|
& Config.map Number.parse -- with transformation
|
|
39
39
|
```
|
|
@@ -26,13 +26,13 @@ suggestSkills:
|
|
|
26
26
|
|
|
27
27
|
# Use `Context.Service` for All Service Definitions
|
|
28
28
|
|
|
29
|
-
`Context.Service`
|
|
29
|
+
Use `Context.Service` for service definitions. Replace these legacy spellings:
|
|
30
30
|
|
|
31
31
|
| Legacy API | Era | Replacement |
|
|
32
32
|
| --------------------------- | --------- | -------------------------- |
|
|
33
33
|
| `Context.Tag` / `GenericTag` | pre-v4 | `Context.Service` |
|
|
34
34
|
| `Effect.Service` | early v4 | `Context.Service` |
|
|
35
|
-
| `ServiceMap.Service` / `.*` |
|
|
35
|
+
| `ServiceMap.Service` / `.*` | prerelease | `Context.Service` / `Context.*` |
|
|
36
36
|
|
|
37
37
|
```haskell
|
|
38
38
|
-- Anti-pattern: *Tag suffix + Context.Tag — removed in v4
|
|
@@ -43,10 +43,10 @@ data ParallelClientService = ...
|
|
|
43
43
|
-- Anti-pattern: Effect.Service — also removed in v4
|
|
44
44
|
class ParallelClient extends Effect.Service<ParallelClient>()(...)
|
|
45
45
|
|
|
46
|
-
-- Anti-pattern: ServiceMap.*
|
|
46
|
+
-- Anti-pattern: ServiceMap.*
|
|
47
47
|
class MyService extends ServiceMap.Service<MyService>()("@app/MyService", { ... })
|
|
48
48
|
|
|
49
|
-
-- Fix: Context.Service
|
|
49
|
+
-- Fix: Context.Service
|
|
50
50
|
class ParallelClient extends Context.Service<ParallelClient>()(
|
|
51
51
|
"@parallel/ParallelClient"
|
|
52
52
|
)
|
|
@@ -3,13 +3,13 @@ action: context
|
|
|
3
3
|
tool: (edit|write)
|
|
4
4
|
event: after
|
|
5
5
|
name: prefer-redacted-config
|
|
6
|
-
description: Use Config.
|
|
6
|
+
description: Use Config.Redacted or Schema.Redacted for secret-like configuration values
|
|
7
7
|
glob: '**/*.{ts,tsx}'
|
|
8
8
|
detector: ast
|
|
9
9
|
rule:
|
|
10
10
|
any:
|
|
11
|
-
- pattern: Config.
|
|
12
|
-
- pattern: Config.
|
|
11
|
+
- pattern: Config.String($KEY)
|
|
12
|
+
- pattern: Config.NonEmptyString($KEY)
|
|
13
13
|
- all:
|
|
14
14
|
- kind: pair
|
|
15
15
|
- has:
|
|
@@ -33,18 +33,18 @@ suggestSkills:
|
|
|
33
33
|
|
|
34
34
|
```haskell
|
|
35
35
|
-- Transformation
|
|
36
|
-
Config.
|
|
37
|
-
Config.
|
|
36
|
+
Config.String secretKey :: String -- easy to log accidentally
|
|
37
|
+
Config.Redacted secretKey :: Redacted String -- hidden from logs/toString
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
```typescript
|
|
41
41
|
// Bad
|
|
42
|
-
const apiKey = Config.
|
|
43
|
-
const token = Config.
|
|
42
|
+
const apiKey = Config.String('API_KEY');
|
|
43
|
+
const token = Config.NonEmptyString('GITHUB_TOKEN');
|
|
44
44
|
|
|
45
45
|
// Good
|
|
46
|
-
const apiKey = Config.
|
|
47
|
-
const token = Config.
|
|
46
|
+
const apiKey = Config.Redacted('API_KEY');
|
|
47
|
+
const token = Config.Redacted('GITHUB_TOKEN');
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
For structured config schemas, wrap secret-like string fields in `Schema.Redacted`:
|
|
@@ -67,4 +67,4 @@ const AppConfig = Config.schema(
|
|
|
67
67
|
);
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
Secrets should remain redacted from the moment they enter the program. Use `Config.
|
|
70
|
+
Secrets should remain redacted from the moment they enter the program. Use `Config.Redacted` for primitive config values and `Schema.Redacted(Schema.String)` for schema-based config fields.
|
|
@@ -79,7 +79,7 @@ Effect.forEach xs f opts -- concurrency intent explicit
|
|
|
79
79
|
Effect.forEach(items, processItem);
|
|
80
80
|
Effect.all(tasks);
|
|
81
81
|
Effect.validate(inputs, validateInput, { discard: true });
|
|
82
|
-
Effect.all(tasks, { concurrency: 'inherit' }); //
|
|
82
|
+
Effect.all(tasks, { concurrency: 'inherit' }); // unsupported ambient policy
|
|
83
83
|
|
|
84
84
|
// Good
|
|
85
85
|
Effect.forEach(items, processItem, { concurrency: 1 });
|
|
@@ -231,7 +231,7 @@ const program = Effect.gen(function* () {
|
|
|
231
231
|
timeToLive: '1 hour' // optional TTL
|
|
232
232
|
});
|
|
233
233
|
|
|
234
|
-
// chat is a `Chat.Persisted` — same API as Chat.
|
|
234
|
+
// chat is a `Chat.Persisted` — same API as Chat.Chat but auto-saves
|
|
235
235
|
const response = yield* chat
|
|
236
236
|
.generateText({
|
|
237
237
|
prompt: 'Hello!'
|
|
@@ -245,7 +245,7 @@ const program = Effect.gen(function* () {
|
|
|
245
245
|
});
|
|
246
246
|
```
|
|
247
247
|
|
|
248
|
-
The `Persisted` interface extends `Chat.
|
|
248
|
+
The `Persisted` interface extends `Chat.Chat` with:
|
|
249
249
|
|
|
250
250
|
- `id: string` — the chat identifier in the store
|
|
251
251
|
- `save: Effect<void, AiError | PersistenceError>` — manual save trigger
|
|
@@ -94,7 +94,13 @@ const manualTools = LanguageModel.generateText({
|
|
|
94
94
|
});
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
With `disableToolCallResolution: true`, tool-call `params` retain the schema's
|
|
98
|
+
**encoded** representation. Response generics use the mode `"encoded"`, for
|
|
99
|
+
example `GenerateTextResponse<Tools, "encoded">` and
|
|
100
|
+
`Response.StreamPart<Tools, "encoded">`. Automatic resolution uses `"opaque"`
|
|
101
|
+
parameters (`unknown`), because invalid calls can also appear in responses.
|
|
102
|
+
Only successfully decoded handler inputs have the decoded parameter type.
|
|
103
|
+
For `Schema.NumberFromString`, manual calls contain `{ count: '3' }`.
|
|
98
104
|
|
|
99
105
|
Pass those encoded params directly to `toolkit.handle(name, params, toolCallId)` when resolving manually. `Toolkit.handle` now accepts `Tool.ParametersEncoded<T>` and performs the decode before the handler receives `Tool.Parameters<T>`.
|
|
100
106
|
|
|
@@ -104,7 +110,7 @@ Pass those encoded params directly to `toolkit.handle(name, params, toolCallId)`
|
|
|
104
110
|
const response = yield* LanguageModel.generateText({ prompt: '...' });
|
|
105
111
|
|
|
106
112
|
response.text; // string - concatenated text content
|
|
107
|
-
response.toolCalls; //
|
|
113
|
+
response.toolCalls; // opaque params normally; encoded params when resolution is disabled
|
|
108
114
|
response.toolResults; // Array<ToolResultParts> - tool outputs
|
|
109
115
|
response.finishReason; // "stop" | "length" | "content-filter" | "tool-calls" | "error" | "pause" | "unknown" | "other"
|
|
110
116
|
response.usage; // Usage object with nested structure, e.g. response.usage.outputTokens.total
|
|
@@ -297,6 +303,32 @@ const robust = LanguageModel.generateText({
|
|
|
297
303
|
);
|
|
298
304
|
```
|
|
299
305
|
|
|
306
|
+
## Batched classification, rating, and probability
|
|
307
|
+
|
|
308
|
+
Use `DecisionModel` for structured judgments over one schema-encoded input.
|
|
309
|
+
Define named decisions once and send them in one call; input encoding services
|
|
310
|
+
remain explicit and provider/validation failures use `AiError`. Classification
|
|
311
|
+
requires at least two labels, rating requires at least two distinct ordered
|
|
312
|
+
levels, and a definition requires at least one decision.
|
|
313
|
+
|
|
314
|
+
<!-- typecheck -->
|
|
315
|
+
```ts
|
|
316
|
+
import * as Schema from 'effect/Schema';
|
|
317
|
+
import { Decision, DecisionModel } from 'effect/unstable/ai';
|
|
318
|
+
|
|
319
|
+
class Ticket extends Schema.Class<Ticket>('Ticket')({ body: Schema.String }) {}
|
|
320
|
+
const triage = Decision.make({
|
|
321
|
+
input: Ticket,
|
|
322
|
+
decisions: {
|
|
323
|
+
team: Decision.classify({
|
|
324
|
+
instructions: 'Choose the team responsible for this request',
|
|
325
|
+
criteria: { billing: 'Payments and invoices', support: 'Product support' }
|
|
326
|
+
})
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
const result = DecisionModel.decide(triage, { input: new Ticket({ body: 'Invoice question' }) });
|
|
330
|
+
```
|
|
331
|
+
|
|
300
332
|
## Type Extraction Utilities
|
|
301
333
|
|
|
302
334
|
```typescript
|
|
@@ -308,8 +340,8 @@ type MyError = LanguageModel.ExtractError<typeof options>;
|
|
|
308
340
|
// Extract service requirements from options
|
|
309
341
|
type MyRequirements = LanguageModel.ExtractServices<typeof options>;
|
|
310
342
|
|
|
311
|
-
//
|
|
312
|
-
type
|
|
343
|
+
// "encoded" for literal disableToolCallResolution: true; otherwise "opaque"
|
|
344
|
+
type ParametersMode = LanguageModel.ExtractToolParametersMode<typeof options>;
|
|
313
345
|
|
|
314
346
|
// Inferred based on:
|
|
315
347
|
// - toolkit: Toolkit.WithHandler<Tools> → Tool.HandlerError<Tools> ∈ E
|
|
@@ -516,7 +516,7 @@ const program = Effect.gen(function* () {
|
|
|
516
516
|
|
|
517
517
|
## Provider-Specific Options
|
|
518
518
|
|
|
519
|
-
### OpenAI Responses explicit cache breakpoints
|
|
519
|
+
### OpenAI Responses explicit cache breakpoints
|
|
520
520
|
|
|
521
521
|
With `@effect/ai-openai` loaded, system-message and text-part options accept
|
|
522
522
|
`openai.promptCacheBreakpoint`. This requires GPT-5.6 or later; earlier models
|
|
@@ -100,7 +100,7 @@ import { FetchHttpClient } from 'effect/unstable/http';
|
|
|
100
100
|
|
|
101
101
|
// Client layer (reusable across models)
|
|
102
102
|
const AnthropicClientLayer = AnthropicClient.layerConfig({
|
|
103
|
-
apiKey: Config.
|
|
103
|
+
apiKey: Config.Redacted('ANTHROPIC_API_KEY')
|
|
104
104
|
}).pipe(Layer.provide(FetchHttpClient.layer));
|
|
105
105
|
|
|
106
106
|
// Option A: model() — returns Model.Model (preferred)
|
|
@@ -130,7 +130,7 @@ import { Config, Layer } from 'effect';
|
|
|
130
130
|
import { FetchHttpClient } from 'effect/unstable/http';
|
|
131
131
|
|
|
132
132
|
const OpenAiClientLayer = OpenAiClient.layerConfig({
|
|
133
|
-
apiKey: Config.
|
|
133
|
+
apiKey: Config.Redacted('OPENAI_API_KEY')
|
|
134
134
|
}).pipe(Layer.provide(FetchHttpClient.layer));
|
|
135
135
|
|
|
136
136
|
// model() constructor (preferred)
|
|
@@ -186,7 +186,7 @@ import { Config, Layer } from 'effect';
|
|
|
186
186
|
import { FetchHttpClient, HttpClient, HttpClientRequest } from 'effect/unstable/http';
|
|
187
187
|
|
|
188
188
|
const CompatibleClientLayer = OpenAiClient.layerConfig({
|
|
189
|
-
apiKey: Config.
|
|
189
|
+
apiKey: Config.Redacted('OPENAI_COMPAT_API_KEY'),
|
|
190
190
|
apiUrl: Config.succeed('https://my-provider.example.com/v1')
|
|
191
191
|
}).pipe(Layer.provide(FetchHttpClient.layer));
|
|
192
192
|
|
|
@@ -211,7 +211,7 @@ import { Config, Layer } from 'effect';
|
|
|
211
211
|
import { FetchHttpClient } from 'effect/unstable/http';
|
|
212
212
|
|
|
213
213
|
const OpenRouterClientLayer = OpenRouterClient.layerConfig({
|
|
214
|
-
apiKey: Config.
|
|
214
|
+
apiKey: Config.Redacted('OPENROUTER_API_KEY')
|
|
215
215
|
}).pipe(Layer.provide(FetchHttpClient.layer));
|
|
216
216
|
|
|
217
217
|
// model() constructor — use provider-prefixed model IDs
|
|
@@ -420,7 +420,7 @@ export class AiWriter extends Context.Service<
|
|
|
420
420
|
|
|
421
421
|
## Custom Error Wrapping
|
|
422
422
|
|
|
423
|
-
|
|
423
|
+
`AiError.AuthenticationError` accepts an optional `description` and
|
|
424
424
|
appends it after the kind-based remediation message. Anthropic, OpenAI,
|
|
425
425
|
OpenAI-compatible, and OpenRouter adapters propagate provider error text from
|
|
426
426
|
401/403 responses. Preserve this reason rather than replacing it with a generic
|
|
@@ -496,11 +496,11 @@ import { FetchHttpClient } from 'effect/unstable/http';
|
|
|
496
496
|
// ---------------------------------------------------------------------------
|
|
497
497
|
|
|
498
498
|
const AnthropicClientLayer = AnthropicClient.layerConfig({
|
|
499
|
-
apiKey: Config.
|
|
499
|
+
apiKey: Config.Redacted('ANTHROPIC_API_KEY')
|
|
500
500
|
}).pipe(Layer.provide(FetchHttpClient.layer));
|
|
501
501
|
|
|
502
502
|
const OpenAiClientLayer = OpenAiClient.layerConfig({
|
|
503
|
-
apiKey: Config.
|
|
503
|
+
apiKey: Config.Redacted('OPENAI_API_KEY')
|
|
504
504
|
}).pipe(Layer.provide(FetchHttpClient.layer));
|
|
505
505
|
|
|
506
506
|
// ---------------------------------------------------------------------------
|
|
@@ -619,15 +619,15 @@ Effect.runPromise(program.pipe(Effect.provide(AiWriter.layer)));
|
|
|
619
619
|
// WRONG: Hardcoded API keys
|
|
620
620
|
AnthropicClient.layerConfig({ apiKey: 'sk-...' });
|
|
621
621
|
|
|
622
|
-
// RIGHT: Config.
|
|
623
|
-
AnthropicClient.layerConfig({ apiKey: Config.
|
|
622
|
+
// RIGHT: Config.Redacted for secrets
|
|
623
|
+
AnthropicClient.layerConfig({ apiKey: Config.Redacted('ANTHROPIC_API_KEY') });
|
|
624
624
|
|
|
625
625
|
// WRONG: Missing FetchHttpClient layer
|
|
626
|
-
AnthropicClient.layerConfig({ apiKey: Config.
|
|
626
|
+
AnthropicClient.layerConfig({ apiKey: Config.Redacted('KEY') });
|
|
627
627
|
// Will fail at runtime — providers require an HttpClient
|
|
628
628
|
|
|
629
629
|
// RIGHT: Always provide an HTTP client layer
|
|
630
|
-
AnthropicClient.layerConfig({ apiKey: Config.
|
|
630
|
+
AnthropicClient.layerConfig({ apiKey: Config.Redacted('KEY') }).pipe(
|
|
631
631
|
Layer.provide(FetchHttpClient.layer)
|
|
632
632
|
);
|
|
633
633
|
|
|
@@ -653,7 +653,7 @@ import { BedrockClient } from '@effect/ai-amazon-bedrock'; // Does NOT exist
|
|
|
653
653
|
|
|
654
654
|
## Quality Checklist
|
|
655
655
|
|
|
656
|
-
- [ ] Use `Config.
|
|
656
|
+
- [ ] Use `Config.Redacted` for API keys (never hardcode)
|
|
657
657
|
- [ ] Provide `FetchHttpClient.layer` to all client layers
|
|
658
658
|
- [ ] Use `.model()` constructor for `ExecutionPlan` and `Effect.provide`
|
|
659
659
|
- [ ] Use `ExecutionPlan` for multi-provider fallback with retry
|
|
@@ -674,6 +674,17 @@ import { BedrockClient } from '@effect/ai-amazon-bedrock'; // Does NOT exist
|
|
|
674
674
|
|
|
675
675
|
## References
|
|
676
676
|
|
|
677
|
+
Provider-neutral structured decisions use `Decision` / `DecisionModel` from
|
|
678
|
+
`effect/unstable/ai`. `OpenRouterDecisionModel` targets OpenRouter's alpha Decisions
|
|
679
|
+
API; `@effect/ai-typesafe` supplies a provider for TypeSafe System One. Custom
|
|
680
|
+
OpenRouter client implementations include `createDecisions`.
|
|
681
|
+
|
|
682
|
+
Use each branded service's same-name type (`LanguageModel.LanguageModel`,
|
|
683
|
+
`EmbeddingModel.EmbeddingModel`, `Chat.Chat`) and its exported TypeId for custom
|
|
684
|
+
implementations. Prefer provided constructors; do not hard-code marker strings.
|
|
685
|
+
OpenAI web-search sources are a discriminated union: narrow `type` before reading
|
|
686
|
+
`url` or `name`. Provider-executed tool failures remain failure results.
|
|
687
|
+
|
|
677
688
|
- `packages/ai/anthropic/src/AnthropicLanguageModel.ts`
|
|
678
689
|
- `packages/ai/openai/src/OpenAiLanguageModel.ts`
|
|
679
690
|
- `packages/ai/openrouter/src/OpenRouterLanguageModel.ts`
|
|
@@ -208,6 +208,19 @@ type Error = Tool.Failure<typeof FindUser>;
|
|
|
208
208
|
|
|
209
209
|
**Key Pattern: failureMode**
|
|
210
210
|
|
|
211
|
+
Parameter validation follows the tool's `failureMode`. Failure results include
|
|
212
|
+
`Tool.ExecutionFailure` for framework failures (AI errors, denial, interruption)
|
|
213
|
+
as well as declared user errors. Select result codecs using `isFailure` and
|
|
214
|
+
`Tool.failureResultSchema(tool)`; preserve `encodedResult` when storing or
|
|
215
|
+
replaying response parts. A failure with `Schema.NumberFromString` is stored as
|
|
216
|
+
a string, even when the success schema stores a number. Validation errors do not
|
|
217
|
+
expose a `toolParams` field.
|
|
218
|
+
|
|
219
|
+
`Toolkit.handle` accepts parse options for parameter decoding. Returned failures
|
|
220
|
+
carry `failureOrigin`, also available through the `Toolkit.FailureOrigin` cause
|
|
221
|
+
annotation and `Tool.FailureOrigin` type. Keep validation, declared handler, and
|
|
222
|
+
internal failures distinct when presenting or reporting them.
|
|
223
|
+
|
|
211
224
|
- `"error"` (default): Failures go to Effect error channel
|
|
212
225
|
- `"return"`: Failures returned as tool result (captured, not thrown)
|
|
213
226
|
|
|
@@ -20,7 +20,7 @@ Use this skill when building React (or Atom-based) frontends that consume an exi
|
|
|
20
20
|
For the underlying RPC definitions, see the `effect-rpc-cluster` skill.
|
|
21
21
|
For Atom fundamentals (`Atom.make`, `family`, `keepAlive`, `AsyncResult`, hydration), see the `effect-atom-state` skill.
|
|
22
22
|
|
|
23
|
-
The underlying RPC protocols require `codecFor
|
|
23
|
+
The underlying RPC protocols require `codecFor`. Built-in protocol
|
|
24
24
|
layers supply it; forward it when implementing a custom transport. You can pair
|
|
25
25
|
`RpcSerialization.layerSchemaBinary` on client/server without changing query or
|
|
26
26
|
mutation call sites; see `effect-rpc-client` for frame limits and compatibility.
|
|
@@ -29,6 +29,12 @@ type is `string`. This is distinct from the client's `{ discard: true }` option.
|
|
|
29
29
|
|
|
30
30
|
## Effect Source Reference
|
|
31
31
|
|
|
32
|
+
Query and mutation errors include client middleware errors. AtomHttpApi stream
|
|
33
|
+
successes retain transport, decoding, and SSE failures in the stream error channel;
|
|
34
|
+
handle them when consuming the stream. An explicit zero `timeToLive` disables
|
|
35
|
+
default idle retention, so unmount/remount may dispose and refetch. Omission uses
|
|
36
|
+
the registry default. HttpApi calls accept per-call `sseOptions`.
|
|
37
|
+
|
|
32
38
|
- `packages/effect/src/unstable/reactivity/AtomRpc.ts` — the whole API (~270 lines)
|
|
33
39
|
- `packages/effect/test/reactivity/AtomRpc.test.ts` — minimal usage + serialization test
|
|
34
40
|
- `packages/effect/src/unstable/reactivity/AtomHttpApi.ts` — the cousin pattern for `HttpApi` (same idea, same options)
|