### Quick Start: HTTP GET and POST with Base URL Source: https://github.com/thexpert507/oofp/blob/main/packages/http/README.md Demonstrates making GET and POST requests with a configured base URL, including interceptors for authentication and content type, and retry configuration. ```typescript import { get, post } from '@oofp/http/client' import { withBearer, withContentType } from '@oofp/http/interceptors' import * as E from '@oofp/core/either' const context = { baseUrl: 'https://api.example.com', headers: {}, timeout: 5000, } const result = await get('/users')(context)() if (E.isRight(result)) { console.log('Users:', result.value) } else { console.error('Error:', result.value.message) } const createUser = await post( '/users', JSON.stringify({ name: 'John', email: 'john@example.com' }), { contextInterceptors: [ withBearer('your-token'), withContentType('application/json'), ], retry: { maxRetries: 3, delay: 1000, }, }, )(context)() ``` -------------------------------- ### Quick Start: HTTP GET with Absolute URL Source: https://github.com/thexpert507/oofp/blob/main/packages/http/README.md Shows how to make a GET request using an absolute URL when no base URL is configured in the context. ```typescript const context = { headers: {}, timeout: 5000, } const result = await get('https://api.example.com/users')(context)() ``` -------------------------------- ### Development Setup and Commands Source: https://github.com/thexpert507/oofp/blob/main/README.md Provides essential commands for setting up the project, installing dependencies, building, testing, and linting the codebase. Includes commands for managing the monorepo and individual packages. ```bash # Clone and install git clone https://github.com/thexpert507/oofp.git cd oofp pnpm install # Build all packages pnpm build # Run all tests pnpm test # Run benchmarks pnpm bench # Type check pnpm type-check # Lint & format pnpm lint pnpm format ``` ```bash pnpm --filter @oofp/core build # Build a specific package pnpm --filter @oofp/saga test # Test a specific package pnpm --filter @oofp/http test:watch # Watch tests pnpm --filter @oofp/core bench # Run benchmarks for a package ``` -------------------------------- ### Quick Start: ReaderTaskEither Example Source: https://github.com/thexpert507/oofp/blob/main/packages/core/README.md Demonstrates building and running a pure pipeline using ReaderTaskEither for dependency injection and asynchronous operations. ```typescript import * as RTE from "@oofp/core/reader-task-either"; import * as TE from "@oofp/core/task-either"; import * as E from "@oofp/core/either"; import { pipe } from "@oofp/core/pipe"; // Define your dependencies as a type interface AppContext { db: Database; logger: Logger; } // Build pure pipelines that describe what to do const findUser = (id: string): RTE.ReaderTaskEither => pipe( RTE.ask(), RTE.chaint((ctx) => ctx.db.findUser(id)), RTE.tapRTE((user) => logAccess(user)), ); // Execute at the boundary const result = await RTE.run(appContext)(findUser("123"))(); // result: Either ``` -------------------------------- ### Install @oofp/query Source: https://github.com/thexpert507/oofp/blob/main/packages/query/README.md Install the library using npm or pnpm. ```bash npm install @oofp/query # or pnpm add @oofp/query ``` -------------------------------- ### Install @oofp/http Source: https://github.com/thexpert507/oofp/blob/main/packages/http/README.md Install the library using npm or pnpm. ```bash npm install @oofp/http # or pnpm add @oofp/http ``` -------------------------------- ### Install and Run Benchmarks Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/reference/benchmarks.mdx Instructions to clone the repository, install dependencies, and run the benchmarks using pnpm. ```bash git clone https://github.com/thexpert507/oofp.git cd oofp pnpm install pnpm --filter @oofp/benchmarks bench ``` -------------------------------- ### Install @oofp/http Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/packages/http.md Install the package using pnpm. ```bash pnpm add @oofp/http ``` -------------------------------- ### Install @oofp/core Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/getting-started/installation.md Use npm, pnpm, or yarn to install the core @oofp/core package. ```bash npm install @oofp/core # or pnpm add @oofp/core # or yarn add @oofp/core ``` -------------------------------- ### Install @oofp/core Source: https://github.com/thexpert507/oofp/blob/main/packages/core/README.md Install the @oofp/core package using npm or pnpm. ```bash npm install @oofp/core # or pnpm add @oofp/core ``` -------------------------------- ### Install OOFP Core Package Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/blog/functional-error-handling-typescript.md Install the core OOFP package using npm to start using functional error handling primitives like Either and TaskEither. ```bash npm install @oofp/core ``` -------------------------------- ### Install @oofp/query Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/packages/query.md Install the package using pnpm. Peer dependency `@oofp/core` is required, and `redis` is optional for Redis-backed cache. ```bash pnpm add @oofp/query ``` -------------------------------- ### Install @oofp/react Source: https://github.com/thexpert507/oofp/blob/main/packages/react/README.md Install the @oofp/react package using npm or pnpm. ```bash npm install @oofp/react # or pnpm add @oofp/react ``` -------------------------------- ### Install @oofp/saga Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/packages/saga.md Install the saga package using pnpm. ```bash pnpm add @oofp/saga ``` -------------------------------- ### Install OOFP Core and Companion Packages Source: https://context7.com/thexpert507/oofp/llms.txt Install the core OOFP package and any optional companion packages as needed. Companion packages require @oofp/core as a peer dependency. ```bash npm install @oofp/core # Optional companion packages (each requires @oofp/core as a peer dep) npm install @oofp/http npm install @oofp/query npm install @oofp/saga npm install @oofp/focal npm install @oofp/react ``` -------------------------------- ### Install @oofp/core Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/packages/core.md Use this command to add the core package to your project. ```bash pnpm add @oofp/core ``` -------------------------------- ### Install @oofp/saga Source: https://github.com/thexpert507/oofp/blob/main/packages/saga/README.md Install the package using npm or pnpm. Ensure @oofp/core is installed as a peer dependency. ```bash npm install @oofp/saga # or pnpm add @oofp/saga ``` -------------------------------- ### Complete Saga Example: Register Recruiter Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/packages/saga.md A full example demonstrating the saga pattern for registering a recruiter, including database insertion, authentication registration, and sending a welcome email. It shows how to define steps with actions and compensations and compose them using `chain`. ```typescript import * as Saga from "@oofp/saga"; import * as RTE from "@oofp/core/reader-task-either"; import * as TE from "@oofp/core/task-either"; import { pipe } from "@oofp/core/pipe"; // Types interface DbContext { db: { insert: (table: string, data: unknown) => Promise<{ id: string }>; delete: (table: string, id: string) => Promise; }; } interface AuthContext { auth: { register: (email: string) => Promise<{ uid: string }>; delete: (uid: string) => Promise; }; } interface MailContext { mailer: { send: (to: string, body: string) => Promise }; } type Recruiter = { id: string; email: string }; type User = { id: string; recruiterId: string }; type AuthIdentity = { uid: string }; // Step 1: Create recruiter in DB const createRecruiterStep = (email: string) => Saga.step({ name: "create-recruiter", action: pipe( RTE.ask(), RTE.chaint((ctx) => TE.tryCatch( () => ctx.db.insert("recruiters", { email }), (err) => new Error(`Insert failed: ${err}`), ), ), RTE.map((row): Recruiter => ({ id: row.id, email })), ), compensate: (recruiter) => pipe( RTE.ask(), RTE.chaint((ctx) => TE.tryCatch( () => ctx.db.delete("recruiters", recruiter.id), (err) => new Error(`Compensation failed: ${err}`), ), ), ), }); // Step 2: Register in auth service const registerAuthStep = (recruiter: Recruiter) => Saga.step({ name: "register-auth", action: pipe( RTE.ask(), RTE.chaint((ctx) => TE.tryCatch( () => ctx.auth.register(recruiter.email), (err) => new Error(`Auth registration failed: ${err}`), ), ), ), compensate: (identity) => pipe( RTE.ask(), RTE.chaint((ctx) => TE.tryCatch( () => ctx.auth.delete(identity.uid), (err) => new Error(`Auth compensation failed: ${err}`), ), ), ), }); // Step 3: Send welcome email (no compensation needed) const sendWelcomeStep = (recruiter: Recruiter) => Saga.step({ name: "send-welcome", action: pipe( RTE.ask(), RTE.chaint((ctx) => TE.tryCatch( () => ctx.mailer.send(recruiter.email, "Welcome!"), (err) => new Error(`Email failed: ${err}`), ), ), ), // No compensate — can't unsend an email }); // Compose the saga const registerRecruiter = (email: string) => pipe( createRecruiterStep(email), Saga.chain((recruiter) => registerAuthStep(recruiter)), Saga.chain((identity) => Saga.step({ name: "finalize", action: RTE.of(identity), }), ), Saga.run, ); // Type: RTE ``` -------------------------------- ### Install @oofp/focal Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/packages/focal.md Install the focal package using pnpm. ```bash pnpm add @oofp/focal ``` -------------------------------- ### Get User Example with ReaderTaskEither Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/packages/core.md Demonstrates how to fetch a user by ID using ReaderTaskEither, which allows for dependency injection of the database client. Execute the constructed effect at the boundary. ```typescript import { pipe } from "@oofp/core/pipe"; import * as RTE from "@oofp/core/reader-task-either"; import * as TE from "@oofp/core/task-either"; import * as E from "@oofp/core/either"; interface AppContext { db: { findUser: (id: string) => Promise }; } const getUser = (id: string): RTE.ReaderTaskEither => pipe( RTE.ask(), RTE.chaint((ctx) => TE.tryCatch( () => ctx.db.findUser(id), (err) => new Error(`User not found: ${err}`), ), ), ); // Execute at the boundary const result = await pipe( getUser("42"), RTE.run({ db: myDbClient }), )(); pipe( result, E.fold( (err) => console.error(err.message), (user) => console.log(user), ), ); ``` -------------------------------- ### Install @oofp/react Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/packages/react.md Install the @oofp/react package using pnpm. Ensure you have the necessary peer dependencies like @oofp/core, react, and react-dom. ```bash pnpm add @oofp/react ``` -------------------------------- ### Lens GetPut Law Example Source: https://github.com/thexpert507/oofp/blob/main/packages/focal/docs/03-lens.md Demonstrates the GetPut law: setting a value after getting it should result in the original structure. ```typescript const nameLens = pipe(Lens.identity(), Lens.prop("name")); // Extraer "Alice" y volver a ponerla = sin cambios nameLens.set(nameLens.get(alice))(alice); // => { name: "Alice", age: 30, address: {...} } (igual a alice) ``` -------------------------------- ### Install Optional Packages Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/getting-started/installation.md Install additional OOFP packages based on your project's needs, such as the HTTP client, query cache, or saga pattern. ```bash # Functional HTTP client pnpm add @oofp/http # Query cache pnpm add @oofp/query # Saga pattern pnpm add @oofp/saga # React hooks (experimental) pnpm add @oofp/react ``` -------------------------------- ### Counter Example Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/core/state.md Demonstrates a simple counter using State primitives. It shows how to increment, decrement, and get the current count, sequencing these operations using chain and running the program with an initial state. ```typescript import * as S from "@oofp/core/state"; import { pipe } from "@oofp/core/pipe"; type Counter = number; // Primitives: each returns the current count and modifies the state const increment: S.State = (count) => [count, count + 1]; const decrement: S.State = (count) => [count, count - 1]; const getCount: S.State = (count) => [count, count]; const program = pipe( increment, // state: 0 → 1, value: 0 S.chain(() => increment), // state: 1 → 2, value: 1 S.chain(() => increment), // state: 2 → 3, value: 2 S.chain(() => decrement), // state: 3 → 2, value: 3 S.chain(() => getCount), // state: 2 → 2, value: 2 ); const [value, finalState] = S.run(0)(program); // value = 2 (current count) // finalState = 2 // Or get just what you need: const count = S.runS(0)(program); // 2 (final state only) const result = S.runEval(0)(program); // 2 (value only) ``` -------------------------------- ### Making GET and POST Requests with Convenience Functions Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/packages/http.md Demonstrates how to use convenience functions like `get` and `post` for making HTTP requests. These functions return a ReaderTaskEither that can be executed with a provided HttpContext. ```typescript import { get, post, put, patch, del } from "@oofp/http"; import * as RTE from "@oofp/core/reader-task-either"; import { pipe } from "@oofp/core/pipe"; interface User { id: string; name: string; } // GET request — returns RTE const fetchUsers = get("/api/users"); // POST request const createUser = (data: { name: string }) => post("/api/users", JSON.stringify(data), { headers: { "Content-Type": "application/json" }, }); // Execute at the boundary const result = await pipe( fetchUsers, RTE.run({ baseUrl: "https://api.example.com" }), )(); ``` -------------------------------- ### Complete Interactive Cache Dashboard Example Source: https://github.com/thexpert507/oofp/blob/main/packages/query/docs/TELEMETRY-LISTENERS.md Build a live, interactive dashboard that subscribes to telemetry events and updates in real-time. This example demonstrates updating UI metrics, maintaining a history of recent events, and rendering the dashboard dynamically. ```typescript import { InMemoryTelemetryCollector, QueryClientImpl } from "@oofp/query"; const telemetry = new InMemoryTelemetryCollector(); const client = new QueryClientImpl({ telemetry }); // Estado del dashboard const dashboard = { hitRate: 0, cacheSize: 0, avgDuration: 0, recentEvents: [] as string[], }; // Suscribirse a cambios const unsubscribe = telemetry.subscribe((stats, event) => { // Actualizar métricas dashboard.hitRate = stats.hitRate; dashboard.cacheSize = stats.estimatedCacheSize; dashboard.avgDuration = stats.avgHitDuration; // Mantener historial de eventos recientes (últimos 10) dashboard.recentEvents.unshift(event.type); if (dashboard.recentEvents.length > 10) { dashboard.recentEvents.pop(); } // Renderizar dashboard renderDashboard(); }); function renderDashboard() { console.clear(); console.log("═══════════════════════════════════"); console.log(" CACHE DASHBOARD (LIVE) "); console.log("═══════════════════════════════════"); console.log(`Hit Rate: ${dashboard.hitRate}%`); console.log(`Cache Size: ${dashboard.cacheSize} keys`); console.log(`Avg Duration: ${dashboard.avgDuration}ms`); console.log("\nRecent Events:"); dashboard.recentEvents.forEach((event, i) => { console.log(` ${i + 1}. ${event}`); }); console.log("═══════════════════════════════════"); } // Usar el cliente normalmente // El dashboard se actualizará automáticamente // Cleanup cuando ya no se necesite // unsubscribe(); ``` -------------------------------- ### Composition Example Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/core/task.md Build a complete pipeline that stays lazy until the very end. ```APIDOC ## Composition Example Build a complete pipeline that stays lazy until the very end: ```typescript import * as T from "@oofp/core/task" import { pipe } from "@oofp/core/pipe" interface User { id: string name: string email: string } const fetchUser = (id: string): T.Task => () => fetch(`/api/users/${id}`).then((r) => r.json()) const sendWelcomeEmail = (email: string): T.Task => () => fetch("/api/email", { method: "POST", body: JSON.stringify({ to: email, template: "welcome" }), }).then(() => {}) const program = pipe( fetchUser("user-42"), T.tap((user) => console.log(`Processing ${user.name}`)), T.tchain((user) => sendWelcomeEmail(user.email)), T.map((user) => ({ message: `Welcome email sent to ${user.name}` })), T.delay(100), ) // Nothing has executed yet. Run at the application boundary: const result = await T.run(program) // { message: "Welcome email sent to Alice" } ``` ``` -------------------------------- ### Pipe Function Usage Example Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/blog/understanding-monads-typescript-practical-guide.md Shows a basic example of the `pipe` function, demonstrating how it chains simple functions together, passing the output of one as the input to the next. ```typescript import { pipe } from "@oofp/core/pipe"; // pipe(value, f1, f2, f3) === f3(f2(f1(value))) const result = pipe(5, (x) => x * 2, (x) => x + 1, (x) => `${x}!`); ``` -------------------------------- ### Service Factory Pattern Example Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/core/reader.md Demonstrates building services as Readers using R.from and wiring dependencies for execution. ```APIDOC ## Service Factory Pattern Use `R.from` to build services that declare their dependencies as the `Reader` environment: ```typescript import * as R from "@oofp/core/reader" import { pipe } from "@oofp/core/pipe" // Define service interfaces interface Logger { info: (msg: string) => void error: (msg: string) => void } interface UserRepository { findById: (id: string) => User | undefined save: (user: User) => void } interface Deps { logger: Logger userRepo: UserRepository } // Build service functions as Readers const findUser = (id: string): R.Reader => R.from(({ userRepo, logger }) => { logger.info(`Looking up user: ${id}`) return userRepo.findById(id) }) const createUser = (data: CreateUserDTO): R.Reader => R.from(({ userRepo, logger }) => { const user = { id: crypto.randomUUID(), ...data } userRepo.save(user) logger.info(`Created user: ${user.id}`) return user }) const getOrCreateUser = (id: string, fallback: CreateUserDTO) => pipe( findUser(id), R.chain((existing) => existing ? R.of(existing) : createUser(fallback) ), ) // Wire dependencies and run at the boundary const deps: Deps = { logger: console, userRepo: new InMemoryUserRepo(), } const user = R.run(deps)(getOrCreateUser("123", { name: "Alice" })) ``` ``` -------------------------------- ### Iso Conversion Example Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/packages/focal.md Illustrates creating and using an Iso for lossless, reversible conversions between types. Requires imports from '@oofp/core/pipe' and '@oofp/focal/iso'. ```typescript import { pipe } from "@oofp/core/pipe"; import * as Iso from "@oofp/focal/iso"; const celsiusToFahrenheit = Iso.make( (c: number) => c * 9 / 5 + 32, (f: number) => (f - 32) * 5 / 9, ); // Forward pipe(celsiusToFahrenheit, Iso.view(100)); // => 212 // Backward pipe(celsiusToFahrenheit, Iso.review(32)); // => 0 // Reverse the Iso const fahrenheitToCelsius = Iso.reverse(celsiusToFahrenheit); pipe(fahrenheitToCelsius, Iso.view(212)); // => 100 ``` -------------------------------- ### Basic GET Request Source: https://github.com/thexpert507/oofp/blob/main/packages/http/README.md Illustrates how to perform a basic GET request to fetch data. It shows how to specify the expected response type and how to chain operations using `pipe` and `TE.map` for data transformation. ```APIDOC ## Basic GET Request ### Description Perform a basic GET request to retrieve data. The example shows how to define a function that fetches a user by ID, transforms the response, and handles potential errors using TaskEither. ### Method GET ### Endpoint `/api/users/:id` ### Example ```typescript import { get } from '@oofp/http/client' import { pipe } from '@ किसी/core/pipe' import * as TE from '@oofp/core/task-either' const fetchUser = (id: string) => pipe( get(`/api/users/${id}`), TE.map((user) => ({ ...user, fullName: `${user.firstName} ${user.lastName}` })) ) const result = await fetchUser('123')({ headers: {}, timeout: 5000 })() ``` ``` -------------------------------- ### Example: Error Handling with catchError Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/core/io.md Shows how to use `throwError` to create a failing IO and `catchError` to recover from errors. ```APIDOC ### Error Handling with catchError `throwError` creates an IO that fails. `catchError` provides recovery. ```typescript import * as IO from "@oofp/core/io"; import { pipe } from "@oofp/core/pipe"; const parseJSON = (raw: string): IO.IO => IO.from(() => JSON.parse(raw)); const safeParseJSON = (raw: string): IO.IO => pipe( parseJSON(raw), IO.catchError((err) => IO.of({ error: `Invalid JSON: ${err}` }), ), ); IO.run(safeParseJSON('{"valid": true}')); // { valid: true } IO.run(safeParseJSON("not json")); // { error: "Invalid JSON: SyntaxError: ..." } ``` ``` -------------------------------- ### Quick Start: Fetching Data with Caching Source: https://github.com/thexpert507/oofp/blob/main/packages/query/README.md Demonstrates how to create a query client and fetch data with caching enabled. Ensure you have necessary imports from '@oofp/query' and '@oofp/core'. ```typescript import { createQueryClient } from '@oofp/query' import * as TE from '@oofp/core/task-either' import * as E from '@oofp/core/either' const client = createQueryClient({ defaultTTL: 60_000 }) // Fetch with caching const result = await client.fetchQuery({ queryKey: ['users', 123], queryFn: () => TE.tryCatch( () => fetch('/api/users/123').then(r => r.json()), (err) => new Error(String(err)), ), ttl: 30_000, })() if (E.isRight(result)) { console.log(result.value.data) // the user object console.log(result.value.cached) // true if served from cache } ``` -------------------------------- ### Manual Lens Creation Source: https://github.com/thexpert507/oofp/blob/main/packages/focal/docs/03-lens.md Demonstrates how to manually construct a Lens object with the required `tag`, `get`, and `set` properties. ```APIDOC ## Create a Lens manually A Lens is a plain object with `tag`, `get`, and `set`. You can build it manually or use `Lens.make`. ```ts const xLens: Lens<{ x: number; y: number }, number> = { tag: 'Lens', get: (point) => point.x, set: (x) => (point) => ({ ...point, x }), }; xLens.get({ x: 1, y: 2 }); // => 1 xLens.set(10)({ x: 1, y: 2 }); // => { x: 10, y: 2 } ``` Note that `set` does not mutate the original object; it creates a new one using the spread operator (`...`). ``` -------------------------------- ### Lens PutGet Law Example Source: https://github.com/thexpert507/oofp/blob/main/packages/focal/docs/03-lens.md Demonstrates the PutGet law: getting a value after setting it should return the newly set value. ```typescript nameLens.get(nameLens.set("Bob")(alice)); // => "Bob" ``` -------------------------------- ### Get User with TaskEither Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/guides/error-handling.md Implement a function to retrieve a user by ID using TaskEither. This example demonstrates chaining operations and handling potential 'not_found' errors. ```typescript const getUser = (id: string): TE.TaskEither => pipe( findUserById(db)(id), // TE TE.chainw((row) => // widen to include not_found row === null ? TE.left({ kind: "not_found", resource: "user", id }) : TE.of(toUser(row)), ), ); ``` -------------------------------- ### Reader Constructor: from Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/core/reader.md Creates a Reader from a function, commonly used for accessing the environment. This example shows creating a Reader to get an API URL from a configuration object. ```typescript interface Config { apiUrl: string timeout: number } const getApiUrl = R.from((config) => config.apiUrl) ``` -------------------------------- ### Create Starlight Project Source: https://github.com/thexpert507/oofp/blob/main/docs/README.md Use this command to initialize a new Astro project with the Starlight template. ```bash npm create astro@latest -- --template starlight ``` -------------------------------- ### Working with DOM Elements Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/core/maybe.md Example of using Maybe to safely handle DOM element retrieval and value extraction. It maps over the element to get its value and filters out empty strings. ```typescript import { pipe } from "@oofp/core/pipe"; import * as M from "@oofp/core/maybe"; const getInputValue = (id: string): M.Maybe => pipe( M.fromNullable(document.getElementById(id)), M.map((el) => (el as HTMLInputElement).value), M.iif((v) => v.trim().length > 0), ); const greeting = pipe( getInputValue("name-field"), M.map((name) => `Hello, ${name}! `), M.getOrElse("Hello, stranger!"), ); ``` -------------------------------- ### Functional HTTP Client with Interceptors and Retries Source: https://context7.com/thexpert507/oofp/llms.txt Illustrates using the OOFP HTTP client for typed requests. Demonstrates GET, POST, and composing requests using pipe. Includes examples of adding interceptors like bearer tokens and content types, and configuring retry logic for transient errors. ```typescript import { pipe } from "@oofp/core/pipe"; import * as RTE from "@oofp/core/reader-task-either"; import * as E from "@oofp/core/either"; import { get, post, put, del } from "@oofp/http/client"; import { withBearer, withContentType, withHeader } from "@oofp/http/interceptors"; import { HttpError } from "@oofp/http/primitives"; interface User { id: number; name: string; email: string } interface CreateUserDto { name: string; email: string } const httpContext = { baseUrl: "https://api.example.com", headers: {}, timeout: 8000 }; // GET with auth header const getUser = (id: number) => get(`/users/${id}`, { contextInterceptors: [withBearer("my-token")], }); // POST with JSON body + auth const createUser = (dto: CreateUserDto) => post("/users", JSON.stringify(dto), { contextInterceptors: [ withBearer("my-token"), withContentType("application/json"), ], }); // Compose in a pipeline const registerAndFetch = (dto: CreateUserDto) => pipe( createUser(dto), RTE.chain((created) => getUser(created.id)), RTE.map((user) => ({ ...user, displayName: user.name.toUpperCase() })), ); const result = await registerAndFetch({ name: "Alice", email: "alice@example.com" })(httpContext)(); if (E.isLeft(result)) { const err = result.value; if (HttpError.isUnauthorized(err)) console.error("401 – re-login required"); else if (HttpError.isServerError(err)) console.error("5xx – retry later"); else console.error(err.message); } else { console.log(result.value.displayName); // "ALICE" } // Retry on transient failures const resilientFetch = get("/users", { retry: { maxRetries: 3, delay: 1000, skipIf: (e) => HttpError.isNotFound(e), onError: (e, attempt) => console.warn(`Attempt ${attempt}:`, e.message), }, }); const users = await resilientFetch(httpContext)(); ``` -------------------------------- ### Layered Dependency Injection with Reader Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/core/reader.md Build up the environment layer by layer using `provide`. This example demonstrates providing configuration, logging, database, and cache dependencies. ```typescript interface AppDeps { config: Config logger: Logger db: Database cache: Cache } const program: R.Reader = R.from((deps) => { deps.logger.info(`Starting with DB: ${deps.config.dbHost}`) // ... }) // Infrastructure layer provides config and logger const withInfra = pipe( program, R.provide({ config: loadConfig(), logger: createLogger(), }), ) // Reader<{ db: Database; cache: Cache }, void> // Data layer provides db and cache const fullyProvided = pipe( withInfra, R.provide({ db: createDatabase(), cache: createCache(), }), ) // Reader<{}, void> — all dependencies satisfied R.run({})(fullyProvided) ``` -------------------------------- ### Creating and Using a HttpClient Instance Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/packages/http.md Shows how to create a reusable `HttpClient` instance using `createHttpClient` and then use its methods (`get`, `put`, etc.) for making requests. ```typescript import { createHttpClient } from "@oofp/http"; const http = createHttpClient(); const fetchUser = (id: string) => http.get(`/api/users/${id}`); const updateUser = (id: string, data: Partial) => http.put(`/api/users/${id}`, JSON.stringify(data)); ``` -------------------------------- ### Importing Composition Utilities Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/blog/typescript-pipe-compose-function-composition.md Demonstrates how to import `pipe`, `flow`, and `compose` from the `@oofp/core` package. ```typescript import { pipe } from "@oofp/core/pipe"; import { flow } from "@oofp/core/flow"; import { compose } from "@oofp/core/compose"; ``` -------------------------------- ### Using HttpClient Class Source: https://github.com/thexpert507/oofp/blob/main/packages/http/README.md Shows how to create and use an `HttpClient` instance for making various HTTP requests (GET, POST, PUT, DELETE). This approach centralizes client configuration and provides a convenient way to manage requests. ```APIDOC ## Using HttpClient Class ### Description Instantiate and use the `HttpClient` class to manage HTTP requests. This class provides methods for common HTTP verbs like GET, POST, PUT, and DELETE, and allows for request-specific interceptors. ### Methods - **get<T>(url, options?)**: Makes a GET request. - **post<T>(url, body, options?)**: Makes a POST request. - **put<T>(url, body, options?)**: Makes a PUT request. - **delete(url, options?)**: Makes a DELETE request. ### Example ```typescript import { createHttpClient } from '@oofp/http/client' import { withBearer } from '@oofp/http/interceptors' const client = createHttpClient() const context = { headers: {}, timeout: 5000, } // GET const users = await client.get('/api/users')(context)() // POST const newUser = await client.post('/api/users', JSON.stringify({ name: 'John' }), { contextInterceptors: [withBearer('token')], })(context)() // PUT const updated = await client.put(`/api/users/${id}`, JSON.stringify(data))(context)() // DELETE const deleted = await client.delete(`/api/users/${id}`)(context)() ``` ``` -------------------------------- ### OOFP Object Functional Utilities Example Source: https://github.com/thexpert507/oofp/blob/main/packages/core/README.md Example of using OOFP Object utilities with `pipe` for object manipulation. Imports are required from '@oofp/core/object'. ```typescript import * as O from "@oofp/core/object"; const result = pipe( config, O.pick(["apiUrl", "timeout", "retries"]), O.mapValues(String), O.filter((v) => v !== ""), ); ``` -------------------------------- ### OOFP List Functional Utilities Example Source: https://github.com/thexpert507/oofp/blob/main/packages/core/README.md Example of using OOFP List utilities with `pipe` for array manipulation. Imports are required from '@oofp/core/list'. ```typescript import * as L from "@oofp/core/list"; const result = pipe( users, L.filter((u) => u.active), L.distinctBy((u) => u.email), L.sort(({ a, b }) => a.name.localeCompare(b.name)), L.groupBy((u) => u.department), ); ``` -------------------------------- ### Example: Composing IO Operations Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/core/io.md Illustrates sequencing dependent IO operations using `chain` and performing intermediate side effects with `tap`. ```APIDOC ### Composing IO Operations Use `chain` to sequence dependent IOs and `tap` for intermediate side effects. ```typescript import * as IO from "@oofp/core/io"; import { pipe } from "@oofp/core/pipe"; const readEnv = (key: string): IO.IO => IO.from(() => process.env[key]); const log = (msg: string): IO.IO => IO.from(() => console.log(msg)); const getPort: IO.IO = pipe( readEnv("PORT"), IO.map((val) => (val ? parseInt(val, 10) : 3000)), IO.tap((port) => console.log(`Using port: ${port}`)), ); const port = IO.run(getPort); // logs: "Using port: 3000" // port = 3000 ``` ``` -------------------------------- ### Creating a QueryClient Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/packages/query.md Initialize a new QueryClient with customizable options for default Time-To-Live (TTL), maximum cache size, and retry behavior. ```APIDOC ## Creating a QueryClient ```typescript import { createQueryClient } from "@oofp/query"; const queryClient = createQueryClient({ defaultTTL: 60_000, // 60 seconds maxCacheSize: 1000, // LRU eviction after 1000 entries defaultRetry: 3, // retry failed queries 3 times defaultRetryDelay: 1000, // 1s between retries }); ``` ``` -------------------------------- ### Perform Basic GET Request Source: https://github.com/thexpert507/oofp/blob/main/packages/http/README.md Shows how to perform a basic GET request to fetch user data. It pipes the request through a TaskEither for error handling and maps the result to include a fullName property. ```typescript import { get } from '@oofp/http/client' import { pipe } from '@oofp/core/pipe' import * as TE from '@oofp/core/task-either' const fetchUser = (id: string) => pipe( get(`/api/users/${id}`), TE.map((user) => ({ ...user, fullName: `${user.firstName} ${user.lastName}` }))) const result = await fetchUser('123')({ headers: {}, timeout: 5000 })() ``` -------------------------------- ### Quick Start: Define and Run a Saga Source: https://github.com/thexpert507/oofp/blob/main/packages/saga/README.md Define multi-step operations with actions and compensations. Chain steps together and run the saga, with automatic compensation on failure. ```typescript import { step, chain, run } from '@oofp/saga' import { pipe } from '@oofp/core/pipe' import * as RTE from '@oofp/core/reader-task-either' import * as E from '@oofp/core/either' interface Deps { db: Database auth: AuthService } // Step 1: Create user in database const createUser = step({ name: 'create-user', action: ({ db }) => async () => { const user = await db.users.create({ name: 'Alice' }) return E.right(user) }, compensate: (user) => ({ db }) => async () => { await db.users.delete(user.id) return E.right(undefined) }, }) // Step 2: Register in auth system const registerAuth = (user: User) => step({ name: 'register-auth', action: ({ auth }) => async () => { const identity = await auth.register(user.email) return E.right(identity) }, compensate: (identity) => ({ auth }) => async () => { await auth.delete(identity.uid) return E.right(undefined) }, }) // Compose and run const result = await pipe( createUser, chain(registerAuth), run, RTE.run({ db, auth }), )() if (E.isRight(result)) { console.log('Success:', result.value) } else { console.error('Failed (all steps rolled back):', result.value) } ``` -------------------------------- ### Prism Composition Example Source: https://github.com/thexpert507/oofp/blob/main/packages/focal/docs/04-prism.md Demonstrates composing two Prisms to create a new Prism that targets a nested optional value. Shows how `preview` and `review` interact with the composed Prism. ```ts const outerPrism = Prism._just>(); const innerPrism = Prism._right(); const composed = pipe(outerPrism, Prism.compose(innerPrism)); // Ambos coinciden: pipe(composed, Prism.preview(M.just(E.right(42)))); // => Just(42) // El externo falla: pipe(composed, Prism.preview(M.nothing())); // => Nothing // El interno falla: pipe(composed, Prism.preview(M.just(E.left("err")))); // => Nothing // review construye de adentro hacia afuera: pipe(composed, Prism.review(42)); // => Just(Right(42)) ``` -------------------------------- ### Error Transformation Example Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/guides/error-handling.md Provides a concrete example of using TE.mapLeft to convert a database error (DbError) into an API error (ApiError) using a mapping function `dbToApiError`. This is useful when translating errors between layers. ```typescript type DbError = { code: string; detail: string }; type ApiError = { status: number; message: string }; const dbToApiError = (err: DbError): ApiError => { switch (err.code) { case "23505": return { status: 409, message: "Resource already exists" }; case "23503": return { status: 400, message: "Referenced resource not found" }; default: return { status: 500, message: "Internal database error" }; } }; const createUser = (data: UserInput): TE.TaskEither => pipe( insertUser(data), // TE TE.mapLeft(dbToApiError), // TE ); ``` -------------------------------- ### Data Processing Pipeline Example Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/utilities/list.md Demonstrates a data processing pipeline using `pipe` and various list utilities to filter, sort, map, and join product data. ```typescript import { pipe } from "@oofp/core/pipe"; import * as L from "@oofp/core/list"; interface Product { name: string; category: string; price: number; inStock: boolean; } const products: Product[] = [ { name: "Laptop", category: "electronics", price: 999, inStock: true }, { name: "Phone", category: "electronics", price: 699, inStock: true }, { name: "Shirt", category: "clothing", price: 29, inStock: false }, { name: "Shoes", category: "clothing", price: 89, inStock: true }, { name: "Tablet", category: "electronics", price: 499, inStock: true }, ]; const expensiveInStock = pipe( products, L.filter((p) => p.inStock), L.filter((p) => p.price > 100), L.sort(({ a, b }) => b.price - a.price), L.map((p) => `${p.name}: $${p.price}`), L.join("\n"), ); // "Laptop: $999\nPhone: $699\nTablet: $499" ``` -------------------------------- ### Maybe Monad API Examples in TypeScript Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/blog/understanding-monads-typescript-practical-guide.md Provides examples of common `Maybe` monad operations from `@oofp/core`, including creating `Just` and `Nothing` values, transforming values with `map` and `chain`, and extracting values with `getOrElse` or converting to nullable types. ```typescript import * as M from "@oofp/core/maybe"; M.just(42); // Just(42) M.nothing(); // Nothing M.fromNullable(null); // Nothing M.fromNullable(42); // Just(42) // Transform M.map((x) => x + 1); // Just(42) -> Just(43), Nothing -> Nothing M.chain((x) => M.just(x + 1)); // Just(42) -> Just(43) // Extract M.getOrElse(() => 0); // Just(42) -> 42, Nothing -> 0 M.toNullable; // Just(42) -> 42, Nothing -> null ``` -------------------------------- ### Optic Composition Example Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/packages/focal.md Shows how to compose different types of optics (Lens, Traversal) to create more complex data access paths. Requires imports from '@oofp/core/pipe' and specific optic modules. ```typescript import { pipe } from "@oofp/core/pipe"; import * as Lens from "@oofp/focal/lens"; import * as Prism from "@oofp/focal/prism"; import * as Traversal from "@oofp/focal/traversal"; interface Company { name: string; employees: { name: string; age: number }[]; } // Lens → Traversal → Lens = Traversal const allAges = pipe( Lens.identity(), Lens.prop("employees"), // Lens Lens.compose(Traversal.each()), // Traversal Traversal.compose(Lens.make( (e) => e.age, (age) => (e) => ({ ...e, age }), )), // Traversal ); const acme: Company = { name: "Acme", employees: [ { name: "Alice", age: 30 }, { name: "Bob", age: 25 }, ], }; // Collect all ages pipe(allAges, Traversal.collect(acme)); // => [30, 25] // Give everyone a birthday pipe(allAges, Traversal.modify((n) => n + 1))(acme); // => { name: "Acme", employees: [{ name: "Alice", age: 31 }, { name: "Bob", age: 26 }] } ``` -------------------------------- ### String Length Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/utilities/string.md Get the number of characters in a string. ```APIDOC ## length Returns the length of a string. ```typescript Str.length("hello"); // 5 Str.length(""); // 0 ``` ``` -------------------------------- ### Basic use-case with ask() and chaint() Source: https://github.com/thexpert507/oofp/blob/main/docs/src/content/docs/core/reader-task-either.md Demonstrates how to access context using `ask()` and chain operations with `chaint()` to perform asynchronous work. ```APIDOC ## Basic use-case with `ask()` and `chaint()` ### Description `ask()` gives you access to the full context. `chaint()` lets you chain into a `TaskEither` without manually lifting. ### Code Example ```typescript import * as RTE from "@oofp/core/reader-task-either"; import * as TE from "@oofp/core/task-either"; import * as E from "@oofp/core/either"; import { pipe } from "@oofp/core/pipe"; interface DbContext { db: { query: (sql: string) => Promise }; } interface User { id: string; name: string; email: string; } // Build a pipeline that reads from the context, then does async work const getUsers: RTE.ReaderTaskEither = pipe( RTE.ask(), RTE.chaint((ctx) => TE.tryCatch( () => ctx.db.query("SELECT * FROM users") as Promise, (err) => new Error(`DB query failed: ${err}`), ), ), ); // Nothing has executed yet — getUsers is just a description. // At the application boundary, provide context and run: const result: E.Either = await pipe( getUsers, RTE.run({ db: myDatabaseClient }), )(); ``` ```