klaim 1.12.47 → 1.12.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/context7.json +4 -0
- package/deno.json +1 -1
- package/docs/codedocs/api-and-routes.md +120 -0
- package/docs/codedocs/api-reference/api.md +143 -0
- package/docs/codedocs/api-reference/cache.md +84 -0
- package/docs/codedocs/api-reference/errors.md +113 -0
- package/docs/codedocs/api-reference/group.md +107 -0
- package/docs/codedocs/api-reference/hook.md +78 -0
- package/docs/codedocs/api-reference/klaim.md +105 -0
- package/docs/codedocs/api-reference/registry.md +109 -0
- package/docs/codedocs/api-reference/route.md +165 -0
- package/docs/codedocs/architecture.md +129 -0
- package/docs/codedocs/groups-and-hierarchy.md +110 -0
- package/docs/codedocs/guides/advanced-runtime-patterns.md +152 -0
- package/docs/codedocs/guides/defining-a-client.md +137 -0
- package/docs/codedocs/guides/pagination-validation-and-observability.md +110 -0
- package/docs/codedocs/index.md +128 -0
- package/docs/codedocs/request-lifecycle.md +130 -0
- package/docs/codedocs/resilience-and-control.md +123 -0
- package/docs/codedocs/types.md +187 -0
- package/package.json +8 -8
- package/tests/04.klaim.test.ts +15 -1
- package/tests/05.cache.test.ts +11 -1
- package/tests/06.retry.test.ts +9 -1
- package/tests/07.validate.test.ts +13 -1
- package/tests/08.group.test.ts +9 -1
- package/tests/09.pagination.test.ts +19 -1
package/context7.json
ADDED
package/deno.json
CHANGED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "APIs and Routes"
|
|
3
|
+
description: "Learn how Klaim turns API declarations and route definitions into callable runtime functions."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
APIs and routes are the core abstraction in Klaim. An API defines a base URL and shared defaults. A route defines an HTTP method, a path fragment, optional headers, and optional route-specific behavior. Together they become functions on the exported `Klaim` object.
|
|
7
|
+
|
|
8
|
+
## What This Concept Solves
|
|
9
|
+
|
|
10
|
+
Without a declaration layer, most apps end up with a pile of small wrappers around `fetch()` that disagree on URL formatting, headers, and error behavior. Klaim centralizes that setup:
|
|
11
|
+
|
|
12
|
+
- `Api.create()` defines the host and top-level namespace.
|
|
13
|
+
- `Route.get()`, `Route.post()`, and the other static helpers define endpoints.
|
|
14
|
+
- `Registry` converts those definitions into callable functions.
|
|
15
|
+
|
|
16
|
+
The result is a runtime shape that matches how people think about an integration: `Klaim.github.repos.list`, not `"GET /repos"` buried in a helper string.
|
|
17
|
+
|
|
18
|
+
## How It Relates to Other Concepts
|
|
19
|
+
|
|
20
|
+
- [Groups and Hierarchy](/docs/groups-and-hierarchy) add nesting above or below APIs.
|
|
21
|
+
- [Request Lifecycle](/docs/request-lifecycle) explains what happens once you call a route.
|
|
22
|
+
- [Resilience and Control](/docs/resilience-and-control) covers the chainable settings inherited from `Element`.
|
|
23
|
+
|
|
24
|
+
## How It Works Internally
|
|
25
|
+
|
|
26
|
+
`Api.create()` in `src/core/Api.ts` does four important things:
|
|
27
|
+
|
|
28
|
+
1. Normalizes the API name with `toCamelCase()`.
|
|
29
|
+
2. Creates an `Api` instance that inherits from `Element`.
|
|
30
|
+
3. Registers that instance in `Registry.i.registerElement(api)`.
|
|
31
|
+
4. Sets the API as the current parent so routes defined in the callback register under it.
|
|
32
|
+
|
|
33
|
+
`Route.get()` and the other static helpers in `src/core/Route.ts` all delegate to `createRoute()`. That helper creates a `Route` instance, records its HTTP method, and calls `detectArguments()` to scan the path for bracket parameters like `[id]`.
|
|
34
|
+
|
|
35
|
+
When `Registry.registerRoute()` runs in `src/core/Registry.ts`, the route gets a `parent` path and is immediately added to the `Klaim` object by `addToKlaimRoute()`. That method uses `createRouteHandler()` from `src/core/Klaim.ts` so the route becomes a function instead of a plain object.
|
|
36
|
+
|
|
37
|
+
```mermaid
|
|
38
|
+
flowchart TD
|
|
39
|
+
A[Api.create] --> B[registerElement api]
|
|
40
|
+
B --> C[setCurrentParent api]
|
|
41
|
+
C --> D[Route.get or Route.post]
|
|
42
|
+
D --> E[detectArguments]
|
|
43
|
+
E --> F[registerRoute]
|
|
44
|
+
F --> G[addToKlaimRoute]
|
|
45
|
+
G --> H[Klaim.api.route = function]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Two details are easy to miss:
|
|
49
|
+
|
|
50
|
+
- Path arguments are required only when the route path contains `[name]` segments.
|
|
51
|
+
- Pagination changes the call signature. If `withPagination()` has been set on a route, the first argument becomes the page or offset value.
|
|
52
|
+
|
|
53
|
+
## Basic Usage
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { Api, Klaim, Route } from "klaim";
|
|
57
|
+
|
|
58
|
+
type User = {
|
|
59
|
+
id: number;
|
|
60
|
+
name: string;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
Api.create("usersApi", "https://jsonplaceholder.typicode.com", () => {
|
|
64
|
+
Route.get("listUsers", "/users");
|
|
65
|
+
Route.get("getUser", "/users/[id]");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const users = await Klaim.usersApi.listUsers<User[]>();
|
|
69
|
+
const one = await Klaim.usersApi.getUser<User>({ id: 1 });
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Advanced Usage
|
|
73
|
+
|
|
74
|
+
This example combines route headers, a request body, and response validation on a non-GET route.
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { Api, Klaim, Route } from "klaim";
|
|
78
|
+
import * as yup from "yup";
|
|
79
|
+
|
|
80
|
+
type PostInput = {
|
|
81
|
+
title: string;
|
|
82
|
+
body: string;
|
|
83
|
+
userId: number;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
type PostResponse = PostInput & {
|
|
87
|
+
id: number;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const postSchema = yup.object({
|
|
91
|
+
id: yup.number().required(),
|
|
92
|
+
title: yup.string().required(),
|
|
93
|
+
body: yup.string().required(),
|
|
94
|
+
userId: yup.number().required(),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
Api.create("posts", "https://jsonplaceholder.typicode.com", () => {
|
|
98
|
+
Route.post("create", "/posts", {
|
|
99
|
+
Authorization: "Bearer example-token",
|
|
100
|
+
}).validate(postSchema);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const created = await Klaim.posts.create<PostResponse>(
|
|
104
|
+
{},
|
|
105
|
+
{ title: "Hello", body: "Created with Klaim", userId: 1 }
|
|
106
|
+
);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
<Callout type="warn">Route names and API names are always normalized with `toCamelCase()` from `src/tools/toCamelCase.ts`. If you declare `Route.get("get-user", "/users/[id]")`, the runtime property will be `Klaim.api.getUser`, not `get-user`. Missing `[id]` arguments throw `MissingArgumentError` before any network request is made.</Callout>
|
|
110
|
+
|
|
111
|
+
<Accordions>
|
|
112
|
+
<Accordion title="Why use declarative routes instead of hand-written fetch helpers?">
|
|
113
|
+
Klaim’s declaration model gives you a single source of truth for the base URL, route path, HTTP method, and runtime behavior. That makes it easy to audit what an integration does because the registration code mirrors the final runtime object. In `src/core/Registry.ts`, route declarations are converted directly into functions on `Klaim`, so the runtime stays close to the source. The trade-off is that type inference is weaker than a generated client because the object graph is created dynamically at runtime, not inferred from static declarations.
|
|
114
|
+
</Accordion>
|
|
115
|
+
<Accordion title="Why do route helpers return `Element` instead of a narrower route-specific type?">
|
|
116
|
+
The static route helpers in `src/core/Route.ts` return `Element`, which keeps the chaining surface consistent with `Api` and `Group`. That makes calls like `.withRetry()`, `.withTimeout()`, and `.before()` available from one shared base class. The cost is that route-specific methods such as `validate()` are only visible when you work with a `Route` instance directly rather than through the static return type. In practice, users usually chain `validate()` immediately on the static call site, and TypeScript still resolves it from the concrete class instance.
|
|
117
|
+
</Accordion>
|
|
118
|
+
</Accordions>
|
|
119
|
+
|
|
120
|
+
For full signatures, see [API](/docs/api-reference/api), [Route](/docs/api-reference/route), and [Klaim Runtime](/docs/api-reference/klaim).
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Api"
|
|
3
|
+
description: "Reference for the exported `Api` class and its chainable configuration methods."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Source: `src/core/Api.ts`
|
|
7
|
+
|
|
8
|
+
Import path:
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
import { Api } from "klaim";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`Api` represents a top-level service definition. You do not instantiate it directly; the public entrypoint is the static `Api.create()` method.
|
|
15
|
+
|
|
16
|
+
## Signatures
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
class Api extends Element {
|
|
20
|
+
static create(
|
|
21
|
+
name: string,
|
|
22
|
+
url: string,
|
|
23
|
+
callback: IApiCallback,
|
|
24
|
+
headers: IHeaders = {}
|
|
25
|
+
): Element;
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Inherited chainable methods from `Element`:
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
before(callback: ICallback<ICallbackBeforeArgs>): this
|
|
33
|
+
after(callback: ICallback<ICallbackAfterArgs>): this
|
|
34
|
+
onCall(callback: ICallback<ICallbackCallArgs>): this
|
|
35
|
+
withCache(duration: number = 20): this
|
|
36
|
+
withRetry(maxRetries: number = 2): this
|
|
37
|
+
withPagination(config: IPaginationConfig = {}): this
|
|
38
|
+
withRate(config: Partial<IRateLimitConfig> = {}): this
|
|
39
|
+
withTimeout(duration: number = 5, message: string = "Request timed out"): this
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## `Api.create()`
|
|
43
|
+
|
|
44
|
+
Creates, registers, and scopes an API declaration. Internally, `src/core/Api.ts` stores the current parent from `Registry`, registers the new API, sets it as the active parent while your callback runs, and restores the previous context afterward.
|
|
45
|
+
|
|
46
|
+
| Parameter | Type | Default | Description |
|
|
47
|
+
|-----------|------|---------|-------------|
|
|
48
|
+
| `name` | `string` | — | API name. It is normalized to camelCase before registration. |
|
|
49
|
+
| `url` | `string` | — | Base URL for the API. Leading and trailing slashes are trimmed. |
|
|
50
|
+
| `callback` | `() => void` | — | Declaration callback used to register routes or nested groups. |
|
|
51
|
+
| `headers` | `Record<string, string>` | `{}` | Default headers merged into every route call under the API. |
|
|
52
|
+
|
|
53
|
+
Returns: `Element`
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { Api, Route } from "klaim";
|
|
59
|
+
|
|
60
|
+
Api.create("billing-api", "https://api.example.com", () => {
|
|
61
|
+
Route.get("listInvoices", "/invoices");
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Chainable Methods
|
|
66
|
+
|
|
67
|
+
### `withCache(duration?: number)`
|
|
68
|
+
|
|
69
|
+
Enables response caching for routes under the API. Runtime enforcement happens in `fetchWithRetry()` inside `src/core/Klaim.ts`.
|
|
70
|
+
|
|
71
|
+
Example:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
Api.create("catalog", "https://dummyjson.com", () => {
|
|
75
|
+
Route.get("listProducts", "/products");
|
|
76
|
+
}).withCache(60);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### `withRetry(maxRetries?: number)`
|
|
80
|
+
|
|
81
|
+
Sets the API-level retry fallback used when a route does not define its own retry count.
|
|
82
|
+
|
|
83
|
+
Example:
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
Api.create("search", "https://api.example.com", () => {
|
|
87
|
+
Route.get("query", "/search");
|
|
88
|
+
}).withRetry(3);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### `withRate(config?: Partial<IRateLimitConfig>)`
|
|
92
|
+
|
|
93
|
+
Sets the API-level rate limit budget. `src/core/Klaim.ts` uses this when the route does not define its own rate config.
|
|
94
|
+
|
|
95
|
+
Example:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
Api.create("github", "https://api.github.com", () => {
|
|
99
|
+
Route.get("repos", "/users/[user]/repos");
|
|
100
|
+
}).withRate({ limit: 10, duration: 60 });
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### `withTimeout(duration?: number, message?: string)`
|
|
104
|
+
|
|
105
|
+
Sets the API-level timeout fallback.
|
|
106
|
+
|
|
107
|
+
Example:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
Api.create("slowService", "https://example.com", () => {
|
|
111
|
+
Route.get("data", "/data");
|
|
112
|
+
}).withTimeout(2, "Service request timed out");
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### `onCall(callback)`
|
|
116
|
+
|
|
117
|
+
Sets the API-level `onCall` callback. `fetchWithRetry()` uses it only when the route does not define its own `onCall`.
|
|
118
|
+
|
|
119
|
+
Example:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
Api.create("metrics", "https://example.com", () => {
|
|
123
|
+
Route.get("overview", "/overview");
|
|
124
|
+
}).onCall(() => {
|
|
125
|
+
console.log("a metrics route call started");
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### `before(callback)` and `after(callback)`
|
|
130
|
+
|
|
131
|
+
These methods exist because `Api` inherits them from `Element`, but `callApi()` currently reads only route-level `before` and `after` callbacks. Use them only if you are also copying them to routes yourself or applying them through a group.
|
|
132
|
+
|
|
133
|
+
Example:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
const api = Api.create("users", "https://example.com", () => {
|
|
137
|
+
Route.get("list", "/users");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
api.before(({ route, api, url, config }) => ({ route, api, url, config }));
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Related pages: [Route](/docs/api-reference/route), [Group](/docs/api-reference/group), [Klaim Runtime](/docs/api-reference/klaim)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Cache"
|
|
3
|
+
description: "Reference for the exported in-memory `Cache` singleton used by Klaim's cached fetch path."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Source: `src/core/Cache.ts`
|
|
7
|
+
|
|
8
|
+
Import path:
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
import { Cache } from "klaim";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`Cache` is a singleton in-memory store with TTL expiration and LRU-style eviction. Klaim uses it through `src/tools/fetchWithCache.ts` when caching is enabled.
|
|
15
|
+
|
|
16
|
+
## Signatures
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
class Cache {
|
|
20
|
+
static get i(): Cache;
|
|
21
|
+
set(key: string, value: unknown, ttl: number = 0): void;
|
|
22
|
+
has(key: string): boolean;
|
|
23
|
+
get(key: string): unknown | null;
|
|
24
|
+
clear(): void;
|
|
25
|
+
get size(): number;
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Methods
|
|
30
|
+
|
|
31
|
+
### `Cache.i`
|
|
32
|
+
|
|
33
|
+
Returns the singleton cache instance.
|
|
34
|
+
|
|
35
|
+
### `set(key, value, ttl?)`
|
|
36
|
+
|
|
37
|
+
Stores a value in the cache. A `ttl` of `0` means the cache entry does not expire.
|
|
38
|
+
|
|
39
|
+
| Parameter | Type | Default | Description |
|
|
40
|
+
|-----------|------|---------|-------------|
|
|
41
|
+
| `key` | `string` | — | Cache key. Klaim derives this from URL plus fetch config. |
|
|
42
|
+
| `value` | `unknown` | — | Value to store. |
|
|
43
|
+
| `ttl` | `number` | `0` | Time to live in milliseconds. |
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
Cache.i.set("user:1", { id: 1, name: "Ada" }, 60_000);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### `has(key)`
|
|
52
|
+
|
|
53
|
+
Checks whether a non-expired cache entry exists. Expired entries are removed on access.
|
|
54
|
+
|
|
55
|
+
### `get(key)`
|
|
56
|
+
|
|
57
|
+
Returns cached data or `null`. When an entry is returned, the underlying map entry is moved to the end to behave like an LRU cache.
|
|
58
|
+
|
|
59
|
+
### `clear()`
|
|
60
|
+
|
|
61
|
+
Removes every cached entry.
|
|
62
|
+
|
|
63
|
+
### `size`
|
|
64
|
+
|
|
65
|
+
Returns the current number of entries stored in memory.
|
|
66
|
+
|
|
67
|
+
## Example With Klaim
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
Api.create("catalog", "https://dummyjson.com", () => {
|
|
71
|
+
Route.get("listProducts", "/products");
|
|
72
|
+
}).withCache(60);
|
|
73
|
+
|
|
74
|
+
await Klaim.catalog.listProducts();
|
|
75
|
+
console.log(Cache.i.size);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Implementation Notes
|
|
79
|
+
|
|
80
|
+
`src/tools/fetchWithCache.ts` computes a cache key by hashing `input.toString()` plus `JSON.stringify(init)` with the helper from `src/tools/hashStr.ts`. That means two otherwise identical requests with different headers or bodies will get different cache entries, which is usually the right behavior for HTTP client caching inside an application process.
|
|
81
|
+
|
|
82
|
+
The cache is process-local and memory-backed. It is useful for reducing duplicate requests inside one runtime, but it is not a distributed cache and it is not persisted across restarts. If you need cross-process consistency, you should treat Klaim’s cache as a local optimization layer and keep the authoritative cache somewhere else.
|
|
83
|
+
|
|
84
|
+
Related pages: [Resilience and Control](/docs/resilience-and-control), [Api](/docs/api-reference/api), [Route](/docs/api-reference/route)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Errors"
|
|
3
|
+
description: "Reference for the exported Klaim-specific error classes."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Source: `src/core/errors.ts`
|
|
7
|
+
|
|
8
|
+
Import path:
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
import {
|
|
12
|
+
InvalidPathError,
|
|
13
|
+
KlaimError,
|
|
14
|
+
MissingArgumentError,
|
|
15
|
+
RateLimitError,
|
|
16
|
+
RetryExhaustedError,
|
|
17
|
+
TimeoutError,
|
|
18
|
+
} from "klaim";
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Klaim exports a small error hierarchy so callers can catch specific operational failures.
|
|
22
|
+
|
|
23
|
+
## Signatures
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
class KlaimError extends Error {
|
|
27
|
+
constructor(message: string);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
class RateLimitError extends KlaimError {
|
|
31
|
+
readonly retryAfterMs: number;
|
|
32
|
+
constructor(message: string, retryAfterMs: number);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class TimeoutError extends KlaimError {
|
|
36
|
+
constructor(message: string);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class RetryExhaustedError extends KlaimError {
|
|
40
|
+
readonly attempts: number;
|
|
41
|
+
readonly cause: Error | undefined;
|
|
42
|
+
constructor(message: string, attempts: number, cause?: Error);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
class MissingArgumentError extends KlaimError {
|
|
46
|
+
readonly argument: string;
|
|
47
|
+
constructor(argument: string);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
class InvalidPathError extends KlaimError {
|
|
51
|
+
constructor(path: string);
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Error Classes
|
|
56
|
+
|
|
57
|
+
### `KlaimError`
|
|
58
|
+
|
|
59
|
+
Base class for every library-specific error.
|
|
60
|
+
|
|
61
|
+
### `RateLimitError`
|
|
62
|
+
|
|
63
|
+
Thrown by `fetchWithRetry()` when `checkRateLimit()` denies a request. The `retryAfterMs` property tells you when the next request should be allowed.
|
|
64
|
+
|
|
65
|
+
### `TimeoutError`
|
|
66
|
+
|
|
67
|
+
Thrown by `withTimeout()` from `src/tools/timeout.ts` when the configured duration expires before the request completes.
|
|
68
|
+
|
|
69
|
+
### `RetryExhaustedError`
|
|
70
|
+
|
|
71
|
+
Thrown by `fetchWithRetry()` after all attempts have failed. `attempts` contains the final attempt count and `cause` stores the last underlying error when available.
|
|
72
|
+
|
|
73
|
+
### `MissingArgumentError`
|
|
74
|
+
|
|
75
|
+
Thrown by `applyArgs()` when a route path contains a required `[param]` but the caller did not supply it in `args`.
|
|
76
|
+
|
|
77
|
+
### `InvalidPathError`
|
|
78
|
+
|
|
79
|
+
Thrown by `callApi()` when it cannot resolve a valid route and owning API combination for the requested path.
|
|
80
|
+
|
|
81
|
+
## Example
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
try {
|
|
85
|
+
await Klaim.pokemon.list(0);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (error instanceof MissingArgumentError) {
|
|
88
|
+
console.error(error.argument);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (error instanceof RateLimitError) {
|
|
92
|
+
console.error(error.retryAfterMs);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Practical Guidance
|
|
98
|
+
|
|
99
|
+
These error classes are most useful when you let Klaim’s runtime controls do the work and then branch on the result. For example, `TimeoutError` and `RateLimitError` are usually recoverable at the UI or job-runner layer, while `InvalidPathError` and `MissingArgumentError` usually indicate a programming mistake that should be fixed rather than retried. `RetryExhaustedError` sits between those two categories because it wraps an operational failure but also preserves the final underlying `cause`.
|
|
100
|
+
|
|
101
|
+
Because every custom error extends `KlaimError`, you can also catch the whole family when you want library-specific fallback behavior:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
try {
|
|
105
|
+
await Klaim.inventory.listProducts();
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (error instanceof KlaimError) {
|
|
108
|
+
console.error("klaim runtime failure", error.message);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Related pages: [Resilience and Control](/docs/resilience-and-control), [Klaim Runtime](/docs/api-reference/klaim)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Group"
|
|
3
|
+
description: "Reference for the exported `Group` class and its propagation-based configuration helpers."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Source: `src/core/Group.ts`
|
|
7
|
+
|
|
8
|
+
Import path:
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
import { Group } from "klaim";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`Group` creates a namespace layer in the registry and, for some settings, copies configuration into already-registered children.
|
|
15
|
+
|
|
16
|
+
## Signatures
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
class Group extends Element {
|
|
20
|
+
static create(name: string, callback: () => void): Element;
|
|
21
|
+
|
|
22
|
+
withCache(duration = 20): this;
|
|
23
|
+
withRetry(maxRetries = 2): this;
|
|
24
|
+
withTimeout(duration = 5, message = "Request timed out"): this;
|
|
25
|
+
before(callback: ICallback<ICallbackBeforeArgs>): this;
|
|
26
|
+
after(callback: ICallback<ICallbackAfterArgs>): this;
|
|
27
|
+
onCall(callback: ICallback<ICallbackCallArgs>): this;
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Inherited but not overridden:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
withPagination(config: IPaginationConfig = {}): this
|
|
35
|
+
withRate(config: Partial<IRateLimitConfig> = {}): this
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## `Group.create()`
|
|
39
|
+
|
|
40
|
+
Registers a group under the current parent and executes your callback inside that namespace.
|
|
41
|
+
|
|
42
|
+
| Parameter | Type | Default | Description |
|
|
43
|
+
|-----------|------|---------|-------------|
|
|
44
|
+
| `name` | `string` | — | Group name, normalized to camelCase. |
|
|
45
|
+
| `callback` | `() => void` | — | Declaration callback for nested APIs, groups, or routes. |
|
|
46
|
+
|
|
47
|
+
Returns: `Element`
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
Api.create("shop", "https://dummyjson.com", () => {
|
|
53
|
+
Group.create("products", () => {
|
|
54
|
+
Route.get("list", "/products");
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Propagating Methods
|
|
60
|
+
|
|
61
|
+
### `withCache(duration?)`
|
|
62
|
+
|
|
63
|
+
Copies cache settings to children that do not already define a cache value.
|
|
64
|
+
|
|
65
|
+
### `withRetry(maxRetries?)`
|
|
66
|
+
|
|
67
|
+
Copies retry settings to children that do not already define retry.
|
|
68
|
+
|
|
69
|
+
### `withTimeout(duration?, message?)`
|
|
70
|
+
|
|
71
|
+
Copies timeout settings to children that do not already define timeout.
|
|
72
|
+
|
|
73
|
+
### `before(callback)`, `after(callback)`, `onCall(callback)`
|
|
74
|
+
|
|
75
|
+
Copies callbacks to children that do not already define those callback slots.
|
|
76
|
+
|
|
77
|
+
Example:
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
Api.create("shop", "https://dummyjson.com", () => {
|
|
81
|
+
Group.create("products", () => {
|
|
82
|
+
Route.get("list", "/products");
|
|
83
|
+
Route.get("getOne", "/products/[id]");
|
|
84
|
+
})
|
|
85
|
+
.withRetry(2)
|
|
86
|
+
.before(({ route, api, url, config }) => ({ route, api, url, config }));
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Inherited Methods That Need Caution
|
|
91
|
+
|
|
92
|
+
`Group` inherits `withRate()` and `withPagination()` from `Element`, but `src/core/Group.ts` does not override them and `src/core/Klaim.ts` only reads route pagination plus route or API rate limits. As a result, those inherited methods do not currently have the same practical effect as the explicitly propagated methods above.
|
|
93
|
+
|
|
94
|
+
Example:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
const group = Group.create("products", () => {
|
|
98
|
+
Route.get("list", "/products");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
group.withRate({ limit: 5, duration: 10 });
|
|
102
|
+
group.withPagination({ page: 1, limit: 20 });
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The code above is valid TypeScript because the methods exist on `Element`, but the current runtime does not consume those values at the group level.
|
|
106
|
+
|
|
107
|
+
Related pages: [Groups and Hierarchy](/docs/groups-and-hierarchy), [Api](/docs/api-reference/api), [Route](/docs/api-reference/route)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Hook"
|
|
3
|
+
description: "Reference for the exported `Hook` event helper used to observe completed route calls."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Source: `src/core/Hook.ts`
|
|
7
|
+
|
|
8
|
+
Import path:
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
import { Hook } from "klaim";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`Hook` is a simple static event registry keyed by route name. `callApi()` triggers hooks with a string shaped like ``apiName.routeName`` after a route finishes its request lifecycle.
|
|
15
|
+
|
|
16
|
+
## Signatures
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
class Hook {
|
|
20
|
+
static subscribe(routeName: string, callback: () => any): void;
|
|
21
|
+
static run(routeName: string): void;
|
|
22
|
+
static unsubscribe(routeName: string): void;
|
|
23
|
+
static unsubscribeAll(): void;
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Methods
|
|
28
|
+
|
|
29
|
+
### `subscribe(routeName, callback)`
|
|
30
|
+
|
|
31
|
+
Registers or replaces a callback for a route key.
|
|
32
|
+
|
|
33
|
+
| Parameter | Type | Default | Description |
|
|
34
|
+
|-----------|------|---------|-------------|
|
|
35
|
+
| `routeName` | `string` | — | Route key such as `inventory.listProducts`. |
|
|
36
|
+
| `callback` | `() => any` | — | Function to run when the hook is triggered. |
|
|
37
|
+
|
|
38
|
+
Example:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
Hook.subscribe("inventory.listProducts", () => {
|
|
42
|
+
console.log("products loaded");
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### `run(routeName)`
|
|
47
|
+
|
|
48
|
+
Executes the stored callback for a route key if one exists.
|
|
49
|
+
|
|
50
|
+
### `unsubscribe(routeName)`
|
|
51
|
+
|
|
52
|
+
Removes one hook callback.
|
|
53
|
+
|
|
54
|
+
### `unsubscribeAll()`
|
|
55
|
+
|
|
56
|
+
Clears every hook callback.
|
|
57
|
+
|
|
58
|
+
## Example With Klaim
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
Api.create("inventory", "https://dummyjson.com", () => {
|
|
62
|
+
Route.get("listProducts", "/products");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
Hook.subscribe("inventory.listProducts", () => {
|
|
66
|
+
console.log("request completed");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
await Klaim.inventory.listProducts();
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Operational Notes
|
|
73
|
+
|
|
74
|
+
Hooks are keyed only by one string, and `subscribe()` replaces any previous callback for the same key because `_callbacks` is a `Map<string, IHookCallback>` in `src/core/Hook.ts`. This keeps the implementation small, but it also means `Hook` is not a pub-sub fan-out bus with multiple listeners per event. If you need multiple listeners, wrap them inside one callback or build a thin adapter around `Hook`.
|
|
75
|
+
|
|
76
|
+
Also note the route key format used by `callApi()` in `src/core/Klaim.ts`: the runtime combines the API name and route name only. That means nested group names are not part of the hook key at execution time. A route such as `Klaim.store.products.list()` still emits `store.list`, not `store.products.list`, because the runtime does not include intermediate group segments in the hook identifier.
|
|
77
|
+
|
|
78
|
+
Related pages: [Request Lifecycle](/docs/request-lifecycle), [Klaim Runtime](/docs/api-reference/klaim)
|