Przejdź do treści
Biblioteka skilli
Programowanie · Budowanie i kod

consuming-results-at-api-boundaries

/consuming-results-at-api-boundaries

Wskazuje, gdzie w pakiecie z neverthrow kończyć łańcuch przez .match i jak eksportować funkcje bez wariantów safeX czy xResult zwracających Result lub ResultAsync, także przy opakowaniach fromThrowable.

Licencja
MIT
Ostatnia zmiana repozytorium
14.08.2026

Ocena i bezpieczeństwo

100/ 100Klasa: dobry
Format25 / 25
Frontmatter z nazwą i opisem, poprawna nazwa, opis do 1024 znaków, nazwa zgodna z folderem.
Odkrywalność25 / 25
Opis mówi, kiedy użyć skilla, ma sensowną długość i jest w 3. osobie.
Treść35 / 35
Co najmniej 150 słów i do 500 linii, nagłówki, listy kroków, przykłady albo kod.
Higiena15 / 15
Bez zaślepek typu TODO, bez ścieżek Windows, z licencją.
Ryzykobrak sygnałów w skanie
Pliki w paczce
1
Skrypty
0
Komentarze HTML
0
Adresy URL w SKILL.md
0
Przestarzałość

Brak sygnałów. Repozytorium zmienione 14.08.2026, bez odwołań do wycofanych modeli i API.

Licencja

MIT, otwarta: można używać i zmieniać, z zachowaniem informacji o autorze.

Co robi i kiedy

Wskazuje, gdzie w pakiecie z neverthrow kończyć łańcuch przez .match i jak eksportować funkcje bez wariantów safeX czy xResult zwracających Result lub ResultAsync, także przy opakowaniach fromThrowable.

Wyzwalacze z opisu

provide
Oryginalny opis (EN)

Use when exporting a function from a package that uses neverthrow internally, when tempted to export a safeX / xResult variant returning Result/ResultAsync, when asked to "provide" a fromThrowable-wrapped API to library users, or when deciding where a chain's final .match belongs.

Jak działa

  1. Overview
  2. The rule
  3. The recipe (real example from this repo — packages/core)
  4. Common rationalizations
  5. Red flags — STOP
  6. When NOT to apply
Sekcje z nagłówków SKILL.md, w oryginale · 72 linii, 607 słów
Podgląd SKILL.md
---
name: consuming-results-at-api-boundaries
description: Use when exporting a function from a package that uses neverthrow internally, when tempted to export a safeX / xResult variant returning Result/ResultAsync, when asked to "provide" a fromThrowable-wrapped API to library users, or when deciding where a chain's final .match belongs.
---

# Consuming Results at API Boundaries

## Overview

**A package's public API boundary IS a consumption edge. `Result` / `ResultAsync` never appear in exported signatures — the outermost exported function calls `.match` itself and presents a conventional contract.**

This closes the gap in [[chaining-neverthrow-results]]: "one `.match` at the consumption edge" does NOT mean "leave the `.match` to the caller of your package." Anything that crosses `exports` in package.json leaves your code for the outside world — that is, by definition, the edge. neverthrow is an internal composition tool, not a public contract.

## The rule

| Surface | Error channel |
|---|---|
| Internal module ↔ internal module | Pass `Result` / `ResultAsync`, keep chaining ([[chaining-neverthrow-results]]) |
| **Exported function (sync)** | `.match` inside; ok → return value, err → `throw` a named error class |
| **Exported long-lived object** | `.match` inside; err → `emit("error", error)` / callback |
| **Exported error types** | Export the error **classes** ([[modeling-errors-as-classes]]) — never `Result` values, never `Result` in a public type |

## The recipe (real example from this repo — `packages/core`)

```ts
// ✅ Wrap once at module scope (arguments are forwarded — no IIFE-style
// `fromThrowable(...)()` immediate call), match once at the exported edge.
const safeDispatchSend = Result.fromThrowable(
  dispatchSend,                                             // internal throwing dispatch
  (cause) => new TextureSendError(cause instanceof Error ? cause.message : `${cause}`, { cause }),
);

export const sendTextureFromPaintEvent = (
  sender: InstanceType<typeof TextureSender>,
  textureInfo: TextureInfo | undefined,
): void => {
  if (!textureInfo) return;
  safeDispatchSend(sender, textureInfo).match(
    () => undefined,
    (error) => { throw error; },                            // conventional contract: void + typed throw
  );
};

export { TextureSendError };                                // classes yes, Results no
```

```ts
// ❌ What both baseline attempts produced — a Result in the public contract
export const safeEncode = (encoder: Encoder, buf: Buffer): Result<Buffer, EncodeError> => ...
export const safeFlush: () => Result<void, FlushError> = Result.fromThrowable(...);
```

Consumers get `instanceof TextureSendError`, the original message, and the thrown value on `Error.cause` — full fidelity, zero neverthrow coupling.

## Common rationalizations

| Excuse | Reality |
|---|---|
| "A library is never the consumption edge — only the caller knows their edge" | The package boundary is YOUR edge. The caller has their own edge for their own Results; yours must not become their dependency. |
| "Pre-matching collapses the typed error channel" | No — the channel survives as an exported error class discriminated with `instanceof`. Types collapse; fidelity doesn't. |
| "Declare neverthrow as a peerDependency and make Result part of the contract" | That forces every consumer onto one specific neverthrow major forever. Version skew breaks `instanceof Result` across duplicated installs. |
| "The user asked for the fromThrowable-wrapped version" | They asked for the throw to be handled via fromThrowable — do that internally. 「提供」= provide the *behavior*, not the `Result` object. |
| "An additive `safeX` export is non-breaking, so it's fine" | Additive or not, it leaks the internal composition style into the public contract. |

## Red flags — STOP

- An exported name like `safeX`, `xResult`, `tryX` whose return type mentions `Result` / `ResultAsync`.
- `neverthrow` appearing in `peerDependencies`, or in any exported `.d.ts` type.
- A JSDoc example telling consumers where to put *their* `.match` on *your* return value.
- Reasoning that contains "the library cannot know the caller's edge" — that sentence means you are about to leak a Result.

## When NOT to apply

- Inside the package: keep passing Results between internal modules; do not `.match` mid-pipeline.
- App code (not a published package): the edge is the route handler / CLI main / event emit, exactly as [[chaining-neverthrow-results]] says.

Wersje

W bazie nie ma innych skilli o nazwie consuming-results-at-api-boundaries.

Migawka bazy z 28.10.2026 · wersja paczki 6d2daa2d563a (skrót SHA-256 całej paczki)

Zgłoś problem z tym skillem: szkodliwa treść, błędny opis albo prośba autora o usunięcie.

Podobne skille

  • 100chaining-neverthrow-results
    Pomaga łączyć wartości Result i ResultAsync z neverthrow w łańcuchy kroków, które mogą zawieść, odzyskiwać po konkretnym błędzie i zamieniać wynik na zwykłą wartość lub odpowiedź HTTP.
  • 100error-handling
    Opisuje obsługę błędów w projekcie Esposter: Results z neverthrow, ich łączenie i kończenie, miejsce logowania lub pokazywania błędu oraz rzucanie i zabezpieczanie błędów na serwerze.

Uwagi i zgłoszenia

Wiadomość trafi do Bartosza Sokolińskiego. Adres e-mail jest potrzebny tylko do odpowiedzi. Szczegóły w polityce prywatności.