katanakit-js 2.14.0 → 2.14.2
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/README.md +143 -2
- package/package.json +26 -21
package/README.md
CHANGED
|
@@ -58,6 +58,147 @@ if (result.ok) {
|
|
|
58
58
|
}
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
+
## HTTP Client — API Manager
|
|
62
|
+
|
|
63
|
+
The core of KatanaKit is a **typed, registry-based HTTP client**. You register
|
|
64
|
+
your APIs once, then fetch by name — the client builds URLs, handles serialization,
|
|
65
|
+
and returns a **Safe Result** (`{ ok, data, error }`) that never throws on HTTP errors.
|
|
66
|
+
|
|
67
|
+
### 1. Register your APIs
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { useInitApis } from "katanakit-js";
|
|
71
|
+
|
|
72
|
+
useInitApis({
|
|
73
|
+
// A public REST API
|
|
74
|
+
jsonplaceholder: {
|
|
75
|
+
baseUri: "https://jsonplaceholder.typicode.com",
|
|
76
|
+
endpoints: {
|
|
77
|
+
posts: "/posts",
|
|
78
|
+
postById: "/posts/:id",
|
|
79
|
+
},
|
|
80
|
+
// Applied automatically to specific endpoints (overridable per-call)
|
|
81
|
+
defaultQueryParams: {
|
|
82
|
+
posts: { _limit: 10 },
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
// Your own backend
|
|
87
|
+
myApi: {
|
|
88
|
+
baseUri: "https://api.myapp.com/v1",
|
|
89
|
+
endpoints: {
|
|
90
|
+
users: "/users",
|
|
91
|
+
userById: "/users/:id",
|
|
92
|
+
createUser: "/users",
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 2. GET — list and read
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { useGetApi } from "katanakit-js";
|
|
102
|
+
|
|
103
|
+
// List (uses defaultQueryParams: _limit=10)
|
|
104
|
+
const list = await useGetApi<{ id: number; title: string }[]>("jsonplaceholder", "posts");
|
|
105
|
+
if (list.ok) console.log(list.data);
|
|
106
|
+
|
|
107
|
+
// Read by ID — :id is replaced by params
|
|
108
|
+
const post = await useGetApi<{ title: string }>("jsonplaceholder", "postById", {
|
|
109
|
+
params: { id: 1 },
|
|
110
|
+
});
|
|
111
|
+
if (post.ok) console.log(post.data.title);
|
|
112
|
+
|
|
113
|
+
// Override default query params
|
|
114
|
+
const filtered = await useGetApi("jsonplaceholder", "posts", {
|
|
115
|
+
query: { _limit: 5, userId: 1 },
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 3. POST, PUT, PATCH, DELETE
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
import { usePost, usePut, usePatch, useDelete } from "katanakit-js";
|
|
123
|
+
|
|
124
|
+
// POST — body is auto-serialized to JSON
|
|
125
|
+
const created = await usePost<{ id: number }>("myApi", "createUser", {
|
|
126
|
+
name: "Alice",
|
|
127
|
+
email: "alice@example.com",
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// PUT — full replacement (body + path params)
|
|
131
|
+
const updated = await usePut("myApi", "userById", { name: "Bob" }, { params: { id: 42 } });
|
|
132
|
+
|
|
133
|
+
// PATCH — partial update
|
|
134
|
+
const patched = await usePatch("myApi", "userById", { name: "Charlie" }, { params: { id: 42 } });
|
|
135
|
+
|
|
136
|
+
// DELETE
|
|
137
|
+
const deleted = await useDelete("myApi", "userById", { params: { id: 42 } });
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 4. Auth tokens — inject headers per call
|
|
141
|
+
|
|
142
|
+
There's no global interceptor — pass `headers` directly. This keeps things explicit and testable.
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
const result = await useFetch("myApi", "users", {
|
|
146
|
+
method: "GET",
|
|
147
|
+
headers: {
|
|
148
|
+
Authorization: `Bearer ${getToken()}`,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 5. Error handling — the Safe Result pattern
|
|
154
|
+
|
|
155
|
+
Every fetch returns `{ ok, data, error, status, url }`. No try/catch needed for HTTP failures.
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
const result = await useGetApi("myApi", "userById", { params: { id: 99999 } });
|
|
159
|
+
|
|
160
|
+
if (result.ok) {
|
|
161
|
+
// result.data is typed
|
|
162
|
+
console.log(result.data);
|
|
163
|
+
} else {
|
|
164
|
+
// result.error is always structured
|
|
165
|
+
console.log(result.error.status); // 404
|
|
166
|
+
console.log(result.error.message); // "HTTP Error: Not Found"
|
|
167
|
+
console.log(result.error.details); // parsed response body (if any)
|
|
168
|
+
console.log(result.url); // the URL that was called
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### 6. Build URLs without fetching
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import { useBuildUrl } from "katanakit-js";
|
|
176
|
+
|
|
177
|
+
const url = useBuildUrl("jsonplaceholder", "postById", {
|
|
178
|
+
params: { id: 7 },
|
|
179
|
+
query: { _limit: 3 },
|
|
180
|
+
});
|
|
181
|
+
// "https://jsonplaceholder.typicode.com/posts/7?_limit=3"
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 7. FormData and raw bodies
|
|
185
|
+
|
|
186
|
+
`usePost`/`usePut`/`usePatch` auto-detect `FormData`, `Blob`, `URLSearchParams`,
|
|
187
|
+
`ArrayBuffer`, `ReadableStream`, and `string` — these are sent as-is without
|
|
188
|
+
forcing `Content-Type: application/json`.
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
const form = new FormData();
|
|
192
|
+
form.append("file", blob);
|
|
193
|
+
await usePost("myApi", "upload", form);
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Full example
|
|
197
|
+
|
|
198
|
+
See [`examples/api-manager/demo.ts`](https://github.com/senseikatana/katanakit-js/tree/main/examples/api-manager) for a runnable demo
|
|
199
|
+
covering all CRUD operations, auth injection, URL building, and error handling
|
|
200
|
+
against a real API (JSONPlaceholder).
|
|
201
|
+
|
|
61
202
|
## Features
|
|
62
203
|
|
|
63
204
|
- **Safe Results** — HTTP (and other fallible) operations return `{ data, error, ok }` instead of throwing
|
|
@@ -150,7 +291,7 @@ Also on the main barrel:
|
|
|
150
291
|
| `useResetSession` | `(sessionId) => Promise<void>` |
|
|
151
292
|
| `useCreateMemoryStore` | `() => ConversationStore` |
|
|
152
293
|
|
|
153
|
-
Then start a channel (see below). Copy keys from [`.env.example`](.env.example):
|
|
294
|
+
Then start a channel (see below). Copy keys from [`.env.example`](https://github.com/senseikatana/katanakit-js/blob/main/.env.example):
|
|
154
295
|
|
|
155
296
|
```env
|
|
156
297
|
DASHSCOPE_API_KEY=
|
|
@@ -285,7 +426,7 @@ owns the database, run `prisma contract emit` then `prisma db init` in that app.
|
|
|
285
426
|
|
|
286
427
|
### Real use case
|
|
287
428
|
|
|
288
|
-
[`examples/assistant/`](examples/assistant
|
|
429
|
+
[`examples/assistant/`](https://github.com/senseikatana/katanakit-js/tree/main/examples/assistant) is a generic digital assistant with two demo tools:
|
|
289
430
|
`readFile` on `knowledge-base.md` and `saveNote` to `notes.jsonl`.
|
|
290
431
|
|
|
291
432
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "katanakit-js",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.2",
|
|
4
4
|
"description": "KatanaKit — a sharp, framework-agnostic TypeScript service toolkit organized with hexagonal architecture.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
|
@@ -73,16 +73,18 @@
|
|
|
73
73
|
"telegram:dev": "tsx src/adapters/telegram/main.ts",
|
|
74
74
|
"whatsapp:dev": "tsx src/adapters/whatsapp/main.ts",
|
|
75
75
|
"assistant:demo": "tsx examples/assistant/demo.ts",
|
|
76
|
+
"api:demo": "tsx examples/api-manager/demo.ts",
|
|
76
77
|
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
77
|
-
"check": "biome check ./src && tsc --noEmit && vitest run",
|
|
78
|
-
"fix": "biome check --write ./src && tsc --noEmit && vitest run",
|
|
78
|
+
"check": "biome check ./src ./scripts && tsc --noEmit && vitest run",
|
|
79
|
+
"fix": "biome check --write ./src ./scripts && tsc --noEmit && vitest run",
|
|
79
80
|
"build": "yarn clean && yarn run check && tsc -p tsconfig.json",
|
|
80
81
|
"bump:patch": "yarn version patch --all && git commit -am \"chore: release v$(node -p \"require('./package.json').version\")\" && git tag \"v$(node -p \"require('./package.json').version\")\"",
|
|
81
82
|
"bump:minor": "yarn version minor --all && git commit -am \"chore: release v$(node -p \"require('./package.json').version\")\" && git tag \"v$(node -p \"require('./package.json').version\")\"",
|
|
82
83
|
"bump:major": "yarn version major --all && git commit -am \"chore: release v$(node -p \"require('./package.json').version\")\" && git tag \"v$(node -p \"require('./package.json').version\")\"",
|
|
83
|
-
"release": "yarn build && yarn bump:patch &&
|
|
84
|
-
"release:minor": "yarn build && yarn bump:minor &&
|
|
85
|
-
"release:major": "yarn build && yarn bump:major &&
|
|
84
|
+
"release": "yarn build && yarn bump:patch && npm publish --access public && git push && git push --tags && node scripts/generate-release-notes.mjs \"v$(node -p \"require('./package.json').version\")\" | gh release create \"v$(node -p \"require('./package.json').version\")\" --notes-file -",
|
|
85
|
+
"release:minor": "yarn build && yarn bump:minor && npm publish --access public && git push && git push --tags && node scripts/generate-release-notes.mjs \"v$(node -p \"require('./package.json').version\")\" | gh release create \"v$(node -p \"require('./package.json').version\")\" --notes-file -",
|
|
86
|
+
"release:major": "yarn build && yarn bump:major && npm publish --access public && git push && git push --tags && node scripts/generate-release-notes.mjs \"v$(node -p \"require('./package.json').version\")\" | gh release create \"v$(node -p \"require('./package.json').version\")\" --notes-file -",
|
|
87
|
+
"notes": "node scripts/generate-release-notes.mjs",
|
|
86
88
|
"version:sync": "node -e \"const v=require('child_process').execSync('git tag --sort=-v:refname').toString().split('\\n')[0].replace('v',''); ['package.json','docs/package.json'].forEach(f=>{const p=require('./'+f); p.version=v; require('fs').writeFileSync(f,JSON.stringify(p,null,2)+'\\n')}); console.log('Synced to v'+v)\"",
|
|
87
89
|
"docs:dev": "yarn workspace katanakit-docs run dev",
|
|
88
90
|
"docs:build": "yarn workspace katanakit-docs run build",
|
|
@@ -115,7 +117,7 @@
|
|
|
115
117
|
"unpkg": "./dist/index.js",
|
|
116
118
|
"jsdelivr": "./dist/index.js",
|
|
117
119
|
"dependencies": {
|
|
118
|
-
"@js-temporal/polyfill": "
|
|
120
|
+
"@js-temporal/polyfill": "0.5.1",
|
|
119
121
|
"@prisma/orm-postgres": "8.0.0-rc.8"
|
|
120
122
|
},
|
|
121
123
|
"peerDependencies": {
|
|
@@ -139,21 +141,24 @@
|
|
|
139
141
|
}
|
|
140
142
|
},
|
|
141
143
|
"devDependencies": {
|
|
142
|
-
"@biomejs/biome": "
|
|
143
|
-
"@types/
|
|
144
|
-
"@types/
|
|
145
|
-
"@types/express": "
|
|
146
|
-
"@types/
|
|
147
|
-
"
|
|
148
|
-
"
|
|
149
|
-
"
|
|
150
|
-
"express": "^5.2.1",
|
|
144
|
+
"@biomejs/biome": "2.5.12",
|
|
145
|
+
"@types/cors": "2.8.19",
|
|
146
|
+
"@types/express": "5.0.6",
|
|
147
|
+
"@types/express-rate-limit": "6.0.2",
|
|
148
|
+
"@types/node": "26.5.0",
|
|
149
|
+
"cors": "2.8.6",
|
|
150
|
+
"dotenv": "17.4.2",
|
|
151
|
+
"express": "5.2.1",
|
|
151
152
|
"prisma": "8.0.0-rc.13",
|
|
152
|
-
"tsx": "
|
|
153
|
-
"typescript": "
|
|
154
|
-
"vite": "
|
|
155
|
-
"vitest": "
|
|
156
|
-
"vue": "
|
|
153
|
+
"tsx": "4.23.13",
|
|
154
|
+
"typescript": "7.0.2",
|
|
155
|
+
"vite": "8.2.2",
|
|
156
|
+
"vitest": "5.0.0",
|
|
157
|
+
"vue": "3.5.42"
|
|
158
|
+
},
|
|
159
|
+
"resolutions": {
|
|
160
|
+
"pathe": "2.0.3",
|
|
161
|
+
"jsbi": "4.3.2"
|
|
157
162
|
},
|
|
158
163
|
"packageManager": "yarn@4.18.0"
|
|
159
164
|
}
|