katanakit-js 2.14.0 → 2.14.1

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 +141 -0
  2. package/package.json +23 -19
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`](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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "katanakit-js",
3
- "version": "2.14.0",
3
+ "version": "2.14.1",
4
4
  "description": "KatanaKit — a sharp, framework-agnostic TypeScript service toolkit organized with hexagonal architecture.",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -73,6 +73,7 @@
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
78
  "check": "biome check ./src && tsc --noEmit && vitest run",
78
79
  "fix": "biome check --write ./src && tsc --noEmit && vitest run",
@@ -80,9 +81,9 @@
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 && gh release create \"v$(node -p \"require('./package.json').version\")\" --generate-notes",
85
+ "release:minor": "yarn build && yarn bump:minor && npm publish --access public && git push && git push --tags && gh release create \"v$(node -p \"require('./package.json').version\")\" --generate-notes",
86
+ "release:major": "yarn build && yarn bump:major && npm publish --access public && git push && git push --tags && gh release create \"v$(node -p \"require('./package.json').version\")\" --generate-notes",
86
87
  "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
88
  "docs:dev": "yarn workspace katanakit-docs run dev",
88
89
  "docs:build": "yarn workspace katanakit-docs run build",
@@ -115,7 +116,7 @@
115
116
  "unpkg": "./dist/index.js",
116
117
  "jsdelivr": "./dist/index.js",
117
118
  "dependencies": {
118
- "@js-temporal/polyfill": "^0.5.1",
119
+ "@js-temporal/polyfill": "0.5.1",
119
120
  "@prisma/orm-postgres": "8.0.0-rc.8"
120
121
  },
121
122
  "peerDependencies": {
@@ -139,21 +140,24 @@
139
140
  }
140
141
  },
141
142
  "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",
143
+ "@biomejs/biome": "2.5.12",
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",
151
151
  "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"
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"
157
+ },
158
+ "resolutions": {
159
+ "pathe": "2.0.3",
160
+ "jsbi": "4.3.2"
157
161
  },
158
162
  "packageManager": "yarn@4.18.0"
159
163
  }