### Install and Run Development Servers Source: https://github.com/kitlangton/effect-solutions/blob/main/CLAUDE.md Commands to install dependencies and start development servers for the website and CLI. ```bash bun install # Install dependencies bun run dev # Website dev server bun run dev:cli # CLI dev mode ``` -------------------------------- ### Start Development Server Source: https://github.com/kitlangton/effect-solutions/blob/main/AGENTS.md Starts the website development server. ```bash bun run dev ``` -------------------------------- ### Documentation Testing Setup Source: https://github.com/kitlangton/effect-solutions/blob/main/CONTRIBUTING.md Create a test file in 'tests/' that mirrors your documentation filename. Use 'vitest' for testing Effect code examples. ```typescript import { describe, expect, it } from "vitest" import { Effect } from "effect" describe("CLI Documentation Examples", () => { it("should demonstrate the pattern correctly", async () => { // Test your example code here }) }) ``` -------------------------------- ### GitHub API Client Example Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/11-http-clients.md An example demonstrating how to define and use a GitHub API client with Effect, including methods for getting user and repository information. ```APIDOC ## GitHubApi Service ### Description Defines a service for interacting with the GitHub API, providing methods to fetch user data, repository details, and lists of repositories. ### Methods #### getUser Fetches a user's profile information. - **Parameters** - `username` (string) - The username of the GitHub user. - **Returns** - `Effect.Effect` - An effect that yields the User object or an error. #### getRepo Fetches details for a specific repository. - **Parameters** - `owner` (string) - The owner of the repository. - `repo` (string) - The name of the repository. - **Returns** - `Effect.Effect` - An effect that yields the Repo object or an error. #### listRepos Lists all public repositories for a given user. - **Parameters** - `username` (string) - The username of the GitHub user. - **Returns** - `Effect.Effect, unknown>` - An effect that yields an array of Repo objects or an error. ### Example Usage ```typescript const program = Effect.gen(function* () { const github = yield* GitHubApi const user = yield* github.getUser("effect-ts") console.log(`${user.login} has ${user.public_repos} public repos`) const repo = yield* github.getRepo("Effect-TS", "effect") console.log(`${repo.full_name}: ${repo.stargazers_count} stars`) const repos = yield* github.listRepos("effect-ts") console.log(`First 3 repos: ${repos.slice(0, 3).map((r) => r.name).join(", ")}`) }) program.pipe(Effect.provide(GitHubApi.live), Effect.runPromise) ``` ``` -------------------------------- ### Install Dependencies Source: https://github.com/kitlangton/effect-solutions/blob/main/AGENTS.md Installs project dependencies using Bun. ```bash bun install ``` -------------------------------- ### List Available Guides Source: https://github.com/kitlangton/effect-solutions/blob/main/CLAUDE.md Command to list all available documentation guides provided by the effect-solutions CLI. ```bash effect-solutions list ``` -------------------------------- ### Example Frontmatter for Documentation Source: https://github.com/kitlangton/effect-solutions/blob/main/CLAUDE.md Example frontmatter configuration for a documentation file, specifying title, description, order, and group. ```yaml --- title: Command-Line Interfaces description: "Build CLIs with @effect/cli" order: 13 group: Ecosystem --- ``` -------------------------------- ### Install @effect/vitest Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/08-testing.md Install vitest and @effect/vitest as development dependencies. ```bash bun add -D vitest @effect/vitest@beta ``` -------------------------------- ### Start CLI Development Mode Source: https://github.com/kitlangton/effect-solutions/blob/main/AGENTS.md Starts the CLI in development mode. ```bash bun run dev:cli ``` -------------------------------- ### Install @effect/opentelemetry Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/12-observability.md Install the necessary packages for OpenTelemetry integration. ```bash bun add @effect/opentelemetry effect@beta @opentelemetry/api ``` -------------------------------- ### Install effect-solutions CLI Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/00-quick-start.md Install the effect-solutions CLI globally using bun. This tool provides access to documentation and other utilities from the command line. ```bash bun add -g effect-solutions@latest ``` -------------------------------- ### Run Local Development Server and Checks Source: https://github.com/kitlangton/effect-solutions/blob/main/CONTRIBUTING.md Start the local development server to preview documentation changes and run tests and checks to ensure code integrity. ```bash bun run dev # Start dev server bun run test # Run tests bun run check # Run linting and type checks ``` -------------------------------- ### Example CLI Usage: Task Manager Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/13-cli.md Demonstrates various commands for the task manager CLI, including adding, listing, toggling, and clearing tasks, with expected output. ```bash tasks add "Buy milk" # Added task #1: Buy milk tasks add "Walk the dog" # Added task #2: Walk the dog tasks list # [ ] #1 Buy milk # [ ] #2 Walk the dog tasks toggle 1 # Toggled: Buy milk (done) tasks list # [ ] #2 Walk the dog tasks list --all # [x] #1 Buy milk # [ ] #2 Walk the dog tasks toggle 1 # Toggled: Buy milk (pending) tasks clear # Cleared all tasks. ``` -------------------------------- ### Minimal Example Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/11-http-clients.md Demonstrates fetching a GitHub repository and decoding its response using Schema. It highlights the basic usage of `HttpClient.get` and `HttpClientResponse.schemaBodyJson`, along with providing the `FetchHttpClient` layer. ```APIDOC ## HttpClient.get ### Description Fetches a resource from the specified URL. ### Method GET ### Endpoint `https://api.github.com/repos/Effect-TS/effect` ### Response #### Success Response (200) - **repo** (object) - Decoded repository data matching the `Repo` schema. ### Request Example ```typescript import { FetchHttpClient, HttpClient, HttpClientResponse } from "effect/unstable/http" import { Effect, Schema } from "effect" const Repo = Schema.Struct({ id: Schema.Number, name: Schema.String, full_name: Schema.String, stargazers_count: Schema.Number, }) const program = Effect.gen(function* () { const response = yield* HttpClient.get("https://api.github.com/repos/Effect-TS/effect") const repo = yield* HttpClientResponse.schemaBodyJson(Repo)(response) console.log(`${repo.full_name}: ${repo.stargazers_count} stars`) }) program.pipe(Effect.provide(FetchHttpClient.layer), Effect.runPromise) ``` ``` -------------------------------- ### Install Effect CLI Dependencies Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/13-cli.md Install the necessary Effect CLI and platform packages using Bun. For Node.js, use `@effect/platform-node@beta` instead. ```bash bun add effect@beta @effect/platform-bun@beta ``` -------------------------------- ### Example Documentation File Structure Source: https://github.com/kitlangton/effect-solutions/blob/main/CONTRIBUTING.md Documentation files are organized in 'packages/website/docs/' and follow a 'NN-slug.md' naming convention for sorting and URL path. ```markdown NN-slug.md # NN = sort order (00-99), slug = URL path ``` -------------------------------- ### Show Specific Topic Guide Source: https://github.com/kitlangton/effect-solutions/blob/main/CLAUDE.md Command to display documentation for one or more specific topics using their slugs. ```bash effect-solutions show ... ``` -------------------------------- ### Example YAML Frontmatter for Docs Source: https://github.com/kitlangton/effect-solutions/blob/main/CONTRIBUTING.md All documentation files require YAML frontmatter with 'title', 'description', and 'order'. 'group' is optional for organizing docs. ```yaml --- title: Command-Line Interfaces description: "Build CLIs with @effect/cli" order: 13 group: Ecosystem --- ``` -------------------------------- ### Run Documentation Tests Source: https://github.com/kitlangton/effect-solutions/blob/main/CLAUDE.md Executes documentation tests using Vitest and @effect/vitest. Ensures all code examples are valid and functional. ```bash bunx vitest run tests/ ``` -------------------------------- ### Minimal Greeting CLI Example Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/13-cli.md A basic CLI that greets a user by name, with an option to shout. Built-in flags like `--help` and `--version` are automatically handled. ```typescript import { Argument, Command, Flag } from "effect/unstable/cli" import { BunServices, BunRuntime } from "@effect/platform-bun" import { Console, Effect } from "effect" const name = Argument.string("name").pipe(Argument.withDefault("World")) const shout = Flag.boolean("shout").pipe(Flag.withAlias("s")) const greet = Command.make("greet", { name, shout }, ({ name, shout }) => { const message = `Hello, ${name}!` return Console.log(shout ? message.toUpperCase() : message) }) const program = Command.run(greet, { version: "1.0.0" }) program.pipe( Effect.provide(BunServices.layer), BunRuntime.runMain ) ``` -------------------------------- ### Testing a Full Service Stack Source: https://context7.com/kitlangton/effect-solutions/llms.txt Demonstrates how to use `Layer.provideMerge` to expose leaf services for test setup and assertion while maintaining the production layer structure. ```APIDOC ## Testing a Full Service Stack Use `Layer.provideMerge` to expose leaf services for test setup and assertion while keeping the production layer structure intact. ```typescript import { Clock, Effect, Layer, Option, Schema } from "effect" import * as Context from "effect/Context" import { describe, expect, it } from "@effect/vitest" // --- Domain --- const UserId = Schema.String.pipe(Schema.brand("UserId")) type UserId = typeof UserId.Type const EventId = Schema.String.pipe(Schema.brand("EventId")) type EventId = typeof EventId.Type class User extends Schema.Class("User")({ id: UserId, name: Schema.String, email: Schema.String }) {} class UserNotFound extends Schema.TaggedErrorClass()("UserNotFound", { id: UserId }) {} class Users extends Context.Service< Users, { readonly create: (user: User) => Effect.Effect readonly findById: (id: UserId) => Effect.Effect } >()("@app/Users") { static readonly testLayer = Layer.sync(Users, () => { const store = new Map() return { create: (user) => Effect.sync(() => void store.set(user.id, user)), findById: (id) => Option.fromNullishOr(store.get(id)).pipe( Effect.fromOption, Effect.catch(() => Effect.fail(new UserNotFound({ id }))) ) } }) } // Hypothetical Events service layer (depends on Users) declare class Events extends Context.Service< Events, { readonly register: (eventId: EventId, userId: UserId) => Effect.Effect<{ eventId: EventId; userId: UserId }> } > { static readonly layer: Layer.Layer } const testLayer = Events.layer.pipe(Layer.provideMerge(Users.testLayer)) describe("Events.register", () => { it.effect("registers user for event", () => Effect.gen(function* () { const users = yield* Users const events = yield* Events const user = new User({ id: UserId.make("u1"), name: "Alice", email: "alice@example.com" }) yield* users.create(user) const reg = yield* events.register(EventId.make("e1"), user.id) expect(reg.userId).toBe(user.id) }).pipe(Effect.provide(testLayer)) ) }) ``` ``` -------------------------------- ### Install Effect Language Service Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/01-project-setup.md Installs the Effect Language Service as a development dependency. Ensure you are in your project's root directory. ```bash bun add -d @effect/language-service ``` -------------------------------- ### Example Draft Documentation Frontmatter Source: https://github.com/kitlangton/effect-solutions/blob/main/CONTRIBUTING.md Mark documentation as a draft by setting 'draft: true' in the frontmatter. Drafts are only visible in development mode. ```yaml --- title: Incremental Adoption description: "Strategies for gradually introducing Effect into existing codebases" order: 10 draft: true --- ``` -------------------------------- ### Running Effect CLI Subcommands Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/13-cli.md Examples of how to invoke subcommands defined within an Effect CLI application. ```bash tasks add "Buy milk" # Adding: Buy milk tasks list # Listing tasks... tasks --help # Shows available subcommands ``` -------------------------------- ### Task Persistence Example Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/13-cli.md Shows the JSON structure of persisted tasks, including their ID, text, and completion status. ```json [ { "id": 1, "text": "Buy milk", "done": true }, { "id": 2, "text": "Walk the dog", "done": false } ] ``` -------------------------------- ### CLI Commands for Effect Solutions Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/00-quick-start.md Use the effect-solutions CLI to list all available topics, show specific topics like project setup or tsconfig, and open issues for feedback. ```bash effect-solutions list ``` ```bash effect-solutions show project-setup tsconfig ``` ```bash effect-solutions open-issue ``` -------------------------------- ### Configure ConfigProvider from Unknown Data Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/07-config.md Create a `ConfigProvider` from an unknown object, useful for providing configuration in tests or specific environments. This example sets API key and port values. ```typescript import { ConfigProvider, Effect, Layer } from "effect" const program = Effect.void const testConfigLayer = ConfigProvider.layer( ConfigProvider.fromUnknown({ API_KEY: "test-key", PORT: "3000", }) ) const jsonConfigLayer = ConfigProvider.layer( ConfigProvider.fromUnknown({ API_KEY: "prod-key", PORT: 8080, }) ) // Usage: provide whichever layer matches the environment Effect.runPromise(program.pipe(Effect.provide(testConfigLayer))) ``` -------------------------------- ### Basic Effect Succeed Example Source: https://github.com/kitlangton/effect-solutions/blob/main/CONTRIBUTING.md Demonstrates the basic usage of Effect.succeed to create a successful effect. Ensure the 'effect' library is imported. ```typescript import { Effect } from "effect" const program = Effect.succeed(42) ``` -------------------------------- ### Implement Services with Effect Layers Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/04-services-and-layers.md Implement services using Effect Layers, handling setup, dependency resolution, and resource lifecycle. Use Effect.fn for call-site tracing. ```typescript import { HttpClient, HttpClientError, HttpClientResponse } from "effect/unstable/http" import { Effect, Layer, Schema } from "effect" import * as Context from "effect/Context" const UserId = Schema.String.pipe(Schema.brand("UserId")) type UserId = typeof UserId.Type class User extends Schema.Class("User") ({ id: UserId, name: Schema.String, email: Schema.String, }) {} class UserNotFoundError extends Schema.TaggedErrorClass()( "UserNotFoundError", { id: UserId, } ) {} class GenericUsersError extends Schema.TaggedErrorClass()( "GenericUsersError", { id: UserId, error: Schema.Defect, } ) {} const UsersError = Schema.Union([UserNotFoundError, GenericUsersError]) type UsersError = typeof UsersError.Type class Analytics extends Context.Service< Analytics, { readonly track: (event: string, data: Record) => Effect.Effect } >()("@app/Analytics") {} class Users extends Context.Service< Users, { readonly findById: (id: UserId) => Effect.Effect readonly all: () => Effect.Effect } >()("@app/Users") { static readonly layer = Layer.effect( Users, Effect.gen(function* () { // 1. yield* services you depend on const http = yield* HttpClient.HttpClient const analytics = yield* Analytics // 2. define the service methods with Effect.fn for call-site tracing const findById = Effect.fn("Users.findById")( (id: UserId): Effect.Effect => Effect.gen(function* () { yield* analytics.track("user.find", { id }) const response = yield* http.get(`https://api.example.com/users/${id}`) return yield* HttpClientResponse.schemaBodyJson(User)(response) }).pipe( Effect.catch((error): Effect.Effect => { if (HttpClientError.isHttpClientError(error)) { if (error.reason._tag === "StatusCodeError" && error.reason.response.status === 404) { return Effect.fail(new UserNotFoundError({ id })) } return Effect.fail(new GenericUsersError({ id, error })) } return Effect.fail(error) }), ), ) // Use Effect.fn even for nullary methods (thunks) to enable tracing const all = Effect.fn("Users.all")(function* () { const response = yield* http.get("https://api.example.com/users") return yield* HttpClientResponse.schemaBodyJson(Schema.Array(User))(response) }) // 3. return the service return { findById, all } }) ) } ``` -------------------------------- ### Effect TypeScript HTTP Client Setup Source: https://github.com/kitlangton/effect-solutions/blob/main/README.md Sets up an HTTP client using Effect and provides it with the FetchHttpClient layer. This is a common pattern for making HTTP requests within an Effect application. ```typescript import { Effect, HttpClient } from "effect" import { FetchHttpClient } from "@effect/platform" const program = Effect.gen(function* () { const http = yield* HttpClient.HttpClient const result = yield* http.get("/api/users") }) program.pipe( Effect.provide(FetchHttpClient.layer) ) ``` -------------------------------- ### Test Implementations for Services Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/04-services-and-layers.md Use `Effect.sync` or `Effect.succeed` for test implementations when async operations are not required. These examples show how to create test layers for `Database` and `Cache` services. ```typescript import { Console, Effect, Layer } from "effect" import * as Context from "effect/Context" class Database extends Context.Service< Database, { readonly query: (sql: string) => Effect.Effect readonly execute: (sql: string) => Effect.Effect } >()("@app/Database") { static readonly testLayer = Layer.sync(Database, () => { let records: Record = { "user-1": { id: "user-1", name: "Alice" }, "user-2": { id: "user-2", name: "Bob" }, } const query = (sql: string) => Effect.succeed(Object.values(records)) const execute = (sql: string) => Console.log(`Test execute: ${sql}`) return { query, execute } }) } class Cache extends Context.Service< Cache, { readonly get: (key: string) => Effect.Effect readonly set: (key: string, value: string) => Effect.Effect } >()("@app/Cache") { static readonly testLayer = Layer.sync(Cache, () => { const store = new Map() const get = (key: string) => Effect.succeed(store.get(key) ?? null) const set = (key: string, value: string) => Effect.sync(() => void store.set(key, value)) return { get, set } }) } ``` -------------------------------- ### Testing Service Stacks with Layer.provideMerge Source: https://context7.com/kitlangton/effect-solutions/llms.txt Use `Layer.provideMerge` to expose leaf services for test setup and assertion while keeping the production layer structure intact. This example sets up a test layer for an `Events` service that depends on a `Users` service. ```typescript import { Clock, Effect, Layer, Option, Schema } from "effect" import * as Context from "effect/Context" import { describe, expect, it } from "@effect/vitest" // --- Domain --- const UserId = Schema.String.pipe(Schema.brand("UserId")) type UserId = typeof UserId.Type const EventId = Schema.String.pipe(Schema.brand("EventId")) type EventId = typeof EventId.Type class User extends Schema.Class("User")({ id: UserId, name: Schema.String, email: Schema.String }) {} class UserNotFound extends Schema.TaggedErrorClass()("UserNotFound", { id: UserId }) {} class Users extends Context.Service Effect.Effect readonly findById: (id: UserId) => Effect.Effect }>()("@app/Users") { static readonly testLayer = Layer.sync(Users, () => { const store = new Map() return { create: (user) => Effect.sync(() => void store.set(user.id, user)), findById: (id) => Option.fromNullishOr(store.get(id)).pipe( Effect.fromOption, Effect.catch(() => Effect.fail(new UserNotFound({ id }))) ) } }) } // Hypothetical Events service layer (depends on Users) declare class Events extends Context.Service Effect.Effect<{ eventId: EventId; userId: UserId }> }> { static readonly layer: Layer.Layer } const testLayer = Events.layer.pipe(Layer.provideMerge(Users.testLayer)) describe("Events.register", () => { it.effect("registers user for event", () => Effect.gen(function* () { const users = yield* Users const events = yield* Events const user = new User({ id: UserId.make("u1"), name: "Alice", email: "alice@example.com" }) yield* users.create(user) const reg = yield* events.register(EventId.make("e1"), user.id) expect(reg.userId).toBe(user.id) }).pipe(Effect.provide(testLayer)) ) }) ``` -------------------------------- ### Running the Minimal Greeting CLI Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/13-cli.md Demonstrates how to run the minimal greeting CLI with different arguments and flags. ```bash bun run greet.ts # Hello, World! bun run greet.ts Alice # Hello, Alice! bun run greet.ts --shout Bob # HELLO, BOB! bun run greet.ts --help # Shows usage ``` -------------------------------- ### Create Test File for New Documentation Source: https://github.com/kitlangton/effect-solutions/blob/main/CONTRIBUTING.md Create a corresponding test file in the 'tests/' directory for any new documentation file added. ```bash touch tests/14-your-topic.test.ts ``` -------------------------------- ### Setup Test Layer for Services Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/08-testing.md Exposes leaf services in tests for setup and assertions. This layer merges the test layers of Users, Tickets, and Emails. ```typescript const testLayer = Events.layer.pipe( Layer.provideMerge(Users.testLayer), Layer.provideMerge(Tickets.testLayer), Layer.provideMerge(Emails.testLayer) ) ``` -------------------------------- ### Create a Changeset Source: https://github.com/kitlangton/effect-solutions/blob/main/CONTRIBUTING.md Generate a changeset file to document the changes for package versioning and changelog generation. Replace 'description' with a summary of your changes. ```bash bun scripts/changeset-named.ts "description" ``` -------------------------------- ### Persist TypeScript Patch with npm prepare script Source: https://context7.com/kitlangton/effect-solutions/llms.txt Add a 'prepare' script to your package.json to automatically re-apply the TypeScript patch after package installations or updates. ```json // package.json — persist the patch across installs { "scripts": { "prepare": "effect-language-service patch" } } ``` -------------------------------- ### Format Website Documentation Source: https://github.com/kitlangton/effect-solutions/blob/main/AGENTS.md Formats the website and documentation files. ```bash bun --cwd packages/website run format ``` -------------------------------- ### GitHub API Client Service Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/11-http-clients.md A complete example of building a typed API client for the GitHub API using Effect's service pattern. ```APIDOC ## Service GitHubApi ### Description Provides methods to interact with the GitHub API. ### Methods - **getUser**: (username: string) => Effect.Effect Fetches a user by username. - **getRepo**: (owner: string, repo: string) => Effect.Effect Fetches a repository by owner and name. - **listRepos**: (username: string) => Effect.Effect, unknown> Lists repositories for a given user. ### Base URL `https://api.github.com` ### Schemas - **User**: Represents a GitHub user. - **Repo**: Represents a GitHub repository. ``` -------------------------------- ### Show specific CLI topics Source: https://context7.com/kitlangton/effect-solutions/llms.txt Displays documentation for one or more specific topics using the effect-solutions CLI. ```bash effect-solutions show project-setup tsconfig ``` -------------------------------- ### Send traces and logs to OTLP Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/12-observability.md Configure and provide the OpenTelemetry layer to send traces and logs to an OTLP collector. This example uses the FetchHttpClient for HTTP export. ```typescript import { Effect, Layer } from "effect" import { BunHttpServer } from "@effect/platform-bun/HttpServer" import { HttpServerResponse, FetchHttpClient } from "effect/unstable/http" import { Otlp, Tracer } from "@effect/opentelemetry" const otelLayer = Otlp.layer({ baseUrl: "https://otel-collector.company.dev", resource: { serviceName: "effect-app", serviceVersion: "0.1.0" } }).pipe( Layer.provide(FetchHttpClient.layer) // HTTP export ) const app = HttpServerResponse.text("ok").pipe( Effect.withSpan("http.request") ) Effect.runPromise( BunHttpServer.serve(app, { port: 3000 }).pipe( Tracer.withSpan("server"), Effect.provide(otelLayer) ) ) ``` -------------------------------- ### Handle Multiple Errors with Effect.catchTags Source: https://context7.com/kitlangton/effect-solutions/llms.txt Use Effect.catchTags to handle multiple specific error types. This example recovers from both HttpError and ValidationError, resulting in a 'never' error channel. ```typescript import { Effect, Schema } from "effect" class HttpError extends Schema.TaggedErrorClass()( "HttpError", { statusCode: Schema.Number, message: Schema.String } ) {} class ValidationError extends Schema.TaggedErrorClass()( "ValidationError", { message: Schema.String } ) {} const program: Effect.Effect = Effect.fail(new HttpError({ statusCode: 500, message: "Internal server error" })) // Handle all tags — error channel becomes never const recovered: Effect.Effect = program.pipe( Effect.catchTags({ HttpError: () => Effect.succeed("recovered from HttpError"), ValidationError: () => Effect.succeed("recovered from ValidationError"), }) ) ``` -------------------------------- ### Recommended Config Layer Pattern Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/07-config.md Illustrates the best practice of creating a dedicated config service with a `layer` export for dependency injection. Includes loading from environment variables with fallbacks and a separate test layer. ```typescript import { Config, Effect, Layer, Redacted } from "effect" import * as Context from "effect/Context" class ApiConfig extends Context.Service< ApiConfig, { readonly apiKey: Redacted.Redacted readonly baseUrl: string readonly timeout: number } >()("@app/ApiConfig") { static readonly layer = Layer.effect( ApiConfig, Effect.gen(function* () { const apiKey = yield* Config.redacted("API_KEY") const baseUrl = yield* Config.string("API_BASE_URL").pipe( Config.orElse(() => Config.succeed("https://api.example.com")) ) const timeout = yield* Config.int("API_TIMEOUT").pipe( Config.orElse(() => Config.succeed(30000)) ) return { apiKey, baseUrl, timeout } }) ) // For tests - hardcoded values static readonly testLayer = Layer.succeed( ApiConfig, { apiKey: Redacted.make("test-key"), baseUrl: "https://test.example.com", timeout: 5000, } ) } ``` -------------------------------- ### Handle Specific Errors with Effect.catchTag Source: https://context7.com/kitlangton/effect-solutions/llms.txt Use Effect.catchTag to recover from a specific error type by its _tag. This example shows handling HttpError while leaving ValidationError in the error channel. ```typescript import { Effect, Schema } from "effect" class HttpError extends Schema.TaggedErrorClass()( "HttpError", { statusCode: Schema.Number, message: Schema.String } ) {} class ValidationError extends Schema.TaggedErrorClass()( "ValidationError", { message: Schema.String } ) {} const program: Effect.Effect = Effect.fail(new HttpError({ statusCode: 500, message: "Internal server error" })) // Handle one tag — leaves ValidationError in the error channel const partial: Effect.Effect = program.pipe( Effect.catchTag("HttpError", (e) => Effect.gen(function* () { yield* Effect.logWarning(`HTTP ${e.statusCode}: ${e.message}`) return "recovered from HttpError" }) ) ) ``` -------------------------------- ### Development Settings Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/02-tsconfig.md Enable declaration maps, source maps, and skip library checking for better debugging and faster builds. ```jsonc "declarationMap": true, "sourceMap": true, "skipLibCheck": true, ``` -------------------------------- ### Basic Config Loading from Environment Variables Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/07-config.md Demonstrates reading API key and port from environment variables using Config.redacted and Config.int. Environment variables are the default source. ```typescript import { Config, Effect } from "effect" const program = Effect.gen(function* () { // Reads from process.env.API_KEY and process.env.PORT const apiKey = yield* Config.redacted("API_KEY") const port = yield* Config.int("PORT") console.log(`Starting server on port ${port}`) // apiKey is redacted in logs }) // Run with default provider (environment variables) Effect.runPromise(program) ``` -------------------------------- ### Client Middleware: Base URL and Authentication Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/11-http-clients.md Demonstrates how to configure an HTTP client with a base URL, bearer token authentication, and custom headers using middleware. ```APIDOC ## GitHubClient Layer ### Description Provides a configured `HttpClient` layer for the GitHub API, setting the base URL, bearer token, and Accept header for all requests. ### Configuration - **Base URL**: `https://api.github.com` - **Authentication**: Bearer Token (`ghp_xxxx`) - **Headers**: `Accept: application/vnd.github.v3+json` ### Usage ```typescript const GitHubClient = Layer.effect( HttpClient.HttpClient, Effect.gen(function* () { const baseClient = yield* HttpClient.HttpClient return baseClient.pipe( HttpClient.mapRequest( flow( HttpClientRequest.prependUrl("https://api.github.com"), HttpClientRequest.bearerToken("ghp_xxxx"), HttpClientRequest.setHeader("Accept", "application/vnd.github.v3+json") ) ) ) }) ).pipe(Layer.provide(FetchHttpClient.layer)) ``` ``` -------------------------------- ### Build Performance Settings Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/02-tsconfig.md Enable incremental and composite builds for faster rebuilds and monorepo project references. ```jsonc "incremental": true, "composite": true, ``` -------------------------------- ### Define Domain Types and Services with Test Layers Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/08-testing.md Defines domain types and services with built-in test layers. This setup is crucial for creating isolated and reproducible tests. ```typescript import { Clock, Effect, Layer, Option, Schema } from "effect" import * as Context from "effect/Context" import { describe, expect, it } from "@effect/vitest" // Domain types const RegistrationId = Schema.String.pipe(Schema.brand("RegistrationId")) type RegistrationId = typeof RegistrationId.Type const EventId = Schema.String.pipe(Schema.brand("EventId")) type EventId = typeof EventId.Type const UserId = Schema.String.pipe(Schema.brand("UserId")) type UserId = typeof UserId.Type const TicketId = Schema.String.pipe(Schema.brand("TicketId")) type TicketId = typeof TicketId.Type class User extends Schema.Class("User")({ id: UserId, name: Schema.String, email: Schema.String, }) {} class Registration extends Schema.Class("Registration")({ id: RegistrationId, eventId: EventId, userId: UserId, ticketId: TicketId, registeredAt: Schema.Date, }) {} class Ticket extends Schema.Class("Ticket")({ id: TicketId, eventId: EventId, code: Schema.String, }) class Email extends Schema.Class("Email")({ to: Schema.String, subject: Schema.String, body: Schema.String, }) class UserNotFound extends Schema.TaggedErrorClass()("UserNotFound", { id: UserId, }) {} // Users service with test layer that has create + findById class Users extends Context.Service Effect.Effect readonly findById: (id: UserId) => Effect.Effect }>()("@app/Users") { // Mutable state is fine in tests - JS is single-threaded static readonly testLayer = Layer.sync(Users, () => { const store = new Map() const create = (user: User) => Effect.sync(() => void store.set(user.id, user)) const findById = (id: UserId) => Option.fromNullishOr(store.get(id)).pipe( Effect.fromOption, Effect.catch(() => Effect.fail(new UserNotFound({ id }))) ) return { create, findById } }) } // Tickets service with test layer class Tickets extends Context.Service Effect.Effect }>()("@app/Tickets") { static readonly testLayer = Layer.sync(Tickets, () => { let counter = 0 const issue = (eventId: EventId, _userId: UserId) => Effect.sync(() => new Ticket({ id: TicketId.make(`ticket-${counter++}`), eventId, code: `CODE-${counter}`, }) ) return { issue } }) } // Emails service with test layer that tracks sent emails class Emails extends Context.Service Effect.Effect readonly sent: Effect.Effect> }>()("@app/Emails") { static readonly testLayer = Layer.sync(Emails, () => { const emails: Array = [] const send = (email: Email) => Effect.sync(() => void emails.push(email)) const sent = Effect.sync(() => emails) return { send, sent } }) } ``` -------------------------------- ### Create New Documentation Markdown File Source: https://github.com/kitlangton/effect-solutions/blob/main/CONTRIBUTING.md Create a new markdown file for documentation in the 'packages/website/docs/' directory, following the 'NN-slug.md' naming convention. ```bash # Choose next available number in sequence touch packages/website/docs/14-your-topic.md ``` -------------------------------- ### CLI Commands for Effect Solutions Source: https://github.com/kitlangton/effect-solutions/blob/main/README.md Commands to list all topics, show a specific topic, or open an issue for feedback. ```bash bunx effect-solutions list # List all topics bunx effect-solutions show http-clients # Show specific topic bunx effect-solutions open-issue # Leave feedback ``` -------------------------------- ### Run generate-manifest Script Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/cli/scripts/README.md Manually execute the `generate-manifest.ts` script using Bun. This script auto-generates `src/docs-manifest.ts` by scanning markdown files. ```bash bun run generate:manifest ``` -------------------------------- ### Config Defaults and Fallbacks with orElse and option Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/07-config.md Demonstrates how to provide default values or fallbacks for configuration variables using `Config.orElse` and how to handle optional configuration values using `Config.option`. ```typescript import { Config, Effect } from "effect" const program = Effect.gen(function* () { // With orElse const port = yield* Config.int("PORT").pipe( Config.orElse(() => Config.succeed(3000)) ) // Optional values const optionalKey = yield* Config.option(Config.string("OPTIONAL_KEY")) // Returns Option return { port, optionalKey } }) ``` -------------------------------- ### Run All Project Tests and Type Checking Source: https://github.com/kitlangton/effect-solutions/blob/main/AGENTS.md Execute all project tests, including documentation tests, and perform type checking. ```bash bun run test ``` ```bash bunx tsc --build --force ``` ```bash bun run typecheck:docs ``` -------------------------------- ### Persist Build-Time Diagnostics Patch Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/01-project-setup.md Adds a `prepare` script to package.json to automatically apply the Effect Language Service patch after installations or updates, ensuring build-time diagnostics are always enabled. ```json { "scripts": { "prepare": "effect-language-service patch" } } ``` -------------------------------- ### Usage of FileSystem Service Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/14-use-pattern.md Demonstrates how to use the FileSystem service to perform file operations like reading, writing, and listing directory contents. The `use` method is called with callbacks that interact with `fs/promises` and receive an `AbortSignal`. ```typescript import { Effect, pipe } from "effect" // hide-start import { Layer, Schema } from "effect" import * as Context from "effect/Context" import * as fs_ from "node:fs/promises" class FileSystemError extends Schema.TaggedErrorClass()( "FileSystemError", { cause: Schema.optional(Schema.Unknown) } ) {} class FileSystem extends Context.Service(fn: (fs: typeof fs_, signal: AbortSignal) => Promise) => Effect.Effect }>()("FileSystem") { static readonly Default = Layer.effect(FileSystem, Effect.succeed({ use: (fn: (fs: typeof fs_, signal: AbortSignal) => Promise) => Effect.tryPromise({ try: (signal) => fn(fs_, signal), catch: (cause) => new FileSystemError({ cause }), }), })) } // hide-end const program = Effect.gen(function* () { const fileSystem = yield* FileSystem // Read a file const content = yield* fileSystem.use((fs, signal) => fs.readFile("config.json", { encoding: "utf-8", signal }) ) // Write a file yield* fileSystem.use((fs, signal) => fs.writeFile("output.txt", content, { signal }) ) // List directory contents const files = yield* fileSystem.use((fs) => fs.readdir("./src") ) return files }) const main = pipe(program, Effect.provide(FileSystem.Default)) ``` -------------------------------- ### Create custom spans inside services Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/12-observability.md Define custom spans within Effect computations to add more detail to traces. This example shows a database lookup operation with a custom span. ```typescript import { Effect } from "effect" const performDbLookup = Effect.gen(function* () { yield* Effect.sleep("50 millis").pipe(Effect.withSpan("db.lookup")) return { data: "result" } }) const fetchData = Effect.fn("fetchData")(function* () { yield* Effect.log("Fetching data") return yield* performDbLookup }) ``` -------------------------------- ### Run Documentation Tests Source: https://github.com/kitlangton/effect-solutions/blob/main/CONTRIBUTING.md Execute all documentation tests and type checks using Bun. 'bun run test' runs all tests, while 'bun run typecheck:docs' specifically checks documentation types. ```bash bun run test bun run typecheck:docs ``` -------------------------------- ### Create Descriptive Changeset Source: https://github.com/kitlangton/effect-solutions/blob/main/AGENTS.md Creates a descriptive changeset file using a script, avoiding random names. Prompts for package selection and change type interactively. ```bash bun scripts/changeset-named.ts "fix audio overlap" ``` -------------------------------- ### Manual Port Validation with Config.mapOrFail Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/07-config.md Implement custom validation logic for configuration values using `Config.mapOrFail` when not using `Schema`. This example manually checks if a port number is within the valid range. ```typescript import { Config, ConfigProvider, Effect } from "effect" const program = Effect.gen(function* () { const port = yield* Config.int("PORT").pipe( Config.mapOrFail((p) => p > 0 && p < 65536 ? Effect.succeed(p) : Effect.fail(new Config.ConfigError(new ConfigProvider.SourceError({ message: "Port must be 1-65535" }))) ) ) return port }) ``` -------------------------------- ### Testing Effect Programs with @effect/vitest Source: https://context7.com/kitlangton/effect-solutions/llms.txt Integrates Effect with Vitest for testing. Use `it.effect` to run Effect programs directly and `TestClock` to simulate time deterministically. Each test gets its own fresh layer. ```typescript import { Effect, Layer } from "effect" import * as Context from "effect/Context" import { describe, expect, it } from "@effect/vitest" import { TestClock } from "effect/testing" import { Fiber } from "effect" // --- Service under test --- class Counter extends Context.Service< Counter, { readonly get: () => Effect.Effect; readonly increment: () => Effect.Effect } >()("@app/Counter") { static readonly layer = Layer.sync(Counter, () => { let count = 0 return { get: () => Effect.succeed(count), increment: () => Effect.sync(() => void count++), } }) } describe("Counter", () => { // Each test gets its own fresh layer it.effect("starts at zero", () => Effect.gen(function* () { const counter = yield* Counter expect(yield* counter.get()).toBe(0) }).pipe(Effect.provide(Counter.layer)) ) it.effect("increments", () => Effect.gen(function* () { const counter = yield* Counter yield* counter.increment() expect(yield* counter.get()).toBe(1) }).pipe(Effect.provide(Counter.layer)) ) // TestClock: it.effect provides TestContext where time starts at 0 it.effect("simulates time", () => Effect.gen(function* () { const fiber = yield* Effect.delay(Effect.succeed("done"), "10 seconds").pipe(Effect.forkChild) yield* TestClock.adjust("10 seconds") const result = yield* Fiber.join(fiber) expect(result).toBe("done") }) ) }) ``` -------------------------------- ### Retry GET Request with Exponential Backoff Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/11-http-clients.md Demonstrates how to fetch repository data with automatic retries for transient network issues using an exponential backoff schedule. Requires providing the FetchHttpClient layer. ```typescript import { FetchHttpClient, HttpClient, HttpClientResponse } from "effect/unstable/http" import { Effect, Schedule, Schema } from "effect" // hide-start const Repo = Schema.Struct({ id: Schema.Number, name: Schema.String }) // hide-end const getRepoWithRetry = Effect.gen(function* () { const response = yield* HttpClient.get("https://api.github.com/repos/Effect-TS/effect") return yield* HttpClientResponse.schemaBodyJson(Repo)(response) }).pipe( Effect.retry(Schedule.exponential("100 millis").pipe(Schedule.both(Schedule.recurs(3)))) ) getRepoWithRetry.pipe(Effect.provide(FetchHttpClient.layer), Effect.runPromise) ``` -------------------------------- ### Create Test FileSystem Layer Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/14-use-pattern.md This snippet demonstrates how to create a test layer for the FileSystem service using in-memory storage. It mocks file system operations like readFile, writeFile, and readdir to facilitate testing without actual file system access. Use this when you need to isolate components for testing and control the file system's state. ```typescript import { Effect, Layer, Schema } from "effect" import * as Context from "effect/Context" import * as fs_ from "node:fs/promises" class FileSystemError extends Schema.TaggedErrorClass()( "FileSystemError", { cause: Schema.optional(Schema.Unknown) } ) {} class FileSystem extends Context.Service(fn: (fs: typeof fs_, signal: AbortSignal) => Promise) => Effect.Effect }>()("FileSystem") { static readonly Default = Layer.effect( FileSystem, Effect.gen(function* () { const use = ( fn: (fs: typeof fs_, signal: AbortSignal) => Promise ): Effect.Effect => Effect.tryPromise({ try: (signal) => fn(fs_, signal), catch: (cause) => new FileSystemError({ cause }), }) return { use } as const }), ) static readonly Test = Layer.succeed(FileSystem, { use: (fn: (fs: typeof fs_, signal: AbortSignal) => Promise) => { const files = new Map([ ["config.json", '{"port": 3000}'], ["data.txt", "hello world"], ]) const mockFs = { readFile: async (path: string) => { const content = files.get(path) if (!content) throw new Error(`ENOENT: ${path}`) return content }, writeFile: async (path: string, content: string) => { files.set(path, content) }, readdir: async () => Array.from(files.keys()), } return Effect.tryPromise({ try: (signal) => fn(mockFs as typeof fs_, signal), catch: (cause) => new FileSystemError({ cause }), }) }, }) } ``` -------------------------------- ### Resilient API Calls with Retry and Timeout Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/03-basics.md Combine retry policies with timeouts for production code to handle transient failures. This example retries failed attempts with exponential backoff and applies both individual and overall timeouts. ```typescript import { Effect, Schedule } from "effect" // hide-start declare const callExternalApi: Effect.Effect // hide-end // Retry with exponential backoff, max 3 attempts const retryPolicy = Schedule.exponential("100 millis").pipe( Schedule.both(Schedule.recurs(3)) ) const resilientCall = callExternalApi.pipe( // Timeout each individual attempt Effect.timeout("2 seconds"), // Retry failed attempts Effect.retry(retryPolicy), // Overall timeout for all attempts Effect.timeout("10 seconds") ) ``` -------------------------------- ### Bad: Duplicate Layer Construction Source: https://github.com/kitlangton/effect-solutions/blob/main/packages/website/docs/04-services-and-layers.md Avoid calling parameterized layer constructors multiple times directly. This example shows how it leads to creating separate instances of a resource (e.g., database connection pools), potentially exceeding limits. ```typescript import { Layer } from "effect" // hide-start import { Effect } from "effect" import * as Context from "effect/Context" class SqlClient extends Context.Service Effect.Effect }>()("@app/SqlClient") {} class Postgres { static layer(_: { readonly url: string; readonly poolSize: number }): Layer.Layer { return Layer.succeed(SqlClient, { query: () => Effect.succeed([]) }) } } class UserRepo extends Context.Service()("@app/UserRepo") { static readonly layer: Layer.Layer = Layer.succeed(UserRepo, {}) } class OrderRepo extends Context.Service()("@app/OrderRepo") { static readonly layer: Layer.Layer = Layer.succeed(OrderRepo, {}) } // hide-end // ❌ Bad: calling the constructor twice creates two connection pools const badAppLayer = Layer.merge( UserRepo.layer.pipe( Layer.provide(Postgres.layer({ url: "postgres://localhost/mydb", poolSize: 10 })) ), OrderRepo.layer.pipe( Layer.provide(Postgres.layer({ url: "postgres://localhost/mydb", poolSize: 10 })) // Different reference! ) ) // Creates TWO connection pools (20 connections total). Could hit server limits. ```