katanakit-js 3.2.2 → 4.0.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.
- package/README.md +435 -307
- package/dist/adapters/angular/fetch.d.ts +37 -0
- package/dist/adapters/angular/fetch.d.ts.map +1 -0
- package/dist/adapters/angular/fetch.js +67 -0
- package/dist/adapters/angular/fetch.js.map +1 -0
- package/dist/adapters/angular/index.d.ts +4 -0
- package/dist/adapters/angular/index.d.ts.map +1 -0
- package/dist/adapters/angular/index.js +4 -0
- package/dist/adapters/angular/index.js.map +1 -0
- package/dist/adapters/angular/query.d.ts +107 -0
- package/dist/adapters/angular/query.d.ts.map +1 -0
- package/dist/adapters/angular/query.js +166 -0
- package/dist/adapters/angular/query.js.map +1 -0
- package/dist/adapters/angular/watch.d.ts +34 -0
- package/dist/adapters/angular/watch.d.ts.map +1 -0
- package/dist/adapters/angular/watch.js +38 -0
- package/dist/adapters/angular/watch.js.map +1 -0
- package/dist/adapters/assistant/assistant.service.js +1 -1
- package/dist/adapters/assistant/assistant.service.js.map +1 -1
- package/dist/adapters/astro/astro.service.d.ts +0 -1
- package/dist/adapters/astro/astro.service.d.ts.map +1 -1
- package/dist/adapters/astro/astro.service.js +0 -1
- package/dist/adapters/astro/astro.service.js.map +1 -1
- package/dist/adapters/bun/dummyjson.service.d.ts +88 -0
- package/dist/adapters/bun/dummyjson.service.d.ts.map +1 -0
- package/dist/adapters/bun/dummyjson.service.js +149 -0
- package/dist/adapters/bun/dummyjson.service.js.map +1 -0
- package/dist/adapters/bun/index.d.ts +5 -0
- package/dist/adapters/bun/index.d.ts.map +1 -0
- package/dist/adapters/bun/index.js +5 -0
- package/dist/adapters/bun/index.js.map +1 -0
- package/dist/adapters/bun/main.d.ts +2 -0
- package/dist/adapters/bun/main.d.ts.map +1 -0
- package/dist/adapters/bun/main.js +6 -0
- package/dist/adapters/bun/main.js.map +1 -0
- package/dist/adapters/bun/routes.d.ts +62 -0
- package/dist/adapters/bun/routes.d.ts.map +1 -0
- package/dist/adapters/bun/routes.js +60 -0
- package/dist/adapters/bun/routes.js.map +1 -0
- package/dist/adapters/bun/seed.d.ts +65 -0
- package/dist/adapters/bun/seed.d.ts.map +1 -0
- package/dist/adapters/bun/seed.js +117 -0
- package/dist/adapters/bun/seed.js.map +1 -0
- package/dist/adapters/bun/server.d.ts +53 -0
- package/dist/adapters/bun/server.d.ts.map +1 -0
- package/dist/adapters/bun/server.js +64 -0
- package/dist/adapters/bun/server.js.map +1 -0
- package/dist/adapters/express/app.d.ts +2 -1
- package/dist/adapters/express/app.d.ts.map +1 -1
- package/dist/adapters/express/app.js +14 -2
- package/dist/adapters/express/app.js.map +1 -1
- package/dist/adapters/express/products.controller.d.ts.map +1 -1
- package/dist/adapters/express/products.controller.js +3 -1
- package/dist/adapters/express/products.controller.js.map +1 -1
- package/dist/adapters/express/server.js +2 -2
- package/dist/adapters/express/server.js.map +1 -1
- package/dist/adapters/notion/notion.service.d.ts +4 -2
- package/dist/adapters/notion/notion.service.d.ts.map +1 -1
- package/dist/adapters/notion/notion.service.js +19 -5
- package/dist/adapters/notion/notion.service.js.map +1 -1
- package/dist/adapters/react/fetch.d.ts +44 -0
- package/dist/adapters/react/fetch.d.ts.map +1 -0
- package/dist/adapters/react/fetch.js +74 -0
- package/dist/adapters/react/fetch.js.map +1 -0
- package/dist/adapters/react/index.d.ts +4 -0
- package/dist/adapters/react/index.d.ts.map +1 -0
- package/dist/adapters/react/index.js +4 -0
- package/dist/adapters/react/index.js.map +1 -0
- package/dist/adapters/react/query.d.ts +104 -0
- package/dist/adapters/react/query.d.ts.map +1 -0
- package/dist/adapters/react/query.js +200 -0
- package/dist/adapters/react/query.js.map +1 -0
- package/dist/adapters/react/watch.d.ts +37 -0
- package/dist/adapters/react/watch.d.ts.map +1 -0
- package/dist/adapters/react/watch.js +39 -0
- package/dist/adapters/react/watch.js.map +1 -0
- package/dist/adapters/solid/fetch.d.ts +43 -0
- package/dist/adapters/solid/fetch.d.ts.map +1 -0
- package/dist/adapters/solid/fetch.js +72 -0
- package/dist/adapters/solid/fetch.js.map +1 -0
- package/dist/adapters/solid/index.d.ts +4 -0
- package/dist/adapters/solid/index.d.ts.map +1 -0
- package/dist/adapters/solid/index.js +4 -0
- package/dist/adapters/solid/index.js.map +1 -0
- package/dist/adapters/solid/query.d.ts +102 -0
- package/dist/adapters/solid/query.d.ts.map +1 -0
- package/dist/adapters/solid/query.js +163 -0
- package/dist/adapters/solid/query.js.map +1 -0
- package/dist/adapters/solid/watch.d.ts +32 -0
- package/dist/adapters/solid/watch.d.ts.map +1 -0
- package/dist/adapters/solid/watch.js +36 -0
- package/dist/adapters/solid/watch.js.map +1 -0
- package/dist/adapters/svelte/fetch.d.ts +40 -0
- package/dist/adapters/svelte/fetch.d.ts.map +1 -0
- package/dist/adapters/svelte/fetch.js +75 -0
- package/dist/adapters/svelte/fetch.js.map +1 -0
- package/dist/adapters/svelte/index.d.ts +4 -0
- package/dist/adapters/svelte/index.d.ts.map +1 -0
- package/dist/adapters/svelte/index.js +4 -0
- package/dist/adapters/svelte/index.js.map +1 -0
- package/dist/adapters/svelte/query.d.ts +108 -0
- package/dist/adapters/svelte/query.d.ts.map +1 -0
- package/dist/adapters/svelte/query.js +172 -0
- package/dist/adapters/svelte/query.js.map +1 -0
- package/dist/adapters/svelte/watch.d.ts +32 -0
- package/dist/adapters/svelte/watch.d.ts.map +1 -0
- package/dist/adapters/svelte/watch.js +41 -0
- package/dist/adapters/svelte/watch.js.map +1 -0
- package/dist/adapters/telegram/telegram.service.js +1 -1
- package/dist/adapters/telegram/telegram.service.js.map +1 -1
- package/dist/adapters/vue/query.d.ts +1 -2
- package/dist/adapters/vue/query.d.ts.map +1 -1
- package/dist/adapters/vue/query.js +9 -9
- package/dist/adapters/vue/query.js.map +1 -1
- package/dist/adapters/vue/vue.service.d.ts +9 -5
- package/dist/adapters/vue/vue.service.d.ts.map +1 -1
- package/dist/adapters/vue/vue.service.js +7 -3
- package/dist/adapters/vue/vue.service.js.map +1 -1
- package/dist/adapters/whatsapp/whatsapp.service.js +1 -1
- package/dist/adapters/whatsapp/whatsapp.service.js.map +1 -1
- package/dist/adapters/wordpress/wordpress.service.d.ts +2 -1
- package/dist/adapters/wordpress/wordpress.service.d.ts.map +1 -1
- package/dist/adapters/wordpress/wordpress.service.js +10 -5
- package/dist/adapters/wordpress/wordpress.service.js.map +1 -1
- package/dist/core/services/agent.service.d.ts.map +1 -1
- package/dist/core/services/agent.service.js +9 -1
- package/dist/core/services/agent.service.js.map +1 -1
- package/dist/core/services/dates.service.d.ts.map +1 -1
- package/dist/core/services/dates.service.js +2 -1
- package/dist/core/services/dates.service.js.map +1 -1
- package/dist/core/services/error.service.d.ts +1 -1
- package/dist/core/services/error.service.js +1 -1
- package/dist/core/services/http.service.d.ts +26 -5
- package/dist/core/services/http.service.d.ts.map +1 -1
- package/dist/core/services/http.service.js +30 -6
- package/dist/core/services/http.service.js.map +1 -1
- package/dist/core/services/logger.service.d.ts +10 -67
- package/dist/core/services/logger.service.d.ts.map +1 -1
- package/dist/core/services/logger.service.js +13 -139
- package/dist/core/services/logger.service.js.map +1 -1
- package/dist/core/services/query.service.d.ts +4 -0
- package/dist/core/services/query.service.d.ts.map +1 -1
- package/dist/core/services/query.service.js +75 -63
- package/dist/core/services/query.service.js.map +1 -1
- package/dist/core/services/reactive.service.d.ts.map +1 -1
- package/dist/core/services/reactive.service.js +7 -10
- package/dist/core/services/reactive.service.js.map +1 -1
- package/dist/core/services/timing.service.js +8 -8
- package/dist/core/services/timing.service.js.map +1 -1
- package/dist/core/services/utils.service.d.ts.map +1 -1
- package/dist/core/services/utils.service.js +3 -0
- package/dist/core/services/utils.service.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/dom/dom.service.d.ts.map +1 -1
- package/dist/infrastructure/dom/dom.service.js +18 -4
- package/dist/infrastructure/dom/dom.service.js.map +1 -1
- package/dist/infrastructure/observer/observer.service.js +2 -2
- package/dist/infrastructure/observer/observer.service.js.map +1 -1
- package/dist/infrastructure/sensors/sensors.service.js +6 -6
- package/dist/infrastructure/sensors/sensors.service.js.map +1 -1
- package/dist/infrastructure/storage/storage.service.d.ts.map +1 -1
- package/dist/infrastructure/storage/storage.service.js +12 -2
- package/dist/infrastructure/storage/storage.service.js.map +1 -1
- package/dist/prisma/assistant.store.d.ts +3 -0
- package/dist/prisma/assistant.store.d.ts.map +1 -1
- package/dist/prisma/assistant.store.js +12 -6
- package/dist/prisma/assistant.store.js.map +1 -1
- package/dist/prisma/use-prisma.d.ts +5 -4
- package/dist/prisma/use-prisma.d.ts.map +1 -1
- package/dist/prisma/use-prisma.js +5 -4
- package/dist/prisma/use-prisma.js.map +1 -1
- package/dist/types/index.d.ts +2 -6
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +244 -185
- package/dist/prisma/db.d.ts +0 -4
- package/dist/prisma/db.d.ts.map +0 -1
- package/dist/prisma/db.js +0 -12
- package/dist/prisma/db.js.map +0 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ npm install katanakit-js
|
|
|
9
9
|
# or
|
|
10
10
|
bun add katanakit-js
|
|
11
11
|
# or
|
|
12
|
-
|
|
12
|
+
bun add katanakit-js
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
### CDN (ESM)
|
|
@@ -18,16 +18,16 @@ In the browser, use jsDelivr **`/+esm`** so named exports and dependencies resol
|
|
|
18
18
|
|
|
19
19
|
```html
|
|
20
20
|
<script type="module">
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
import { useLogger, useGetApi, useInitApis } from "https://cdn.jsdelivr.net/npm/katanakit-js/+esm";
|
|
22
|
+
useLogger("ready");
|
|
23
23
|
</script>
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
| CDN
|
|
27
|
-
|
|
28
|
-
| **jsDelivr `/+esm`** (recommended) | `https://cdn.jsdelivr.net/npm/katanakit-js/+esm`
|
|
29
|
-
| **esm.sh**
|
|
30
|
-
| **Raw ESM file**
|
|
26
|
+
| CDN | URL |
|
|
27
|
+
| ---------------------------------- | --------------------------------------------------------------------------------------- |
|
|
28
|
+
| **jsDelivr `/+esm`** (recommended) | `https://cdn.jsdelivr.net/npm/katanakit-js/+esm` |
|
|
29
|
+
| **esm.sh** | `https://esm.sh/katanakit-js` |
|
|
30
|
+
| **Raw ESM file** | `https://cdn.jsdelivr.net/npm/katanakit-js/dist/index.js` (needs bundler or import map) |
|
|
31
31
|
|
|
32
32
|
Pin a version in production (e.g. `@2.14.2/+esm`). There is no IIFE/UMD build.
|
|
33
33
|
|
|
@@ -40,21 +40,21 @@ useLogger("boot");
|
|
|
40
40
|
|
|
41
41
|
// Register your APIs once
|
|
42
42
|
useInitApis({
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
pokeapi: {
|
|
44
|
+
baseUri: "https://pokeapi.co/api/v2",
|
|
45
|
+
endpoints: { pokemonById: "/pokemon/:id/" },
|
|
46
|
+
},
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
// Fetch with Safe Result — no try/catch needed for HTTP failures
|
|
50
50
|
const result = await useGetApi<{ name: string }>("pokeapi", "pokemonById", {
|
|
51
|
-
|
|
51
|
+
params: { id: 25 },
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
if (result.ok) {
|
|
55
|
-
|
|
55
|
+
console.log(result.data.name); // "pikachu"
|
|
56
56
|
} else {
|
|
57
|
-
|
|
57
|
+
console.error(result.error.message);
|
|
58
58
|
}
|
|
59
59
|
```
|
|
60
60
|
|
|
@@ -70,28 +70,28 @@ and returns a **Safe Result** (`{ ok, data, error }`) that never throws on HTTP
|
|
|
70
70
|
import { useInitApis } from "katanakit-js";
|
|
71
71
|
|
|
72
72
|
useInitApis({
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
95
|
});
|
|
96
96
|
```
|
|
97
97
|
|
|
@@ -106,13 +106,13 @@ if (list.ok) console.log(list.data);
|
|
|
106
106
|
|
|
107
107
|
// Read by ID — :id is replaced by params
|
|
108
108
|
const post = await useGetApi<{ title: string }>("jsonplaceholder", "postById", {
|
|
109
|
-
|
|
109
|
+
params: { id: 1 },
|
|
110
110
|
});
|
|
111
111
|
if (post.ok) console.log(post.data.title);
|
|
112
112
|
|
|
113
113
|
// Override default query params
|
|
114
114
|
const filtered = await useGetApi("jsonplaceholder", "posts", {
|
|
115
|
-
|
|
115
|
+
query: { _limit: 5, userId: 1 },
|
|
116
116
|
});
|
|
117
117
|
```
|
|
118
118
|
|
|
@@ -123,8 +123,8 @@ import { usePost, usePut, usePatch, useDelete } from "katanakit-js";
|
|
|
123
123
|
|
|
124
124
|
// POST — body is auto-serialized to JSON
|
|
125
125
|
const created = await usePost<{ id: number }>("myApi", "createUser", {
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
name: "Alice",
|
|
127
|
+
email: "alice@example.com",
|
|
128
128
|
});
|
|
129
129
|
|
|
130
130
|
// PUT — full replacement (body + path params)
|
|
@@ -143,10 +143,10 @@ There's no global interceptor — pass `headers` directly. This keeps things exp
|
|
|
143
143
|
|
|
144
144
|
```ts
|
|
145
145
|
const result = await useFetch("myApi", "users", {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
146
|
+
method: "GET",
|
|
147
|
+
headers: {
|
|
148
|
+
Authorization: `Bearer ${getToken()}`,
|
|
149
|
+
},
|
|
150
150
|
});
|
|
151
151
|
```
|
|
152
152
|
|
|
@@ -158,27 +158,82 @@ Every fetch returns `{ ok, data, error, status, url }`. No try/catch needed for
|
|
|
158
158
|
const result = await useGetApi("myApi", "userById", { params: { id: 99999 } });
|
|
159
159
|
|
|
160
160
|
if (result.ok) {
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
// result.data is typed
|
|
162
|
+
console.log(result.data);
|
|
163
163
|
} else {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
169
|
}
|
|
170
170
|
```
|
|
171
171
|
|
|
172
172
|
### 6. Build URLs without fetching
|
|
173
173
|
|
|
174
|
+
`useBuildUrl` constructs a full URL from your registered APIs **without making
|
|
175
|
+
a request**. Use it to generate links, image sources, or pass URLs to libraries
|
|
176
|
+
that handle their own fetching.
|
|
177
|
+
|
|
178
|
+
#### Generate navigation links
|
|
179
|
+
|
|
174
180
|
```ts
|
|
175
181
|
import { useBuildUrl } from "katanakit-js";
|
|
176
182
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
query: {
|
|
183
|
+
// Build a download link
|
|
184
|
+
const downloadUrl = useBuildUrl("myApi", "export", {
|
|
185
|
+
query: { format: "pdf", lang: "es" },
|
|
186
|
+
});
|
|
187
|
+
// → "https://api.myapp.com/v1/export?format=pdf&lang=es"
|
|
188
|
+
|
|
189
|
+
// Use in a template
|
|
190
|
+
<a href={downloadUrl}>Download PDF</a>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### Generate image/file URLs
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
// Build an image URL for <img src>
|
|
197
|
+
const avatarUrl = useBuildUrl("myApi", "userAvatar", {
|
|
198
|
+
params: { id: 42 },
|
|
199
|
+
query: { size: "large" },
|
|
180
200
|
});
|
|
181
|
-
// "https://
|
|
201
|
+
// → "https://api.myapp.com/v1/users/42/avatar?size=large"
|
|
202
|
+
|
|
203
|
+
<img src={avatarUrl} alt="User avatar" />
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### Pass to third-party libraries
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
// Charts, maps, analytics — libraries that fetch their own data
|
|
210
|
+
const chartDataUrl = useBuildUrl("myApi", "analytics", {
|
|
211
|
+
query: { range: "7d", metric: "visits" },
|
|
212
|
+
});
|
|
213
|
+
new Chart(canvas, { data: chartDataUrl });
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
#### Debug before fetching
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
// See the full URL before making the request
|
|
220
|
+
console.log(
|
|
221
|
+
"Will fetch:",
|
|
222
|
+
useBuildUrl("myApi", "users", {
|
|
223
|
+
query: { page: 1, per_page: 20 },
|
|
224
|
+
}),
|
|
225
|
+
);
|
|
226
|
+
// → "https://api.myapp.com/v1/users?page=1&per_page=20"
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### SSR: construct URLs server-side
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
// In Astro, Next.js, or Nuxt server code
|
|
233
|
+
const apiUrl = useBuildUrl("notion", "database", {
|
|
234
|
+
params: { id: "db-123" },
|
|
235
|
+
});
|
|
236
|
+
// Pass to client component or pre-render
|
|
182
237
|
```
|
|
183
238
|
|
|
184
239
|
### 7. FormData and raw bodies
|
|
@@ -195,7 +250,7 @@ await usePost("myApi", "upload", form);
|
|
|
195
250
|
|
|
196
251
|
### Full example
|
|
197
252
|
|
|
198
|
-
See [`examples/api-manager/demo.ts`](https://github.com/senseikatana/katanakit
|
|
253
|
+
See [`examples/api-manager/demo.ts`](https://github.com/senseikatana/katanakit/tree/main/examples/api-manager) for a runnable demo
|
|
199
254
|
covering all CRUD operations, auth injection, URL building, and error handling
|
|
200
255
|
against a real API (JSONPlaceholder).
|
|
201
256
|
|
|
@@ -214,11 +269,11 @@ const qc = useQueryClient();
|
|
|
214
269
|
|
|
215
270
|
// Fetch with cache, stale-while-revalidate, retry, and dedup.
|
|
216
271
|
const pokemon = await qc.fetchQuery<Pokemon>({
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
272
|
+
queryKey: ["pokemon", 25],
|
|
273
|
+
queryFn: () => useGetApi<Pokemon>("pokeapi", "pokemonById", { params: { id: 25 } }),
|
|
274
|
+
staleTime: 60_000, // Cache is fresh for 60s.
|
|
275
|
+
retry: 3, // Retry 3 times on failure.
|
|
276
|
+
refetchOnWindowFocus: true,
|
|
222
277
|
});
|
|
223
278
|
```
|
|
224
279
|
|
|
@@ -286,6 +341,27 @@ when you want sessions, persistence, and channels (REST, Telegram, WhatsApp). Fa
|
|
|
286
341
|
follow the Safe Result pattern: they **never throw**. Check `result.ok` and read `result.data`
|
|
287
342
|
or `result.error`.
|
|
288
343
|
|
|
344
|
+
### When to use Kitt
|
|
345
|
+
|
|
346
|
+
Kitt is a **lightweight, zero-dependency** wrapper for OpenAI-compatible APIs.
|
|
347
|
+
Use it when you need:
|
|
348
|
+
|
|
349
|
+
- **One-shot chat** — ask a question, get an answer (`useChat`)
|
|
350
|
+
- **Tool-calling agents** — autonomous loops that read/write/run (`useRunAgent`)
|
|
351
|
+
- **Session-aware bots** — REST, Telegram, WhatsApp channels (`useReply`)
|
|
352
|
+
|
|
353
|
+
### When to use alternatives
|
|
354
|
+
|
|
355
|
+
| Need | Use instead |
|
|
356
|
+
| ------------------------- | --------------------------------------------- |
|
|
357
|
+
| Streaming token-by-token | [Vercel AI SDK](https://sdk.vercel.ai) |
|
|
358
|
+
| RAG with embeddings | [LangChain.js](https://js.langchain.com) |
|
|
359
|
+
| Multi-agent orchestration | [CrewAI](https://github.com/crewAIInc/crewAI) |
|
|
360
|
+
| Full chatbot framework | [Botpress](https://botpress.com) |
|
|
361
|
+
|
|
362
|
+
Kitt stays small (0 dependencies) because it does one thing well:
|
|
363
|
+
**chat completions with tool calls, using any OpenAI-compatible endpoint.**
|
|
364
|
+
|
|
289
365
|
### Low-level API
|
|
290
366
|
|
|
291
367
|
```ts
|
|
@@ -297,21 +373,21 @@ useInitAgent({ model: "qwen3.8-max" });
|
|
|
297
373
|
|
|
298
374
|
// Assistant — single-shot review / question.
|
|
299
375
|
const review = await useChat([
|
|
300
|
-
|
|
376
|
+
{ role: "user", content: "Summarize the benefits of solar energy in three bullet points." },
|
|
301
377
|
]);
|
|
302
378
|
if (review.ok) console.log(review.data);
|
|
303
379
|
|
|
304
380
|
// Agent — autonomous tool-calling loop that can act (read/write/run).
|
|
305
381
|
const result = await useRunAgent("Fix the type errors in src/", {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
382
|
+
tools: [
|
|
383
|
+
{
|
|
384
|
+
name: "readFile",
|
|
385
|
+
description: "Returns file contents",
|
|
386
|
+
parameters: { type: "object", properties: { path: { type: "string" } } },
|
|
387
|
+
execute: ({ path }) => fs.readFile(path, "utf8"),
|
|
388
|
+
},
|
|
389
|
+
],
|
|
390
|
+
maxSteps: 12,
|
|
315
391
|
});
|
|
316
392
|
```
|
|
317
393
|
|
|
@@ -331,20 +407,20 @@ import "dotenv/config";
|
|
|
331
407
|
import { useInitAssistant, useReply } from "katanakit-js";
|
|
332
408
|
|
|
333
409
|
useInitAssistant({
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
410
|
+
// apiKey — process.env.DASHSCOPE_API_KEY
|
|
411
|
+
// baseUrl — KITT_BASE_URL
|
|
412
|
+
// model — "qwen3.8-max"
|
|
413
|
+
// systemPrompt — KITT_SYSTEM_PROMPT
|
|
414
|
+
// store — in-memory (useCreateMemoryStore)
|
|
415
|
+
// tools — AiTool[]
|
|
416
|
+
// maxSteps — number
|
|
341
417
|
});
|
|
342
418
|
|
|
343
419
|
const result = await useReply(undefined, "What are your hours?");
|
|
344
420
|
if (result.ok) {
|
|
345
|
-
|
|
421
|
+
console.log(result.data.reply, result.data.sessionId);
|
|
346
422
|
} else {
|
|
347
|
-
|
|
423
|
+
console.error(result.error.message);
|
|
348
424
|
}
|
|
349
425
|
```
|
|
350
426
|
|
|
@@ -353,14 +429,14 @@ if (result.ok) {
|
|
|
353
429
|
|
|
354
430
|
Also on the main barrel:
|
|
355
431
|
|
|
356
|
-
| Helper
|
|
357
|
-
|
|
358
|
-
| `useCreateSession`
|
|
359
|
-
| `useGetHistory`
|
|
360
|
-
| `useResetSession`
|
|
361
|
-
| `useCreateMemoryStore` | `() => ConversationStore`
|
|
432
|
+
| Helper | Signature |
|
|
433
|
+
| ---------------------- | ------------------------------------- |
|
|
434
|
+
| `useCreateSession` | `(channel?) => Promise<string>` |
|
|
435
|
+
| `useGetHistory` | `(sessionId) => Promise<AiMessage[]>` |
|
|
436
|
+
| `useResetSession` | `(sessionId) => Promise<void>` |
|
|
437
|
+
| `useCreateMemoryStore` | `() => ConversationStore` |
|
|
362
438
|
|
|
363
|
-
Then start a channel (see below). Copy keys from [`.env.example`](https://github.com/senseikatana/katanakit
|
|
439
|
+
Then start a channel (see below). Copy keys from [`.env.example`](https://github.com/senseikatana/katanakit/blob/main/.env.example):
|
|
364
440
|
|
|
365
441
|
```env
|
|
366
442
|
DASHSCOPE_API_KEY=
|
|
@@ -377,12 +453,12 @@ DATABASE_URL=
|
|
|
377
453
|
|
|
378
454
|
### Run standalone (this repo)
|
|
379
455
|
|
|
380
|
-
| Script
|
|
381
|
-
|
|
382
|
-
| `
|
|
383
|
-
| `
|
|
384
|
-
| `
|
|
385
|
-
| `
|
|
456
|
+
| Script | Starts |
|
|
457
|
+
| ------------------------ | --------------------------------------------------------------------------- |
|
|
458
|
+
| `bun run assistant:dev` | REST assistant. Uses Prisma when `DATABASE_URL` is set. |
|
|
459
|
+
| `bun run telegram:dev` | Telegram long polling. Requires `TELEGRAM_BOT_TOKEN`. |
|
|
460
|
+
| `bun run whatsapp:dev` | WhatsApp webhook. Requires `WHATSAPP_*`. |
|
|
461
|
+
| `bun run assistant:demo` | Demo in `examples/assistant/`. Set `KITT_CHANNEL=rest\|telegram\|whatsapp`. |
|
|
386
462
|
|
|
387
463
|
### REST
|
|
388
464
|
|
|
@@ -393,21 +469,21 @@ useStartAssistant(); // port?, host?, mountPath = "/assistant", options?
|
|
|
393
469
|
// or mount useCreateAssistantRouter() on an existing Express app
|
|
394
470
|
```
|
|
395
471
|
|
|
396
|
-
| Method
|
|
397
|
-
|
|
398
|
-
| `POST`
|
|
399
|
-
| `GET`
|
|
400
|
-
| `DELETE` | `/assistant/sessions/:id` | —
|
|
472
|
+
| Method | Path | Body | Response |
|
|
473
|
+
| -------- | ------------------------- | ------------------------- | ------------------------------------------- |
|
|
474
|
+
| `POST` | `/assistant/chat` | `{ sessionId?, message }` | `{ ok, data: { reply, sessionId }, error }` |
|
|
475
|
+
| `GET` | `/assistant/sessions/:id` | — | `{ sessionId, messages }` or `404` |
|
|
476
|
+
| `DELETE` | `/assistant/sessions/:id` | — | `204` or `404` |
|
|
401
477
|
|
|
402
478
|
The endpoints are **public by default**. In production pass an auth guard (applied to every
|
|
403
479
|
route). Unknown session ids return `404`, and `useReply` rejects a `sessionId` that does not exist.
|
|
404
480
|
|
|
405
481
|
```ts
|
|
406
482
|
useStartAssistant(3000, "localhost", "/assistant", {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
483
|
+
guard: (req, res, next) =>
|
|
484
|
+
req.header("authorization") === `Bearer ${process.env.ASSISTANT_API_KEY}`
|
|
485
|
+
? next()
|
|
486
|
+
: res.status(401).end(),
|
|
411
487
|
});
|
|
412
488
|
```
|
|
413
489
|
|
|
@@ -422,9 +498,12 @@ curl -X POST http://localhost:3000/assistant/chat \
|
|
|
422
498
|
POST `/chat` and POST `/whatsapp/webhook` are rate-limited by default (20 req/min and 60 req/min per IP). Override via the router options:
|
|
423
499
|
|
|
424
500
|
```ts
|
|
425
|
-
app.use(
|
|
426
|
-
|
|
427
|
-
|
|
501
|
+
app.use(
|
|
502
|
+
"/assistant",
|
|
503
|
+
useCreateAssistantRouter({
|
|
504
|
+
rateLimit: rateLimit({ windowMs: 60_000, max: 30 }),
|
|
505
|
+
}),
|
|
506
|
+
);
|
|
428
507
|
```
|
|
429
508
|
|
|
430
509
|
### Telegram (BotFather)
|
|
@@ -442,7 +521,7 @@ await useStartTelegramPolling();
|
|
|
442
521
|
2. Send `/newbot` — choose a name and a username.
|
|
443
522
|
3. Copy the token.
|
|
444
523
|
4. Set `TELEGRAM_BOT_TOKEN`.
|
|
445
|
-
5. Run `
|
|
524
|
+
5. Run `bun run telegram:dev` (long polling, no public URL).
|
|
446
525
|
6. Optional webhook: expose HTTPS and call `useHandleTelegramUpdate(update)` on inbound updates.
|
|
447
526
|
|
|
448
527
|
### WhatsApp (Meta Cloud API)
|
|
@@ -453,10 +532,10 @@ Webhook: `GET` / `POST` `/whatsapp/webhook`. Session ids are `wa:<phone>`.
|
|
|
453
532
|
import { useInitWhatsApp, useStartWhatsApp } from "katanakit-js/adapters/whatsapp";
|
|
454
533
|
|
|
455
534
|
useInitWhatsApp({
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
535
|
+
token: process.env.WHATSAPP_TOKEN,
|
|
536
|
+
phoneNumberId: process.env.WHATSAPP_PHONE_NUMBER_ID,
|
|
537
|
+
verifyToken: process.env.WHATSAPP_VERIFY_TOKEN,
|
|
538
|
+
appSecret: process.env.WHATSAPP_APP_SECRET,
|
|
460
539
|
});
|
|
461
540
|
useStartWhatsApp();
|
|
462
541
|
```
|
|
@@ -465,7 +544,7 @@ useStartWhatsApp();
|
|
|
465
544
|
2. Add the WhatsApp product.
|
|
466
545
|
3. Copy the temporary or permanent token, the Phone number ID, and the **App Secret**.
|
|
467
546
|
4. Set `WHATSAPP_TOKEN`, `WHATSAPP_PHONE_NUMBER_ID`, `WHATSAPP_VERIFY_TOKEN`, and `WHATSAPP_APP_SECRET`.
|
|
468
|
-
5. Run `
|
|
547
|
+
5. Run `bun run whatsapp:dev`.
|
|
469
548
|
6. Expose public HTTPS (Cloudflare Tunnel or ngrok) to `GET`/`POST` `/whatsapp/webhook`.
|
|
470
549
|
7. In Meta, set the webhook URL and verify token; subscribe to `messages`.
|
|
471
550
|
|
|
@@ -496,11 +575,11 @@ owns the database, run `prisma contract emit` then `prisma db init` in that app.
|
|
|
496
575
|
|
|
497
576
|
### Real use case
|
|
498
577
|
|
|
499
|
-
[`examples/assistant/`](https://github.com/senseikatana/katanakit
|
|
578
|
+
[`examples/assistant/`](https://github.com/senseikatana/katanakit/tree/main/examples/assistant) is a generic digital assistant with two demo tools:
|
|
500
579
|
`readFile` on `knowledge-base.md` and `saveNote` to `notes.jsonl`.
|
|
501
580
|
|
|
502
581
|
```bash
|
|
503
|
-
|
|
582
|
+
bun run assistant:demo
|
|
504
583
|
# KITT_CHANNEL=rest|telegram|whatsapp
|
|
505
584
|
```
|
|
506
585
|
|
|
@@ -540,14 +619,14 @@ const result = await useGetApi("pokeapi", "pokemonById", { params: { id: 25 } })
|
|
|
540
619
|
|
|
541
620
|
```ts
|
|
542
621
|
import { useLogger, useInitApis, useGetApi } from "katanakit-js";
|
|
543
|
-
import {
|
|
544
|
-
import { useUnwrap } from "katanakit-js/adapters/nuxt";
|
|
622
|
+
import { useRequest } from "katanakit-js/adapters/vue"; // Vue only
|
|
623
|
+
import { useUnwrap } from "katanakit-js/adapters/nuxt"; // Nuxt only
|
|
545
624
|
```
|
|
546
625
|
|
|
547
626
|
```html
|
|
548
627
|
<!-- vanilla -->
|
|
549
628
|
<script type="module">
|
|
550
|
-
|
|
629
|
+
import { useLogger } from "https://cdn.jsdelivr.net/npm/katanakit-js/+esm";
|
|
551
630
|
</script>
|
|
552
631
|
```
|
|
553
632
|
|
|
@@ -555,24 +634,24 @@ See [Getting Started](https://senseikatana.com/katanakit-js/docs/guides/getting-
|
|
|
555
634
|
|
|
556
635
|
## Framework Adapters
|
|
557
636
|
|
|
558
|
-
| Adapter
|
|
559
|
-
|
|
560
|
-
| **Express**
|
|
561
|
-
| **Nuxt**
|
|
562
|
-
| **Vue**
|
|
563
|
-
| **Astro**
|
|
564
|
-
| **Assistant** | `katanakit-js/adapters/assistant`
|
|
565
|
-
| **Telegram**
|
|
566
|
-
| **WhatsApp**
|
|
637
|
+
| Adapter | Import | Description |
|
|
638
|
+
| ------------- | ----------------------------------------------- | ------------------------------------------------------------ |
|
|
639
|
+
| **Express** | `katanakit-js/adapters/express` | Reference server with CORS and hardened headers |
|
|
640
|
+
| **Nuxt** | `katanakit-js/adapters/nuxt` | `useUnwrap`, `useSafeResponse`, `useEventResponse` |
|
|
641
|
+
| **Vue** | `katanakit-js/adapters/vue` | `useRequest` composable with reactivity |
|
|
642
|
+
| **Astro** | `katanakit-js` or `katanakit-js/adapters/astro` | `AstroService`, `RssService` |
|
|
643
|
+
| **Assistant** | `katanakit-js/adapters/assistant` | REST digital assistant (`useStartAssistant`) |
|
|
644
|
+
| **Telegram** | `katanakit-js/adapters/telegram` | BotFather bot (`useInitTelegram`, `useStartTelegramPolling`) |
|
|
645
|
+
| **WhatsApp** | `katanakit-js/adapters/whatsapp` | Meta Cloud API (`useInitWhatsApp`, `useStartWhatsApp`) |
|
|
567
646
|
|
|
568
647
|
## REST API Adapters
|
|
569
648
|
|
|
570
649
|
Typed adapters for popular REST APIs with auth, pagination helpers, and full TypeScript types.
|
|
571
650
|
All functions return `FetchResult<T>` — the same Safe Result pattern used by the HTTP client.
|
|
572
651
|
|
|
573
|
-
| Adapter
|
|
574
|
-
|
|
575
|
-
| **Notion**
|
|
652
|
+
| Adapter | Import | Description |
|
|
653
|
+
| ------------- | --------------------------------- | ----------------------------------------------------------------- |
|
|
654
|
+
| **Notion** | `katanakit-js/adapters/notion` | Pages, databases, blocks, search with cursor pagination |
|
|
576
655
|
| **WordPress** | `katanakit-js/adapters/wordpress` | Posts, pages, media, categories, tags, comments, users, batch ops |
|
|
577
656
|
|
|
578
657
|
### Notion
|
|
@@ -593,10 +672,10 @@ useInitNotion({ token: process.env.NOTION_TOKEN });
|
|
|
593
672
|
|
|
594
673
|
```ts
|
|
595
674
|
import {
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
675
|
+
useNotionGetPage,
|
|
676
|
+
useNotionCreatePage,
|
|
677
|
+
useNotionUpdatePage,
|
|
678
|
+
useNotionArchivePage,
|
|
600
679
|
} from "katanakit-js/adapters/notion";
|
|
601
680
|
|
|
602
681
|
// Get a single page with all its properties
|
|
@@ -605,24 +684,24 @@ if (page.ok) console.log(page.data.properties);
|
|
|
605
684
|
|
|
606
685
|
// Create a page inside a database
|
|
607
686
|
const created = await useNotionCreatePage(
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
687
|
+
{ type: "database_id", database_id: "db-id" },
|
|
688
|
+
{
|
|
689
|
+
Name: { title: [{ type: "text", text: { content: "My Task" } }] },
|
|
690
|
+
Status: { select: { name: "To Do" } },
|
|
691
|
+
},
|
|
613
692
|
);
|
|
614
693
|
|
|
615
694
|
// Create a child page with content blocks
|
|
616
695
|
const child = await useNotionCreatePage(
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
696
|
+
{ type: "page_id", page_id: "parent-id" },
|
|
697
|
+
{ title: { title: [{ type: "text", text: { content: "Child Page" } }] } },
|
|
698
|
+
[{ type: "paragraph", paragraph: { rich_text: [{ type: "text", text: { content: "Hello!" } }] } }],
|
|
620
699
|
);
|
|
621
700
|
|
|
622
701
|
// Update page properties (only changed fields)
|
|
623
702
|
await useNotionUpdatePage("page-id", {
|
|
624
|
-
|
|
625
|
-
|
|
703
|
+
Status: { select: { name: "Done" } },
|
|
704
|
+
DueDate: { date: { start: "2025-12-31" } },
|
|
626
705
|
});
|
|
627
706
|
|
|
628
707
|
// Archive (soft-delete) a page
|
|
@@ -633,26 +712,26 @@ await useNotionArchivePage("page-id");
|
|
|
633
712
|
|
|
634
713
|
```ts
|
|
635
714
|
import {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
715
|
+
useNotionGetDatabase,
|
|
716
|
+
useNotionQueryDatabase,
|
|
717
|
+
useNotionCreateDatabase,
|
|
718
|
+
useNotionUpdateDatabase,
|
|
719
|
+
useNotionListAllDatabasePages,
|
|
641
720
|
} from "katanakit-js/adapters/notion";
|
|
642
721
|
|
|
643
722
|
// Inspect database schema (property names, types, options)
|
|
644
723
|
const schema = await useNotionGetDatabase("db-id");
|
|
645
724
|
if (schema.ok) {
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
725
|
+
Object.entries(schema.data.properties).forEach(([name, prop]) => {
|
|
726
|
+
console.log(`${name}: ${prop.type}`);
|
|
727
|
+
});
|
|
649
728
|
}
|
|
650
729
|
|
|
651
730
|
// Query with filter + sort (single page of results)
|
|
652
731
|
const page1 = await useNotionQueryDatabase("db-id", {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
732
|
+
filter: { property: "Status", select: { equals: "Published" } },
|
|
733
|
+
sorts: [{ property: "Date", direction: "descending" }],
|
|
734
|
+
page_size: 10,
|
|
656
735
|
});
|
|
657
736
|
|
|
658
737
|
// Get ALL pages (auto-pagination — handles cursors internally)
|
|
@@ -660,76 +739,74 @@ const all = await useNotionListAllDatabasePages("db-id");
|
|
|
660
739
|
|
|
661
740
|
// With filter + sort
|
|
662
741
|
const published = await useNotionListAllDatabasePages(
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
742
|
+
"db-id",
|
|
743
|
+
{ property: "Status", select: { equals: "Published" } },
|
|
744
|
+
[{ property: "Date", direction: "descending" }],
|
|
666
745
|
);
|
|
667
746
|
|
|
668
747
|
// Create a new database
|
|
669
748
|
await useNotionCreateDatabase(
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
749
|
+
{ type: "page_id", page_id: "parent-id" },
|
|
750
|
+
[{ type: "text", text: { content: "My Tasks" } }],
|
|
751
|
+
{
|
|
752
|
+
Name: { title: {} },
|
|
753
|
+
Status: { select: { options: [{ name: "To Do" }, { name: "Done" }] } },
|
|
754
|
+
Priority: { select: { options: [{ name: "Low" }, { name: "High" }] } },
|
|
755
|
+
},
|
|
677
756
|
);
|
|
678
757
|
|
|
679
758
|
// Rename a database
|
|
680
|
-
await useNotionUpdateDatabase("db-id", [
|
|
681
|
-
{ type: "text", text: { content: "Renamed Database" } },
|
|
682
|
-
]);
|
|
759
|
+
await useNotionUpdateDatabase("db-id", [{ type: "text", text: { content: "Renamed Database" } }]);
|
|
683
760
|
```
|
|
684
761
|
|
|
685
762
|
#### Blocks — read, write, append, delete page content
|
|
686
763
|
|
|
687
764
|
```ts
|
|
688
765
|
import {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
766
|
+
useNotionGetBlock,
|
|
767
|
+
useNotionGetBlockChildren,
|
|
768
|
+
useNotionListAllBlockChildren,
|
|
769
|
+
useNotionAppendBlocks,
|
|
770
|
+
useNotionUpdateBlock,
|
|
771
|
+
useNotionDeleteBlock,
|
|
695
772
|
} from "katanakit-js/adapters/notion";
|
|
696
773
|
|
|
697
774
|
// Get ALL blocks of a page (auto-pagination)
|
|
698
775
|
const blocks = await useNotionListAllBlockChildren("page-id");
|
|
699
776
|
if (blocks.ok) {
|
|
700
|
-
|
|
777
|
+
blocks.data.forEach((b) => console.log(b.type)); // "paragraph", "heading_1", etc.
|
|
701
778
|
}
|
|
702
779
|
|
|
703
780
|
// Manual pagination (for fine-grained cursor control)
|
|
704
781
|
const page = await useNotionGetBlockChildren("page-id", { page_size: 50 });
|
|
705
782
|
if (page.ok) {
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
783
|
+
console.log(page.data.results); // blocks
|
|
784
|
+
console.log(page.data.has_more); // true if more pages exist
|
|
785
|
+
console.log(page.data.next_cursor); // pass as start_cursor for next page
|
|
709
786
|
}
|
|
710
787
|
|
|
711
788
|
// Append content blocks to a page
|
|
712
789
|
await useNotionAppendBlocks("page-id", [
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
790
|
+
{
|
|
791
|
+
type: "heading_2",
|
|
792
|
+
heading_2: { rich_text: [{ type: "text", text: { content: "New Section" } }] },
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
type: "paragraph",
|
|
796
|
+
paragraph: { rich_text: [{ type: "text", text: { content: "Body text here." } }] },
|
|
797
|
+
},
|
|
798
|
+
{
|
|
799
|
+
type: "to_do",
|
|
800
|
+
to_do: {
|
|
801
|
+
rich_text: [{ type: "text", text: { content: "Checklist item" } }],
|
|
802
|
+
checked: false,
|
|
803
|
+
},
|
|
804
|
+
},
|
|
728
805
|
]);
|
|
729
806
|
|
|
730
807
|
// Update a block's content
|
|
731
808
|
await useNotionUpdateBlock("block-id", {
|
|
732
|
-
|
|
809
|
+
paragraph: { rich_text: [{ type: "text", text: { content: "Updated text" } }] },
|
|
733
810
|
});
|
|
734
811
|
|
|
735
812
|
// Delete (archive) a block
|
|
@@ -746,14 +823,14 @@ const found = await useNotionSearchContent({ query: "meeting notes" });
|
|
|
746
823
|
|
|
747
824
|
// Search only databases
|
|
748
825
|
const dbs = await useNotionSearchContent({
|
|
749
|
-
|
|
750
|
-
|
|
826
|
+
query: "tasks",
|
|
827
|
+
filter: { value: "database", property: "object" },
|
|
751
828
|
});
|
|
752
829
|
|
|
753
830
|
// Search only pages, sorted by recently edited
|
|
754
831
|
const pages = await useNotionSearchContent({
|
|
755
|
-
|
|
756
|
-
|
|
832
|
+
filter: { value: "page", property: "object" },
|
|
833
|
+
sort: { direction: "descending", timestamp: "last_edited_time" },
|
|
757
834
|
});
|
|
758
835
|
```
|
|
759
836
|
|
|
@@ -768,22 +845,22 @@ if (user.ok) console.log(user.data.name);
|
|
|
768
845
|
|
|
769
846
|
// List all workspace members + bots
|
|
770
847
|
const users = await useNotionListUsers();
|
|
771
|
-
if (users.ok) users.data.results.forEach(u => console.log(u.name));
|
|
848
|
+
if (users.ok) users.data.results.forEach((u) => console.log(u.name));
|
|
772
849
|
```
|
|
773
850
|
|
|
774
851
|
#### Framework examples
|
|
775
852
|
|
|
776
|
-
| Framework
|
|
777
|
-
|
|
778
|
-
| **Vue 3**
|
|
779
|
-
| **Vue 3**
|
|
780
|
-
| **Nuxt 3**
|
|
781
|
-
| **Nuxt 3**
|
|
782
|
-
| **Astro**
|
|
783
|
-
| **Astro**
|
|
784
|
-
| **Next.js** | [`examples/notion/next-blog.tsx`](https://github.com/senseikatana/katanakit
|
|
785
|
-
| **Next.js** | [`examples/notion/next-[slug].tsx`](https://github.com/senseikatana/katanakit
|
|
786
|
-
| **Node.js** | [`examples/notion/demo.ts`](https://github.com/senseikatana/katanakit
|
|
853
|
+
| Framework | Example | Description |
|
|
854
|
+
| ----------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------- |
|
|
855
|
+
| **Vue 3** | [`examples/notion/vue-blog.vue`](https://github.com/senseikatana/katanakit/tree/main/examples/notion/vue-blog.vue) | Blog listing with `useQuery` composable |
|
|
856
|
+
| **Vue 3** | [`examples/notion/vue-post.vue`](https://github.com/senseikatana/katanakit/tree/main/examples/notion/vue-post.vue) | Single post view |
|
|
857
|
+
| **Nuxt 3** | [`examples/notion/nuxt-blog.vue`](https://github.com/senseikatana/katanakit/tree/main/examples/notion/nuxt-blog.vue) | SSR blog listing with `useAsyncData` |
|
|
858
|
+
| **Nuxt 3** | [`examples/notion/nuxt-post.vue`](https://github.com/senseikatana/katanakit/tree/main/examples/notion/nuxt-post.vue) | SSR single post view |
|
|
859
|
+
| **Astro** | [`examples/notion/astro-blog.astro`](https://github.com/senseikatana/katanakit/tree/main/examples/notion/astro-blog.astro) | Static blog listing |
|
|
860
|
+
| **Astro** | [`examples/notion/astro-[slug].astro`](https://github.com/senseikatana/katanakit/tree/main/examples/notion/astro-[slug].astro) | Dynamic `[slug]` route |
|
|
861
|
+
| **Next.js** | [`examples/notion/next-blog.tsx`](https://github.com/senseikatana/katanakit/tree/main/examples/notion/next-blog.tsx) | Server component blog listing |
|
|
862
|
+
| **Next.js** | [`examples/notion/next-[slug].tsx`](https://github.com/senseikatana/katanakit/tree/main/examples/notion/next-[slug].tsx) | Dynamic `[slug]` page |
|
|
863
|
+
| **Node.js** | [`examples/notion/demo.ts`](https://github.com/senseikatana/katanakit/tree/main/examples/notion/demo.ts) | Runnable demo covering all Notion operations |
|
|
787
864
|
|
|
788
865
|
### WordPress
|
|
789
866
|
|
|
@@ -798,20 +875,20 @@ import { useInitWordPress } from "katanakit-js/adapters/wordpress";
|
|
|
798
875
|
|
|
799
876
|
// Application Passwords (recommended) — WP Admin → Users → Your Profile → Application Passwords
|
|
800
877
|
useInitWordPress({
|
|
801
|
-
|
|
802
|
-
|
|
878
|
+
baseUrl: "https://mysite.com",
|
|
879
|
+
auth: { type: "application-passwords", username: "admin", password: "xxxx xxxx xxxx" },
|
|
803
880
|
});
|
|
804
881
|
|
|
805
882
|
// JWT tokens
|
|
806
883
|
useInitWordPress({
|
|
807
|
-
|
|
808
|
-
|
|
884
|
+
baseUrl: "https://mysite.com",
|
|
885
|
+
auth: { type: "jwt", token: "eyJhbGci..." },
|
|
809
886
|
});
|
|
810
887
|
|
|
811
888
|
// Nonce-based (for WP themes)
|
|
812
889
|
useInitWordPress({
|
|
813
|
-
|
|
814
|
-
|
|
890
|
+
baseUrl: "https://mysite.com",
|
|
891
|
+
auth: { type: "nonce", nonce: "abc123" },
|
|
815
892
|
});
|
|
816
893
|
```
|
|
817
894
|
|
|
@@ -819,22 +896,22 @@ useInitWordPress({
|
|
|
819
896
|
|
|
820
897
|
```ts
|
|
821
898
|
import {
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
899
|
+
useWpGetPosts,
|
|
900
|
+
useWpGetPost,
|
|
901
|
+
useWpCreatePost,
|
|
902
|
+
useWpUpdatePost,
|
|
903
|
+
useWpDeletePost,
|
|
904
|
+
useWpListAllPosts,
|
|
905
|
+
useWpSearchAllPosts,
|
|
906
|
+
useWpFindPostBySlug,
|
|
830
907
|
} from "katanakit-js/adapters/wordpress";
|
|
831
908
|
|
|
832
909
|
// List published posts (paginated)
|
|
833
910
|
const posts = await useWpGetPosts({
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
911
|
+
per_page: 5,
|
|
912
|
+
status: "publish",
|
|
913
|
+
orderby: "date",
|
|
914
|
+
order: "desc",
|
|
838
915
|
});
|
|
839
916
|
|
|
840
917
|
// Get a single post with embedded resources
|
|
@@ -843,19 +920,19 @@ if (post.ok) console.log(post.data.title.rendered);
|
|
|
843
920
|
|
|
844
921
|
// Create a post
|
|
845
922
|
await useWpCreatePost({
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
923
|
+
title: "My New Post",
|
|
924
|
+
content: "<p>Hello World!</p>",
|
|
925
|
+
status: "publish", // "draft" | "pending" | "publish"
|
|
926
|
+
categories: [1, 3],
|
|
927
|
+
tags: [5, 8],
|
|
851
928
|
});
|
|
852
929
|
|
|
853
930
|
// Update a post
|
|
854
931
|
await useWpUpdatePost(42, { title: "Updated Title", status: "publish" });
|
|
855
932
|
|
|
856
933
|
// Delete (trash or permanent)
|
|
857
|
-
await useWpDeletePost(42);
|
|
858
|
-
await useWpDeletePost(42, true);
|
|
934
|
+
await useWpDeletePost(42); // move to trash
|
|
935
|
+
await useWpDeletePost(42, true); // permanently delete
|
|
859
936
|
|
|
860
937
|
// Get ALL posts (auto-pagination — loops until exhausted)
|
|
861
938
|
const all = await useWpListAllPosts({ status: "publish" });
|
|
@@ -863,7 +940,7 @@ if (all.ok) console.log(`Total: ${all.data.length}`);
|
|
|
863
940
|
|
|
864
941
|
// Search ALL posts by keyword
|
|
865
942
|
const found = await useWpSearchAllPosts("tutorial");
|
|
866
|
-
if (found.ok) found.data.forEach(p => console.log(p.title.rendered));
|
|
943
|
+
if (found.ok) found.data.forEach((p) => console.log(p.title.rendered));
|
|
867
944
|
|
|
868
945
|
// Find post by slug (for dynamic routes like /blog/:slug)
|
|
869
946
|
const bySlug = await useWpFindPostBySlug("hello-world");
|
|
@@ -874,11 +951,11 @@ if (bySlug.ok && bySlug.data) console.log(bySlug.data.title.rendered);
|
|
|
874
951
|
|
|
875
952
|
```ts
|
|
876
953
|
import {
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
954
|
+
useWpGetPages,
|
|
955
|
+
useWpGetPage,
|
|
956
|
+
useWpCreatePage,
|
|
957
|
+
useWpUpdatePage,
|
|
958
|
+
useWpDeletePage,
|
|
882
959
|
} from "katanakit-js/adapters/wordpress";
|
|
883
960
|
|
|
884
961
|
const pages = await useWpGetPages({ per_page: 20 });
|
|
@@ -887,10 +964,10 @@ const page = await useWpGetPage(10);
|
|
|
887
964
|
if (page.ok) console.log(page.data.title.rendered);
|
|
888
965
|
|
|
889
966
|
await useWpCreatePage({
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
967
|
+
title: "About Us",
|
|
968
|
+
content: "<p>Welcome to our site!</p>",
|
|
969
|
+
status: "publish",
|
|
970
|
+
parent: 0, // top-level page (set a page ID for child pages)
|
|
894
971
|
});
|
|
895
972
|
|
|
896
973
|
await useWpUpdatePage(10, { title: "Updated About" });
|
|
@@ -901,11 +978,11 @@ await useWpDeletePage(10); // trash
|
|
|
901
978
|
|
|
902
979
|
```ts
|
|
903
980
|
import {
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
981
|
+
useWpGetMedia,
|
|
982
|
+
useWpGetMediaItem,
|
|
983
|
+
useWpUploadMedia,
|
|
984
|
+
useWpUpdateMedia,
|
|
985
|
+
useWpDeleteMedia,
|
|
909
986
|
} from "katanakit-js/adapters/wordpress";
|
|
910
987
|
|
|
911
988
|
// List media items
|
|
@@ -918,9 +995,9 @@ if (item.ok) console.log(item.data.source_url);
|
|
|
918
995
|
// Upload from browser (File from <input type="file">)
|
|
919
996
|
const file = document.querySelector("input[type=file]").files[0];
|
|
920
997
|
const uploaded = await useWpUploadMedia(file, {
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
998
|
+
title: "My Image",
|
|
999
|
+
alt_text: "Description for accessibility",
|
|
1000
|
+
caption: "Image caption",
|
|
924
1001
|
});
|
|
925
1002
|
if (uploaded.ok) console.log(uploaded.data.source_url); // URL to use in content
|
|
926
1003
|
|
|
@@ -940,10 +1017,16 @@ await useWpDeleteMedia(42, true); // permanent
|
|
|
940
1017
|
|
|
941
1018
|
```ts
|
|
942
1019
|
import {
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
1020
|
+
useWpGetCategories,
|
|
1021
|
+
useWpGetCategory,
|
|
1022
|
+
useWpCreateCategory,
|
|
1023
|
+
useWpUpdateCategory,
|
|
1024
|
+
useWpDeleteCategory,
|
|
1025
|
+
useWpGetTags,
|
|
1026
|
+
useWpGetTag,
|
|
1027
|
+
useWpCreateTag,
|
|
1028
|
+
useWpUpdateTag,
|
|
1029
|
+
useWpDeleteTag,
|
|
947
1030
|
} from "katanakit-js/adapters/wordpress";
|
|
948
1031
|
|
|
949
1032
|
// Categories
|
|
@@ -963,8 +1046,11 @@ await useWpDeleteTag(12);
|
|
|
963
1046
|
|
|
964
1047
|
```ts
|
|
965
1048
|
import {
|
|
966
|
-
|
|
967
|
-
|
|
1049
|
+
useWpGetComments,
|
|
1050
|
+
useWpGetComment,
|
|
1051
|
+
useWpCreateComment,
|
|
1052
|
+
useWpUpdateComment,
|
|
1053
|
+
useWpDeleteComment,
|
|
968
1054
|
} from "katanakit-js/adapters/wordpress";
|
|
969
1055
|
|
|
970
1056
|
// Get comments for a post
|
|
@@ -972,10 +1058,10 @@ const comments = await useWpGetComments({ post: 42, per_page: 10 });
|
|
|
972
1058
|
|
|
973
1059
|
// Create a comment (public or authenticated)
|
|
974
1060
|
await useWpCreateComment({
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1061
|
+
post: 42,
|
|
1062
|
+
content: "Great article!",
|
|
1063
|
+
author_name: "John",
|
|
1064
|
+
author_email: "john@example.com",
|
|
979
1065
|
});
|
|
980
1066
|
|
|
981
1067
|
// Update / delete
|
|
@@ -987,18 +1073,22 @@ await useWpDeleteComment(7);
|
|
|
987
1073
|
|
|
988
1074
|
```ts
|
|
989
1075
|
import {
|
|
990
|
-
|
|
991
|
-
|
|
1076
|
+
useWpGetUsers,
|
|
1077
|
+
useWpGetUser,
|
|
1078
|
+
useWpGetCurrentUser,
|
|
1079
|
+
useWpCreateUser,
|
|
1080
|
+
useWpUpdateUser,
|
|
1081
|
+
useWpDeleteUser,
|
|
992
1082
|
} from "katanakit-js/adapters/wordpress";
|
|
993
1083
|
|
|
994
1084
|
const users = await useWpGetUsers({ roles: "editor" });
|
|
995
1085
|
const me = await useWpGetCurrentUser(); // authenticated user
|
|
996
1086
|
|
|
997
1087
|
await useWpCreateUser({
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1088
|
+
username: "johndoe",
|
|
1089
|
+
email: "john@example.com",
|
|
1090
|
+
password: "secure-password",
|
|
1091
|
+
roles: ["editor"],
|
|
1002
1092
|
});
|
|
1003
1093
|
|
|
1004
1094
|
await useWpUpdateUser(2, { name: "John Smith" });
|
|
@@ -1009,8 +1099,11 @@ await useWpDeleteUser(2, 1); // reassign content to user 1
|
|
|
1009
1099
|
|
|
1010
1100
|
```ts
|
|
1011
1101
|
import {
|
|
1012
|
-
|
|
1013
|
-
|
|
1102
|
+
useWpGetCustomPosts,
|
|
1103
|
+
useWpGetCustomPost,
|
|
1104
|
+
useWpCreateCustomPost,
|
|
1105
|
+
useWpUpdateCustomPost,
|
|
1106
|
+
useWpDeleteCustomPost,
|
|
1014
1107
|
} from "katanakit-js/adapters/wordpress";
|
|
1015
1108
|
|
|
1016
1109
|
// Works with any registered CPT: "product", "portfolio", "event", etc.
|
|
@@ -1018,9 +1111,9 @@ const products = await useWpGetCustomPosts("product", { per_page: 10 });
|
|
|
1018
1111
|
const product = await useWpGetCustomPost("product", 15);
|
|
1019
1112
|
|
|
1020
1113
|
await useWpCreateCustomPost("product", {
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1114
|
+
title: "Widget",
|
|
1115
|
+
content: "<p>A great widget</p>",
|
|
1116
|
+
status: "publish",
|
|
1024
1117
|
});
|
|
1025
1118
|
|
|
1026
1119
|
await useWpUpdateCustomPost("product", 15, { title: "Updated Widget" });
|
|
@@ -1033,13 +1126,13 @@ await useWpDeleteCustomPost("product", 15, true);
|
|
|
1033
1126
|
import { useWpBatch } from "katanakit-js/adapters/wordpress";
|
|
1034
1127
|
|
|
1035
1128
|
const result = await useWpBatch([
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1129
|
+
{ method: "GET", path: "/wp/v2/posts?per_page=2" },
|
|
1130
|
+
{ method: "GET", path: "/wp/v2/pages?per_page=2" },
|
|
1131
|
+
{ method: "GET", path: "/wp/v2/categories?per_page=5" },
|
|
1039
1132
|
]);
|
|
1040
1133
|
|
|
1041
1134
|
if (result.ok) {
|
|
1042
|
-
|
|
1135
|
+
result.data.responses.forEach((resp) => console.log(resp.status));
|
|
1043
1136
|
}
|
|
1044
1137
|
```
|
|
1045
1138
|
|
|
@@ -1051,8 +1144,8 @@ significantly for list views.
|
|
|
1051
1144
|
```ts
|
|
1052
1145
|
// Only fetch id, title, link, slug, and date
|
|
1053
1146
|
const posts = await useWpGetPosts({
|
|
1054
|
-
|
|
1055
|
-
|
|
1147
|
+
per_page: 20,
|
|
1148
|
+
_fields: "id,title,link,slug,date",
|
|
1056
1149
|
});
|
|
1057
1150
|
```
|
|
1058
1151
|
|
|
@@ -1067,16 +1160,17 @@ const posts = await useWpGetPosts({ _embed: true });
|
|
|
1067
1160
|
|
|
1068
1161
|
// Embed only specific resources
|
|
1069
1162
|
const posts = await useWpGetPosts({
|
|
1070
|
-
|
|
1163
|
+
_embed: "author,wp:featuredmedia",
|
|
1071
1164
|
});
|
|
1072
1165
|
|
|
1073
1166
|
// Access embedded data
|
|
1074
1167
|
if (posts.ok) {
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1168
|
+
for (const post of posts.data) {
|
|
1169
|
+
const author = post._embedded?.author?.[0]?.name;
|
|
1170
|
+
const image = post._embedded?.["wp:featuredmedia"]?.[0]?.source_url;
|
|
1171
|
+
const thumbnail =
|
|
1172
|
+
post._embedded?.["wp:featuredmedia"]?.[0]?.media_details?.sizes?.thumbnail?.source_url;
|
|
1173
|
+
}
|
|
1080
1174
|
}
|
|
1081
1175
|
```
|
|
1082
1176
|
|
|
@@ -1089,40 +1183,74 @@ media item, or custom post type entry.
|
|
|
1089
1183
|
```ts
|
|
1090
1184
|
// Request ACF fields explicitly with _fields
|
|
1091
1185
|
const posts = await useWpGetPosts({
|
|
1092
|
-
|
|
1093
|
-
|
|
1186
|
+
per_page: 5,
|
|
1187
|
+
_fields: "id,title,acf",
|
|
1094
1188
|
});
|
|
1095
1189
|
|
|
1096
1190
|
if (posts.ok) {
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1191
|
+
for (const post of posts.data) {
|
|
1192
|
+
if (post.acf) {
|
|
1193
|
+
// ACF fields are dynamic — access by field name
|
|
1194
|
+
console.log(post.acf.my_field_name);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1103
1197
|
}
|
|
1104
1198
|
|
|
1105
1199
|
// Combine _fields + _embed + ACF for full-featured list views
|
|
1106
1200
|
const full = await useWpGetPosts({
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1201
|
+
per_page: 5,
|
|
1202
|
+
_fields: "id,title,link,slug,date,acf",
|
|
1203
|
+
_embed: "author,wp:featuredmedia",
|
|
1110
1204
|
});
|
|
1111
1205
|
```
|
|
1112
1206
|
|
|
1113
1207
|
#### Framework examples
|
|
1114
1208
|
|
|
1115
|
-
| Framework
|
|
1116
|
-
|
|
1117
|
-
| **Vue 3**
|
|
1118
|
-
| **Vue 3**
|
|
1119
|
-
| **Nuxt 3**
|
|
1120
|
-
| **Nuxt 3**
|
|
1121
|
-
| **Astro**
|
|
1122
|
-
| **Astro**
|
|
1123
|
-
| **Next.js** | [`examples/wordpress/next-blog.tsx`](https://github.com/senseikatana/katanakit
|
|
1124
|
-
| **Next.js** | [`examples/wordpress/next-[slug].tsx`](https://github.com/senseikatana/katanakit
|
|
1125
|
-
| **Node.js** | [`examples/wordpress/demo.ts`](https://github.com/senseikatana/katanakit
|
|
1209
|
+
| Framework | Example | Description |
|
|
1210
|
+
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------ |
|
|
1211
|
+
| **Vue 3** | [`examples/wordpress/vue-blog.vue`](https://github.com/senseikatana/katanakit/tree/main/examples/wordpress/vue-blog.vue) | Blog listing with categories, featured images, `_embed` |
|
|
1212
|
+
| **Vue 3** | [`examples/wordpress/vue-post.vue`](https://github.com/senseikatana/katanakit/tree/main/examples/wordpress/vue-post.vue) | Single post view with embedded author |
|
|
1213
|
+
| **Nuxt 3** | [`examples/wordpress/nuxt-blog.vue`](https://github.com/senseikatana/katanakit/tree/main/examples/wordpress/nuxt-blog.vue) | SSR blog listing with `useAsyncData` |
|
|
1214
|
+
| **Nuxt 3** | [`examples/wordpress/nuxt-post.vue`](https://github.com/senseikatana/katanakit/tree/main/examples/wordpress/nuxt-post.vue) | SSR single post view |
|
|
1215
|
+
| **Astro** | [`examples/wordpress/astro-blog.astro`](https://github.com/senseikatana/katanakit/tree/main/examples/wordpress/astro-blog.astro) | Static blog listing |
|
|
1216
|
+
| **Astro** | [`examples/wordpress/astro-[slug].astro`](https://github.com/senseikatana/katanakit/tree/main/examples/wordpress/astro-[slug].astro) | Dynamic `[slug]` route |
|
|
1217
|
+
| **Next.js** | [`examples/wordpress/next-blog.tsx`](https://github.com/senseikatana/katanakit/tree/main/examples/wordpress/next-blog.tsx) | Server component blog listing |
|
|
1218
|
+
| **Next.js** | [`examples/wordpress/next-[slug].tsx`](https://github.com/senseikatana/katanakit/tree/main/examples/wordpress/next-[slug].tsx) | Dynamic `[slug]` page |
|
|
1219
|
+
| **Node.js** | [`examples/wordpress/demo.ts`](https://github.com/senseikatana/katanakit/tree/main/examples/wordpress/demo.ts) | Runnable demo covering all WP operations, `_fields`, `_embed`, ACF |
|
|
1220
|
+
|
|
1221
|
+
## Contributing
|
|
1222
|
+
|
|
1223
|
+
We welcome contributions from the community! Whether it's fixing a bug, adding
|
|
1224
|
+
a feature, or improving documentation, every contribution helps make KatanaKit
|
|
1225
|
+
better for everyone.
|
|
1226
|
+
|
|
1227
|
+
### Quick start
|
|
1228
|
+
|
|
1229
|
+
```bash
|
|
1230
|
+
git clone https://github.com/senseikatana/katanakit.git
|
|
1231
|
+
cd katanakit-js
|
|
1232
|
+
git checkout dev
|
|
1233
|
+
bun install
|
|
1234
|
+
bun run check # verify everything works
|
|
1235
|
+
```
|
|
1236
|
+
|
|
1237
|
+
### Ways to contribute
|
|
1238
|
+
|
|
1239
|
+
- **Report bugs** — [Open an issue](https://github.com/senseikatana/katanakit/issues) with a clear description and reproduction steps
|
|
1240
|
+
- **Suggest features** — [Start a discussion](https://github.com/senseikatana/katanakit/discussions) to propose new ideas
|
|
1241
|
+
- **Submit a PR** — Fork the repo, create a branch from `dev`, make your changes, and open a PR
|
|
1242
|
+
- **Improve docs** — Fix typos, add examples, or clarify explanations
|
|
1243
|
+
- **Add adapters** — Build integrations for new APIs or frameworks
|
|
1244
|
+
|
|
1245
|
+
### PR guidelines
|
|
1246
|
+
|
|
1247
|
+
1. Branch from `dev` (not `main`)
|
|
1248
|
+
2. Follow the `use*` naming convention
|
|
1249
|
+
3. Add TypeScript types in `src/types/index.ts`
|
|
1250
|
+
4. Run `bun run check` before submitting
|
|
1251
|
+
5. Update `CHANGELOG.md` if the public API changed
|
|
1252
|
+
|
|
1253
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the full development contract.
|
|
1126
1254
|
|
|
1127
1255
|
## Documentation
|
|
1128
1256
|
|