klaim 1.12.48 → 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 ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "url": "https://context7.com/antharuu/klaim",
3
+ "public_key": "pk_uReHomekMKo4V8j7PQ2Xi"
4
+ }
package/deno.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antharuu/klaim",
3
- "version": "1.12.48",
3
+ "version": "1.12.49",
4
4
  "description": "Klaim is a lightweight TypeScript library designed to manage APIs and record requests, optimized for an optimal user experience.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -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)