Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ if (error) {
case 'outOfStock':
console.log(error.resource.message);
// Follow-up links on errors can be navigated as well (with full type safety)
let restocked = await navigate(error.resource.restock);
const restocked = await navigate(error.resource.restock);
console.log(`Notify user that ${restocked.name} needs to be restocked`);
return;
// Default branch handles unexpected errors (network, error status, parse errors)
Expand Down
18 changes: 9 additions & 9 deletions docs/hateoas-bff-example.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Everything runs in one Fastify process for ease of running. The logical separati
## 1b. Link conventions by backend

Each backend uses a different link representation to demonstrate that `typesafe-hypermedia`
is format-agnostic (see AGENTS.md Core Concept #5 and `docs/how-it-works.md §9`):
is format-agnostic (see AGENTS.md Core Concept #5 and `docs/how-it-works.md §8`):

| Backend | Link pattern | Example | Why |
|---------|-------------|---------|-----|
Expand Down Expand Up @@ -121,14 +121,14 @@ The client's job is to render whichever `view.*` sub-object is populated. Becaus

| View | Triggered by | Composes from |
|----------------------|-------------------------------------------|---------------|
| **Home** | `view=home` (or no `view`) | PIM (featured by tag) + ERP (quotes) |
| **Category** | `view=category&category=<id>` | PIM + ERP |
| **Product detail** | `view=product&sku=<sku>` | PIM (product, reviews, related) + ERP (quote, stock) + DAM (images, optional) |
| **Cart** | `view=cart` | PIM (names) + ERP (quotes) + CRM (offers for promo) — plus recommendations via PIM `relatedProductsUrl` |
| **Wishlist** | `view=wishlist` | PIM + ERP |
| **Search results** | `view=search&search=<q>` | PIM + ERP (when results) |
| **Orders list** | `view=orders` | ERP |
| **Order confirmation** | `view=order-confirmation&orderId=<id>` | ERP (falls back to orders list + danger toast if id not found) |
| **Home** | `GET /bff/home` | PIM (featured by tag) + ERP (quotes) |
| **Category** | `GET /bff/category?category=<id>` | PIM + ERP |
| **Product detail** | `GET /bff/product?sku=<sku>` | PIM (product, reviews, related) + ERP (quote, stock) + DAM (images, optional) |
| **Cart** | `GET /bff/cart` | PIM (names) + ERP (quotes) + CRM (offers for promo) — plus recommendations via PIM `relatedProductsUrl` |
| **Wishlist** | `GET /bff/wishlist` | PIM + ERP |
| **Search results** | `GET /bff/search?search=<q>` | PIM + ERP (when results) |
| **Orders list** | `GET /bff/orders` | ERP |
| **Order confirmation** | `GET /bff/order-confirmation?orderId=<id>` | ERP (falls back to orders list + danger toast if id not found) |

Every view builder threads a `used: Set<SourceId>` and calls `used.add('pim'|'erp'|'crm'|'dam')` as it fetches. `buildDataSources(used)` turns the set into the `dataSources` field at the end. This gives the UI an automatic, honest attribution bar at the bottom of every page.

Expand Down
10 changes: 5 additions & 5 deletions docs/how-it-works.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ A key insight: error resources are not special. They are regular resources that

This design keeps the API definition simple and consistent.

## 9. Design Decisions (The "Why")
## 8. Design Decisions (The "Why")

### Why Support Both Link Objects and String Properties?

Expand Down Expand Up @@ -377,7 +377,7 @@ The library doesn't pick winners - it provides the tools and lets developers cho

### Why Simple Initialization?
* **Ease of Use**: `linkTo()` provides a simple starting point - validates config and returns the root link.
* **Type Safety**: Explicit `apiRoot` parameter ensures correct return type.
* **Type Safety**: Explicit `resource` parameter ensures correct return type.
* **Separation of Concerns**: Initialization separated from navigation (use `navigate()` to navigate).
* **Trade-off**: Resources must stay in memory for navigation (metadata stored in module-level `WeakMap`s keyed by object identity).

Expand All @@ -389,16 +389,16 @@ The library doesn't pick winners - it provides the tools and lets developers cho
* **Type Safety**: TypeScript enforces handling through the tuple pattern. You cannot accidentally ignore errors once `expect` is declared.
* **Trade-off**: More verbose than try/catch, but explicit and type-safe. Forces thinking about error cases upfront.

## 10. Known Gaps & Roadmap
## 9. Known Gaps & Roadmap

**Phase 1: Core Completeness**

* No known gaps at this time.

**Phase 2: Robustness**
* ✅ **Performance (RESOLVED)**: Link extraction uses compiled path accessors cached per ResourceDefinition. First fetch compiles optimized traversal functions, subsequent fetches reuse them. No repeated traversals for similar paths (e.g., `items[].author` and `items[].category` share traversal). Performance optimized for production use.
* **Reattachment after serialization**: Need a utility to reattach runtime metadata to objects that have been serialized/deserialized (Note: This is generally an anti-pattern in HATEOAS - see Section 7.3 for best practices).
* **Reattachment after serialization**: Need a utility to reattach runtime metadata to objects that have been serialized/deserialized (Note: Caching hrefs like this is an anti-pattern in HATEOAS — you lose the server's ability to evolve URLs. Prefer re-fetching from the root navigable when the link might have changed.).

## 11. Lessons Learned
## 10. Lessons Learned

* *Placeholder: Document failed refactoring attempts here to avoid repeating mistakes.*
2 changes: 1 addition & 1 deletion docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Debugging "Link metadata not found" errors currently requires developers to ment

### Approach

Export a `debugNavigable(obj)` function that queries the global `navigableOwner` map and the owning client's `MetadataStore`, returning a structured snapshot:
Export a `debugNavigable(obj)` function that queries the `apiClientByNavigable` and `linksByNavigable` WeakMaps in `src/runtime-metadata.ts`, returning a structured snapshot:

```typescript
debugNavigable(shop)
Expand Down
Loading