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.
Files changed (2) hide show
  1. package/README.md +143 -2
  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/) is a generic digital assistant with two demo tools:
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.0",
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 && yarn npm publish && git push && git push --tags && gh release create \"v$(node -p \"require('./package.json').version\")\" --generate-notes",
84
- "release:minor": "yarn build && yarn bump:minor && yarn npm publish && git push && git push --tags && gh release create \"v$(node -p \"require('./package.json').version\")\" --generate-notes",
85
- "release:major": "yarn build && yarn bump:major && yarn npm publish && git push && git push --tags && gh release create \"v$(node -p \"require('./package.json').version\")\" --generate-notes",
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": "^0.5.1",
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": "^2.5.12",
143
- "@types/bun": "^1.4.2",
144
- "@types/cors": "^2.8.19",
145
- "@types/express": "^5.0.6",
146
- "@types/express-rate-limit": "^6.0.2",
147
- "@types/node": "^26.5.0",
148
- "cors": "^2.8.6",
149
- "dotenv": "^17.4.2",
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": "^4.23.13",
153
- "typescript": "^7.0.2",
154
- "vite": "^8.2.2",
155
- "vitest": "^5.0.0",
156
- "vue": "^3.5.42"
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
  }