Merge commit '36c66dd290d3ce6eb1ccd310d0c658d4a32bb8eb' as 'repos/effect'
This commit is contained in:
383
repos/effect/LLMS.md
Normal file
383
repos/effect/LLMS.md
Normal file
@@ -0,0 +1,383 @@
|
||||
# Effect library documentation
|
||||
|
||||
This documentation resides in the Effect monorepo, which contains the source
|
||||
code for the Effect library and its related packages.
|
||||
|
||||
When you need to find any information about the Effect library, only use this
|
||||
documentation and the source code found in `./packages`. Do not use
|
||||
`node_modules` or any other external documentation, as it may be outdated or
|
||||
incorrect.
|
||||
|
||||
**Note**: The examples in this documentation contain comments for illustration
|
||||
purposes. In practice, you would not include these comments in your code.
|
||||
|
||||
## Writing `Effect` code
|
||||
|
||||
Prefer writing Effect code with `Effect.gen` & `Effect.fn("name")`. Then attach
|
||||
additional behaviour with combinators. This style is more readable and easier to
|
||||
maintain than using combinators alone.
|
||||
|
||||
### Using Effect.gen
|
||||
|
||||
Use `Effect.gen` to write code in an imperative style similar to async await.
|
||||
You can use `yield*` to access the result of an effect.
|
||||
|
||||
```ts
|
||||
import { Effect, Schema } from "effect"
|
||||
|
||||
Effect.gen(function*() {
|
||||
yield* Effect.log("Starting the file processing...")
|
||||
yield* Effect.log("Reading file...")
|
||||
|
||||
// Always return when raising an error, to ensure typescript understands that
|
||||
// the function will not continue executing.
|
||||
return yield* new FileProcessingError({ message: "Failed to read the file" })
|
||||
}).pipe(
|
||||
// Add additional functionality with .pipe
|
||||
Effect.catch((error) => Effect.logError(`An error occurred: ${error}`)),
|
||||
Effect.withSpan("fileProcessing", {
|
||||
attributes: {
|
||||
method: "Effect.gen"
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
// Use Schema.TaggedErrorClass to define a custom error
|
||||
export class FileProcessingError extends Schema.TaggedErrorClass<FileProcessingError>()("FileProcessingError", {
|
||||
message: Schema.String
|
||||
}) {}
|
||||
```
|
||||
|
||||
### Using Effect.fn
|
||||
|
||||
When writing functions that return an Effect, use `Effect.fn` to use the
|
||||
generator syntax.
|
||||
|
||||
**Avoid creating functions that return an Effect.gen**, use `Effect.fn`
|
||||
instead.
|
||||
|
||||
```ts
|
||||
import { Effect, Schema } from "effect"
|
||||
|
||||
// Pass a string to Effect.fn, which will improve stack traces and also
|
||||
// attach a tracing span (using Effect.withSpan behind the scenes).
|
||||
//
|
||||
// The name string should match the function name.
|
||||
//
|
||||
export const effectFunction = Effect.fn("effectFunction")(
|
||||
// You can use `Effect.fn.Return` to specify the return type of the function.
|
||||
// It accepts the same type parameters as `Effect.Effect`.
|
||||
function*(n: number): Effect.fn.Return<string, SomeError> {
|
||||
yield* Effect.logInfo("Received number:", n)
|
||||
|
||||
// Always return when raising an error, to ensure typescript understands that
|
||||
// the function will not continue executing.
|
||||
return yield* new SomeError({ message: "Failed to read the file" })
|
||||
},
|
||||
// Add additional functionality by passing in additional arguments.
|
||||
// **Do not** use .pipe with Effect.fn
|
||||
Effect.catch((error) => Effect.logError(`An error occurred: ${error}`)),
|
||||
Effect.annotateLogs({
|
||||
method: "effectFunction"
|
||||
})
|
||||
)
|
||||
|
||||
// Use Schema.TaggedErrorClass to define a custom error
|
||||
export class SomeError extends Schema.TaggedErrorClass<SomeError>()("SomeError", {
|
||||
message: Schema.String
|
||||
}) {}
|
||||
```
|
||||
|
||||
### More examples
|
||||
|
||||
- **[Creating effects from common sources](./ai-docs/src/01_effect/01_basics/10_creating-effects.ts)**:
|
||||
Learn how to create effects from various sources, including plain values,
|
||||
synchronous code, Promise APIs, optional values, and callback-based APIs.
|
||||
|
||||
## Defining schemas and domain models
|
||||
|
||||
All validation and domain modeling in Effect is done with `Schema`.
|
||||
|
||||
**AVOID using predicates or manual parsing**, instead use `Schema` to parse untrusted data and validate it.
|
||||
|
||||
For a comprehensive guide, see [packages/effect/SCHEMA.md](./packages/effect/SCHEMA.md). Make sure to read the guide in chunks, as it is a large document.
|
||||
|
||||
- **[Schema basics](./ai-docs/src/01_effect/02_schema/10_schema-basics.ts)**:
|
||||
Define `Schema.Class`s, decode unknown input into typed values, and
|
||||
encode typed values back into their external representation.
|
||||
|
||||
## Writing Effect services
|
||||
|
||||
Effect services are the most common way to structure Effect code. Prefer using
|
||||
services to encapsulate behaviour over other approaches, as it ensures that your
|
||||
code is modular, testable, and maintainable.
|
||||
|
||||
### Context.Service
|
||||
|
||||
The default way to define a service is to extend `Context.Service`,
|
||||
passing in the service interface as a type parameter.
|
||||
|
||||
```ts
|
||||
// file: src/db/Database.ts
|
||||
import { Context, Effect, Layer, Schema } from "effect"
|
||||
|
||||
// Pass in the service class name as the first type parameter, and the service
|
||||
// interface as the second type parameter.
|
||||
export class Database extends Context.Service<Database, {
|
||||
query(sql: string): Effect.Effect<Array<unknown>, DatabaseError>
|
||||
}>()(
|
||||
// The string identifier for the service, which should include the package
|
||||
// name and the subdirectory path to the service file.
|
||||
"myapp/db/Database"
|
||||
) {
|
||||
// Attach a static layer to the service, which will be used to provide an
|
||||
// implementation of the service.
|
||||
static readonly layer = Layer.effect(
|
||||
Database,
|
||||
Effect.gen(function*() {
|
||||
// Define the service methods using Effect.fn
|
||||
const query = Effect.fn("Database.query")(function*(sql: string) {
|
||||
yield* Effect.log("Executing SQL query:", sql)
|
||||
return [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]
|
||||
})
|
||||
|
||||
// Return an instance of the service using Database.of, passing in an
|
||||
// object that implements the service interface.
|
||||
return Database.of({
|
||||
query
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
export class DatabaseError extends Schema.TaggedErrorClass<DatabaseError>()("DatabaseError", {
|
||||
cause: Schema.Defect()
|
||||
}) {}
|
||||
|
||||
// If you ever need to access the service type, use `Database["Service"]`
|
||||
export type DatabaseService = Database["Service"]
|
||||
```
|
||||
|
||||
### More examples
|
||||
|
||||
- **[Context.Reference](./ai-docs/src/01_effect/03_services/10_reference.ts)**: For defining configuration values, feature flags, or any other service that has a default value.
|
||||
- **[Composing services with the Layer module](./ai-docs/src/01_effect/03_services/20_layer-composition.ts)**:
|
||||
Build focused service layers, then compose them with `Layer.provide` and
|
||||
`Layer.provideMerge` based on what services you want to expose.
|
||||
- **[Creating Layers from configuration and/or Effects](./ai-docs/src/01_effect/03_services/20_layer-unwrap.ts)**: Build a layer dynamically from an Effect / Config with `Layer.unwrap`.
|
||||
|
||||
## Error handling
|
||||
|
||||
### Error handling basics
|
||||
|
||||
Defining custom errors and handling them with Effect.catch and Effect.catchTag.
|
||||
|
||||
```ts
|
||||
import { Effect, Schema } from "effect"
|
||||
|
||||
// Define custom errors using Schema.TaggedErrorClass
|
||||
export class ParseError extends Schema.TaggedErrorClass<ParseError>()("ParseError", {
|
||||
input: Schema.String,
|
||||
message: Schema.String
|
||||
}) {}
|
||||
|
||||
export class ReservedPortError extends Schema.TaggedErrorClass<ReservedPortError>()("ReservedPortError", {
|
||||
port: Schema.Number
|
||||
}) {}
|
||||
|
||||
declare const loadPort: (input: string) => Effect.Effect<number, ParseError | ReservedPortError>
|
||||
|
||||
export const recovered = loadPort("80").pipe(
|
||||
// Catch multiple errors with Effect.catchTag, and return a default port number.
|
||||
Effect.catchTag(["ParseError", "ReservedPortError"], (_) => Effect.succeed(3000))
|
||||
)
|
||||
|
||||
export const withFinalFallback = loadPort("invalid").pipe(
|
||||
// Catch a specific error with Effect.catchTag
|
||||
Effect.catchTag("ReservedPortError", (_) => Effect.succeed(3000)),
|
||||
// Catch all errors with Effect.catch
|
||||
Effect.catch((_) => Effect.succeed(3000))
|
||||
)
|
||||
```
|
||||
|
||||
### More examples
|
||||
|
||||
- **[Catch multiple errors with Effect.catchTags](./ai-docs/src/01_effect/04_errors/10_catch-tags.ts)**: Use `Effect.catchTags` to handle several tagged errors in one place.
|
||||
- **[Creating and handling errors with reasons](./ai-docs/src/01_effect/04_errors/20_reason-errors.ts)**:
|
||||
Define a tagged error with a tagged `reason` field, then recover with
|
||||
`Effect.catchReason`, `Effect.catchReasons`, or by unwrapping the reason into
|
||||
the error channel with `Effect.unwrapReason`.
|
||||
|
||||
## Managing resources and `Scope`s
|
||||
|
||||
Learn how to safely manage resources in Effect using `Scope`s and finalizers.
|
||||
|
||||
- **[Acquiring resources with Effect.acquireRelease](./ai-docs/src/01_effect/05_resources/10_acquire-release.ts)**:
|
||||
Define a service that uses `Effect.acquireRelease` to manage the lifecycle of
|
||||
a resource, ensuring that it is properly cleaned up when the service is no
|
||||
longer needed.
|
||||
- **[Creating Layers that run background tasks](./ai-docs/src/01_effect/05_resources/20_layer-side-effects.ts)**: Use Layer.effectDiscard to encapsulate background tasks without a service interface.
|
||||
- **[Dynamic resources with LayerMap](./ai-docs/src/01_effect/05_resources/30_layer-map.ts)**:
|
||||
Use `LayerMap.Service` to dynamically build and manage resources that are
|
||||
keyed by some identifier, such as a tenant ID.
|
||||
|
||||
## Running Effect programs
|
||||
|
||||
- **[Running effects with NodeRuntime and BunRuntime](./ai-docs/src/01_effect/06_running/10_run-main.ts)**: Use `NodeRuntime.runMain` to run an Effect program as your process entrypoint.
|
||||
- **[Using Layer.launch as the application entry point](./ai-docs/src/01_effect/06_running/20_layer-launch.ts)**: Use `Layer.launch` to run a long-running Effect program as your process entrypoint.
|
||||
|
||||
## Broadcasting messages with PubSub
|
||||
|
||||
Use `PubSub` when you need one producer to fan out messages to many consumers.
|
||||
|
||||
- **[Broadcasting domain events with PubSub](./ai-docs/src/01_effect/07_pubsub/10_pubsub.ts)**: Build an in-process event bus with `PubSub` and expose it as a service.
|
||||
|
||||
## Working with Streams
|
||||
|
||||
Effect Streams represent effectful, pull-based sequences of values over time.
|
||||
They let you model finite or infinite data sources.
|
||||
|
||||
- **[Creating streams from common data sources](./ai-docs/src/03_stream/10_creating-streams.ts)**:
|
||||
Learn how to create streams from various data sources. Includes:
|
||||
|
||||
- `Stream.fromIterable` for arrays and other iterables
|
||||
- `Stream.fromEffectSchedule` for polling effects
|
||||
- `Stream.paginate` for paginated APIs
|
||||
- `Stream.fromAsyncIterable` for async iterables
|
||||
- `Stream.fromEventListener` for DOM events
|
||||
- `Stream.callback` for any callback-based API
|
||||
- `NodeStream.fromReadable` for Node.js readable streams
|
||||
- **[Consuming and transforming streams](./ai-docs/src/03_stream/20_consuming-streams.ts)**: How to transform and consume streams using operators like `map`, `flatMap`, `filter`, `mapEffect`, and various `run*` methods.
|
||||
- **[Decoding and encoding streams](./ai-docs/src/03_stream/30_encoding.ts)**:
|
||||
Use `Stream.pipeThroughChannel` with the `Ndjson` & `Msgpack` modules to
|
||||
decode and encode streams of structured data.
|
||||
|
||||
## Integrating Effect into existing applications
|
||||
|
||||
`ManagedRuntime` bridges Effect programs with non-Effect code. Build one runtime
|
||||
from your application Layer, then use it anywhere you need imperative execution,
|
||||
like web handlers, framework hooks, worker queues, or legacy callback APIs.
|
||||
|
||||
- **[Using ManagedRuntime with Hono](./ai-docs/src/04_integration/10_managed-runtime.ts)**: Use `ManagedRuntime` to run Effect programs from external frameworks while keeping your domain logic in services and Layers.
|
||||
|
||||
## Batching external requests
|
||||
|
||||
Learn how to batch multiple requests into fewer external calls.
|
||||
|
||||
- **[Batching requests with RequestResolver](./ai-docs/src/05_batching/10_request-resolver.ts)**: Define request types with `Request.Class`, resolve them in batches with `RequestResolver`.
|
||||
|
||||
## Working with Schedules
|
||||
|
||||
Schedules define recurring patterns for retries, repeats and polling.
|
||||
|
||||
- **[Working with the Schedule module](./ai-docs/src/06_schedule/10_schedules.ts)**: Build schedules, compose them, and use them with `Effect.retry` and `Effect.repeat`.
|
||||
|
||||
## Working with DateTime
|
||||
|
||||
When working with dates and time, use the `DateTime` module instead of `Date` and `Date.now`.
|
||||
|
||||
Use it when your Effect programs need testable current time, safe parsing, stable ISO formatting, time-zone conversion, or calendar arithmetic.
|
||||
|
||||
- **[Creating and formatting DateTime values](./ai-docs/src/07_datetime/10_creating-and-formatting.ts)**:
|
||||
Parse incoming date values safely, use Clock-powered current time, and format
|
||||
instants for API payloads or user-facing labels.
|
||||
- **[Working with time zones](./ai-docs/src/07_datetime/20_time-zones.ts)**:
|
||||
Attach IANA zones to instants, render zoned ISO strings, and provide a
|
||||
CurrentTimeZone service for code that should use the workspace/user zone.
|
||||
|
||||
## Observability
|
||||
|
||||
Effect has built-in support for structured logging, distributed tracing, and
|
||||
metrics. For exporting telemetry, use the lightweight Otlp modules from
|
||||
`effect/unstable/observability` in new projects, or use
|
||||
`@effect/opentelemetry` NodeSdk when integrating with an existing OpenTelemetry
|
||||
setup.
|
||||
|
||||
- **[Customizing logging](./ai-docs/src/08_observability/10_logging.ts)**: Configure loggers & log-level filtering for production applications.
|
||||
- **[Setting up tracing with Otlp modules](./ai-docs/src/08_observability/20_otlp-tracing.ts)**: Configure Otlp tracing + log export with a reusable observability layer.
|
||||
|
||||
## Testing Effect programs
|
||||
|
||||
- **[Writing Effect tests with @effect/vitest](./ai-docs/src/09_testing/10_effect-tests.ts)**: Using `it.effect` for Effect-based tests.
|
||||
- **[Testing services with shared layers](./ai-docs/src/09_testing/20_layer-tests.ts)**: How to test Effect services that depend on other services.
|
||||
|
||||
## Runtime type guards
|
||||
|
||||
The `Predicate` module contains small, reusable runtime checks.
|
||||
|
||||
**NEVER** write your own helper functions like `isRecord` or `isString`, instead
|
||||
use the helpers from the `Predicate` module.
|
||||
|
||||
Predicates can be composed with apis such as `Predicate.and`,
|
||||
`Predicate.or`, `Predicate.not`, and `Predicate.compose`.
|
||||
|
||||
### Using the Predicate module
|
||||
|
||||
|
||||
|
||||
```ts
|
||||
import { Predicate } from "effect"
|
||||
|
||||
const thing: unknown = {
|
||||
a: 1
|
||||
}
|
||||
|
||||
if (Predicate.isObject(thing)) {
|
||||
if (Predicate.isNumber(thing.a)) {
|
||||
console.log("number", thing.a)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Effect HttpClient
|
||||
|
||||
Build http clients with the `HttpClient` module.
|
||||
|
||||
- **[Getting started with HttpClient](./ai-docs/src/50_http-client/10_basics.ts)**: Define a service that uses the HttpClient module to fetch data from an external API
|
||||
|
||||
## Building HttpApi servers
|
||||
|
||||
`HttpApi` gives you schema-first, type-safe HTTP APIs with runtime validation, typed clients, and OpenAPI docs from one definition.
|
||||
|
||||
- **[Getting started with HttpApi](./ai-docs/src/51_http-server/10_basics.ts)**:
|
||||
Define a schema-first API, implement handlers, secure endpoints with
|
||||
middleware, serve it over HTTP, and call it using a generated typed client.
|
||||
|
||||
## Working with child processes
|
||||
|
||||
Use the `effect/unstable/process` modules to define child processes and run them with `ChildProcessSpawner`.
|
||||
|
||||
- **[Working with child processes](./ai-docs/src/60_child-process/10_working-with-child-processes.ts)**: This example shows how to collect process output, compose pipelines, and stream long-running command output.
|
||||
|
||||
## Building CLI applications
|
||||
|
||||
Use the "effect/unstable/cli" modules to build CLI applications. These modules
|
||||
provide utilities for parsing command-line arguments, handling user input, and
|
||||
managing the flow of a CLI application.
|
||||
|
||||
- **[Getting started with Effect CLI modules](./ai-docs/src/70_cli/10_basics.ts)**:
|
||||
Build a command-line app with typed arguments and flags, then wire subcommand
|
||||
handlers into a single executable command.
|
||||
|
||||
## Working with AI modules
|
||||
|
||||
Effect's AI modules provide a provider-agnostic interface for language models.
|
||||
You can generate text, decode structured objects with `Schema` and stream partial
|
||||
responses.
|
||||
|
||||
- **[Using LanguageModel for text, objects, and streams](./ai-docs/src/71_ai/10_language-model.ts)**:
|
||||
Configure a provider once, then use `LanguageModel` for plain text
|
||||
generation, schema-validated object generation, and streaming responses.
|
||||
- **[Defining and using AI tools](./ai-docs/src/71_ai/20_tools.ts)**:
|
||||
Define tools with schemas, group them into toolkits, implement handlers,
|
||||
and pass them to `LanguageModel.generateText`.
|
||||
- **[Stateful chat sessions](./ai-docs/src/71_ai/30_chat.ts)**:
|
||||
The AI `Chat` module maintains conversation history automatically. Build
|
||||
AI agents or chat assistants.
|
||||
|
||||
## Building distributed applications with cluster
|
||||
|
||||
The cluster modules let you model stateful services as entities and distribute
|
||||
them across multiple machines.
|
||||
|
||||
- **[Defining cluster entities](./ai-docs/src/80_cluster/10_entities.ts)**: Define distributed entity RPCs and run them in a cluster.
|
||||
Reference in New Issue
Block a user