katanakit-js 3.2.1 → 4.0.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 (181) hide show
  1. package/README.md +435 -307
  2. package/dist/adapters/angular/fetch.d.ts +37 -0
  3. package/dist/adapters/angular/fetch.d.ts.map +1 -0
  4. package/dist/adapters/angular/fetch.js +67 -0
  5. package/dist/adapters/angular/fetch.js.map +1 -0
  6. package/dist/adapters/angular/index.d.ts +4 -0
  7. package/dist/adapters/angular/index.d.ts.map +1 -0
  8. package/dist/adapters/angular/index.js +4 -0
  9. package/dist/adapters/angular/index.js.map +1 -0
  10. package/dist/adapters/angular/query.d.ts +107 -0
  11. package/dist/adapters/angular/query.d.ts.map +1 -0
  12. package/dist/adapters/angular/query.js +166 -0
  13. package/dist/adapters/angular/query.js.map +1 -0
  14. package/dist/adapters/angular/watch.d.ts +34 -0
  15. package/dist/adapters/angular/watch.d.ts.map +1 -0
  16. package/dist/adapters/angular/watch.js +38 -0
  17. package/dist/adapters/angular/watch.js.map +1 -0
  18. package/dist/adapters/assistant/assistant.service.js +1 -1
  19. package/dist/adapters/assistant/assistant.service.js.map +1 -1
  20. package/dist/adapters/astro/astro.service.d.ts +0 -1
  21. package/dist/adapters/astro/astro.service.d.ts.map +1 -1
  22. package/dist/adapters/astro/astro.service.js +0 -1
  23. package/dist/adapters/astro/astro.service.js.map +1 -1
  24. package/dist/adapters/bun/dummyjson.service.d.ts +88 -0
  25. package/dist/adapters/bun/dummyjson.service.d.ts.map +1 -0
  26. package/dist/adapters/bun/dummyjson.service.js +149 -0
  27. package/dist/adapters/bun/dummyjson.service.js.map +1 -0
  28. package/dist/adapters/bun/index.d.ts +5 -0
  29. package/dist/adapters/bun/index.d.ts.map +1 -0
  30. package/dist/adapters/bun/index.js +5 -0
  31. package/dist/adapters/bun/index.js.map +1 -0
  32. package/dist/adapters/bun/main.d.ts +2 -0
  33. package/dist/adapters/bun/main.d.ts.map +1 -0
  34. package/dist/adapters/bun/main.js +6 -0
  35. package/dist/adapters/bun/main.js.map +1 -0
  36. package/dist/adapters/bun/routes.d.ts +62 -0
  37. package/dist/adapters/bun/routes.d.ts.map +1 -0
  38. package/dist/adapters/bun/routes.js +60 -0
  39. package/dist/adapters/bun/routes.js.map +1 -0
  40. package/dist/adapters/bun/seed.d.ts +65 -0
  41. package/dist/adapters/bun/seed.d.ts.map +1 -0
  42. package/dist/adapters/bun/seed.js +117 -0
  43. package/dist/adapters/bun/seed.js.map +1 -0
  44. package/dist/adapters/bun/server.d.ts +53 -0
  45. package/dist/adapters/bun/server.d.ts.map +1 -0
  46. package/dist/adapters/bun/server.js +64 -0
  47. package/dist/adapters/bun/server.js.map +1 -0
  48. package/dist/adapters/express/app.d.ts +2 -1
  49. package/dist/adapters/express/app.d.ts.map +1 -1
  50. package/dist/adapters/express/app.js +14 -2
  51. package/dist/adapters/express/app.js.map +1 -1
  52. package/dist/adapters/express/products.controller.d.ts.map +1 -1
  53. package/dist/adapters/express/products.controller.js +3 -1
  54. package/dist/adapters/express/products.controller.js.map +1 -1
  55. package/dist/adapters/express/server.js +2 -2
  56. package/dist/adapters/express/server.js.map +1 -1
  57. package/dist/adapters/notion/notion.service.d.ts +4 -2
  58. package/dist/adapters/notion/notion.service.d.ts.map +1 -1
  59. package/dist/adapters/notion/notion.service.js +19 -5
  60. package/dist/adapters/notion/notion.service.js.map +1 -1
  61. package/dist/adapters/react/fetch.d.ts +44 -0
  62. package/dist/adapters/react/fetch.d.ts.map +1 -0
  63. package/dist/adapters/react/fetch.js +74 -0
  64. package/dist/adapters/react/fetch.js.map +1 -0
  65. package/dist/adapters/react/index.d.ts +4 -0
  66. package/dist/adapters/react/index.d.ts.map +1 -0
  67. package/dist/adapters/react/index.js +4 -0
  68. package/dist/adapters/react/index.js.map +1 -0
  69. package/dist/adapters/react/query.d.ts +104 -0
  70. package/dist/adapters/react/query.d.ts.map +1 -0
  71. package/dist/adapters/react/query.js +200 -0
  72. package/dist/adapters/react/query.js.map +1 -0
  73. package/dist/adapters/react/watch.d.ts +37 -0
  74. package/dist/adapters/react/watch.d.ts.map +1 -0
  75. package/dist/adapters/react/watch.js +39 -0
  76. package/dist/adapters/react/watch.js.map +1 -0
  77. package/dist/adapters/solid/fetch.d.ts +43 -0
  78. package/dist/adapters/solid/fetch.d.ts.map +1 -0
  79. package/dist/adapters/solid/fetch.js +72 -0
  80. package/dist/adapters/solid/fetch.js.map +1 -0
  81. package/dist/adapters/solid/index.d.ts +4 -0
  82. package/dist/adapters/solid/index.d.ts.map +1 -0
  83. package/dist/adapters/solid/index.js +4 -0
  84. package/dist/adapters/solid/index.js.map +1 -0
  85. package/dist/adapters/solid/query.d.ts +102 -0
  86. package/dist/adapters/solid/query.d.ts.map +1 -0
  87. package/dist/adapters/solid/query.js +163 -0
  88. package/dist/adapters/solid/query.js.map +1 -0
  89. package/dist/adapters/solid/watch.d.ts +32 -0
  90. package/dist/adapters/solid/watch.d.ts.map +1 -0
  91. package/dist/adapters/solid/watch.js +36 -0
  92. package/dist/adapters/solid/watch.js.map +1 -0
  93. package/dist/adapters/svelte/fetch.d.ts +40 -0
  94. package/dist/adapters/svelte/fetch.d.ts.map +1 -0
  95. package/dist/adapters/svelte/fetch.js +75 -0
  96. package/dist/adapters/svelte/fetch.js.map +1 -0
  97. package/dist/adapters/svelte/index.d.ts +4 -0
  98. package/dist/adapters/svelte/index.d.ts.map +1 -0
  99. package/dist/adapters/svelte/index.js +4 -0
  100. package/dist/adapters/svelte/index.js.map +1 -0
  101. package/dist/adapters/svelte/query.d.ts +108 -0
  102. package/dist/adapters/svelte/query.d.ts.map +1 -0
  103. package/dist/adapters/svelte/query.js +172 -0
  104. package/dist/adapters/svelte/query.js.map +1 -0
  105. package/dist/adapters/svelte/watch.d.ts +32 -0
  106. package/dist/adapters/svelte/watch.d.ts.map +1 -0
  107. package/dist/adapters/svelte/watch.js +41 -0
  108. package/dist/adapters/svelte/watch.js.map +1 -0
  109. package/dist/adapters/telegram/telegram.service.js +1 -1
  110. package/dist/adapters/telegram/telegram.service.js.map +1 -1
  111. package/dist/adapters/vue/query.d.ts +1 -2
  112. package/dist/adapters/vue/query.d.ts.map +1 -1
  113. package/dist/adapters/vue/query.js +9 -9
  114. package/dist/adapters/vue/query.js.map +1 -1
  115. package/dist/adapters/vue/vue.service.d.ts +9 -5
  116. package/dist/adapters/vue/vue.service.d.ts.map +1 -1
  117. package/dist/adapters/vue/vue.service.js +7 -3
  118. package/dist/adapters/vue/vue.service.js.map +1 -1
  119. package/dist/adapters/whatsapp/whatsapp.service.js +1 -1
  120. package/dist/adapters/whatsapp/whatsapp.service.js.map +1 -1
  121. package/dist/adapters/wordpress/wordpress.service.d.ts +2 -1
  122. package/dist/adapters/wordpress/wordpress.service.d.ts.map +1 -1
  123. package/dist/adapters/wordpress/wordpress.service.js +10 -5
  124. package/dist/adapters/wordpress/wordpress.service.js.map +1 -1
  125. package/dist/core/services/agent.service.d.ts.map +1 -1
  126. package/dist/core/services/agent.service.js +9 -1
  127. package/dist/core/services/agent.service.js.map +1 -1
  128. package/dist/core/services/dates.service.d.ts.map +1 -1
  129. package/dist/core/services/dates.service.js +2 -1
  130. package/dist/core/services/dates.service.js.map +1 -1
  131. package/dist/core/services/error.service.d.ts +1 -1
  132. package/dist/core/services/error.service.js +1 -1
  133. package/dist/core/services/http.service.d.ts +26 -5
  134. package/dist/core/services/http.service.d.ts.map +1 -1
  135. package/dist/core/services/http.service.js +30 -6
  136. package/dist/core/services/http.service.js.map +1 -1
  137. package/dist/core/services/logger.service.d.ts +10 -67
  138. package/dist/core/services/logger.service.d.ts.map +1 -1
  139. package/dist/core/services/logger.service.js +13 -139
  140. package/dist/core/services/logger.service.js.map +1 -1
  141. package/dist/core/services/query.service.d.ts +4 -0
  142. package/dist/core/services/query.service.d.ts.map +1 -1
  143. package/dist/core/services/query.service.js +75 -63
  144. package/dist/core/services/query.service.js.map +1 -1
  145. package/dist/core/services/reactive.service.d.ts.map +1 -1
  146. package/dist/core/services/reactive.service.js +7 -10
  147. package/dist/core/services/reactive.service.js.map +1 -1
  148. package/dist/core/services/timing.service.js +8 -8
  149. package/dist/core/services/timing.service.js.map +1 -1
  150. package/dist/core/services/utils.service.d.ts.map +1 -1
  151. package/dist/core/services/utils.service.js +3 -0
  152. package/dist/core/services/utils.service.js.map +1 -1
  153. package/dist/index.d.ts +0 -1
  154. package/dist/index.d.ts.map +1 -1
  155. package/dist/index.js +0 -1
  156. package/dist/index.js.map +1 -1
  157. package/dist/infrastructure/dom/dom.service.d.ts.map +1 -1
  158. package/dist/infrastructure/dom/dom.service.js +18 -4
  159. package/dist/infrastructure/dom/dom.service.js.map +1 -1
  160. package/dist/infrastructure/observer/observer.service.js +2 -2
  161. package/dist/infrastructure/observer/observer.service.js.map +1 -1
  162. package/dist/infrastructure/sensors/sensors.service.js +6 -6
  163. package/dist/infrastructure/sensors/sensors.service.js.map +1 -1
  164. package/dist/infrastructure/storage/storage.service.d.ts.map +1 -1
  165. package/dist/infrastructure/storage/storage.service.js +12 -2
  166. package/dist/infrastructure/storage/storage.service.js.map +1 -1
  167. package/dist/prisma/assistant.store.d.ts +3 -0
  168. package/dist/prisma/assistant.store.d.ts.map +1 -1
  169. package/dist/prisma/assistant.store.js +12 -6
  170. package/dist/prisma/assistant.store.js.map +1 -1
  171. package/dist/prisma/use-prisma.d.ts +5 -4
  172. package/dist/prisma/use-prisma.d.ts.map +1 -1
  173. package/dist/prisma/use-prisma.js +5 -4
  174. package/dist/prisma/use-prisma.js.map +1 -1
  175. package/dist/types/index.d.ts +2 -6
  176. package/dist/types/index.d.ts.map +1 -1
  177. package/package.json +244 -185
  178. package/dist/prisma/db.d.ts +0 -4
  179. package/dist/prisma/db.d.ts.map +0 -1
  180. package/dist/prisma/db.js +0 -12
  181. 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
- pnpm add katanakit-js
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
- import { useLogger, useGetApi, useInitApis } from "https://cdn.jsdelivr.net/npm/katanakit-js/+esm";
22
- useLogger("ready");
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 | 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) |
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
- pokeapi: {
44
- baseUri: "https://pokeapi.co/api/v2",
45
- endpoints: { pokemonById: "/pokemon/:id/" },
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
- params: { id: 25 },
51
+ params: { id: 25 },
52
52
  });
53
53
 
54
54
  if (result.ok) {
55
- console.log(result.data.name); // "pikachu"
55
+ console.log(result.data.name); // "pikachu"
56
56
  } else {
57
- console.error(result.error.message);
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
- // 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
- },
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
- params: { id: 1 },
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
- query: { _limit: 5, userId: 1 },
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
- name: "Alice",
127
- email: "alice@example.com",
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
- method: "GET",
147
- headers: {
148
- Authorization: `Bearer ${getToken()}`,
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
- // result.data is typed
162
- console.log(result.data);
161
+ // result.data is typed
162
+ console.log(result.data);
163
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
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
- const url = useBuildUrl("jsonplaceholder", "postById", {
178
- params: { id: 7 },
179
- query: { _limit: 3 },
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://jsonplaceholder.typicode.com/posts/7?_limit=3"
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-js/tree/main/examples/api-manager) for a runnable demo
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
- queryKey: ["pokemon", 25],
218
- queryFn: () => useGetApi<Pokemon>("pokeapi", "pokemonById", { params: { id: 25 } }),
219
- staleTime: 60_000, // Cache is fresh for 60s.
220
- retry: 3, // Retry 3 times on failure.
221
- refetchOnWindowFocus: true,
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
- { role: "user", content: "Summarize the benefits of solar energy in three bullet points." },
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
- tools: [
307
- {
308
- name: "readFile",
309
- description: "Returns file contents",
310
- parameters: { type: "object", properties: { path: { type: "string" } } },
311
- execute: ({ path }) => fs.readFile(path, "utf8"),
312
- },
313
- ],
314
- maxSteps: 12,
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
- // apiKey — process.env.DASHSCOPE_API_KEY
335
- // baseUrl — KITT_BASE_URL
336
- // model — "qwen3.8-max"
337
- // systemPrompt — KITT_SYSTEM_PROMPT
338
- // store — in-memory (useCreateMemoryStore)
339
- // tools — AiTool[]
340
- // maxSteps — number
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
- console.log(result.data.reply, result.data.sessionId);
421
+ console.log(result.data.reply, result.data.sessionId);
346
422
  } else {
347
- console.error(result.error.message);
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 | Signature |
357
- |--------|-----------|
358
- | `useCreateSession` | `(channel?) => Promise<string>` |
359
- | `useGetHistory` | `(sessionId) => Promise<AiMessage[]>` |
360
- | `useResetSession` | `(sessionId) => Promise<void>` |
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-js/blob/main/.env.example):
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 | Starts |
381
- |--------|--------|
382
- | `pnpm assistant:dev` | REST assistant. Uses Prisma when `DATABASE_URL` is set. |
383
- | `pnpm telegram:dev` | Telegram long polling. Requires `TELEGRAM_BOT_TOKEN`. |
384
- | `pnpm whatsapp:dev` | WhatsApp webhook. Requires `WHATSAPP_*`. |
385
- | `pnpm assistant:demo` | Demo in `examples/assistant/`. Set `KITT_CHANNEL=rest\|telegram\|whatsapp`. |
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 | Path | Body | Response |
397
- |--------|------|------|----------|
398
- | `POST` | `/assistant/chat` | `{ sessionId?, message }` | `{ ok, data: { reply, sessionId }, error }` |
399
- | `GET` | `/assistant/sessions/:id` | — | `{ sessionId, messages }` or `404` |
400
- | `DELETE` | `/assistant/sessions/:id` | — | `204` or `404` |
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
- guard: (req, res, next) =>
408
- req.header("authorization") === `Bearer ${process.env.ASSISTANT_API_KEY}`
409
- ? next()
410
- : res.status(401).end(),
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("/assistant", useCreateAssistantRouter({
426
- rateLimit: rateLimit({ windowMs: 60_000, max: 30 }),
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 `pnpm telegram:dev` (long polling, no public URL).
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
- token: process.env.WHATSAPP_TOKEN,
457
- phoneNumberId: process.env.WHATSAPP_PHONE_NUMBER_ID,
458
- verifyToken: process.env.WHATSAPP_VERIFY_TOKEN,
459
- appSecret: process.env.WHATSAPP_APP_SECRET,
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 `pnpm whatsapp:dev`.
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-js/tree/main/examples/assistant) is a generic digital assistant with two demo tools:
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
- pnpm assistant:demo
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 { useKatanaFetch } from "katanakit-js/adapters/vue"; // Vue only
544
- import { useUnwrap } from "katanakit-js/adapters/nuxt"; // Nuxt only
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
- import { useLogger } from "https://cdn.jsdelivr.net/npm/katanakit-js/+esm";
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 | Import | Description |
559
- |---------|--------|-------------|
560
- | **Express** | `katanakit-js/adapters/express` | Reference server with CORS and hardened headers |
561
- | **Nuxt** | `katanakit-js/adapters/nuxt` | `useUnwrap`, `useSafeResponse`, `useEventResponse` |
562
- | **Vue** | `katanakit-js/adapters/vue` | `useKatanaFetch` composable with reactivity |
563
- | **Astro** | `katanakit-js` or `katanakit-js/adapters/astro` | `AstroService`, `RssService` |
564
- | **Assistant** | `katanakit-js/adapters/assistant` | REST digital assistant (`useStartAssistant`) |
565
- | **Telegram** | `katanakit-js/adapters/telegram` | BotFather bot (`useInitTelegram`, `useStartTelegramPolling`) |
566
- | **WhatsApp** | `katanakit-js/adapters/whatsapp` | Meta Cloud API (`useInitWhatsApp`, `useStartWhatsApp`) |
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 | Import | Description |
574
- |---------|--------|-------------|
575
- | **Notion** | `katanakit-js/adapters/notion` | Pages, databases, blocks, search with cursor pagination |
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
- useNotionGetPage,
597
- useNotionCreatePage,
598
- useNotionUpdatePage,
599
- useNotionArchivePage,
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
- { type: "database_id", database_id: "db-id" },
609
- {
610
- Name: { title: [{ type: "text", text: { content: "My Task" } }] },
611
- Status: { select: { name: "To Do" } },
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
- { type: "page_id", page_id: "parent-id" },
618
- { title: { title: [{ type: "text", text: { content: "Child Page" } }] } },
619
- [{ type: "paragraph", paragraph: { rich_text: [{ type: "text", text: { content: "Hello!" } }] } }],
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
- Status: { select: { name: "Done" } },
625
- DueDate: { date: { start: "2025-12-31" } },
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
- useNotionGetDatabase,
637
- useNotionQueryDatabase,
638
- useNotionCreateDatabase,
639
- useNotionUpdateDatabase,
640
- useNotionListAllDatabasePages,
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
- Object.entries(schema.data.properties).forEach(([name, prop]) => {
647
- console.log(`${name}: ${prop.type}`);
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
- filter: { property: "Status", select: { equals: "Published" } },
654
- sorts: [{ property: "Date", direction: "descending" }],
655
- page_size: 10,
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
- "db-id",
664
- { property: "Status", select: { equals: "Published" } },
665
- [{ property: "Date", direction: "descending" }],
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
- { type: "page_id", page_id: "parent-id" },
671
- [{ type: "text", text: { content: "My Tasks" } }],
672
- {
673
- Name: { title: {} },
674
- Status: { select: { options: [{ name: "To Do" }, { name: "Done" }] } },
675
- Priority: { select: { options: [{ name: "Low" }, { name: "High" }] } },
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
- useNotionGetBlock,
690
- useNotionGetBlockChildren,
691
- useNotionListAllBlockChildren,
692
- useNotionAppendBlocks,
693
- useNotionUpdateBlock,
694
- useNotionDeleteBlock,
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
- blocks.data.forEach(b => console.log(b.type)); // "paragraph", "heading_1", etc.
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
- console.log(page.data.results); // blocks
707
- console.log(page.data.has_more); // true if more pages exist
708
- console.log(page.data.next_cursor); // pass as start_cursor for next page
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
- type: "heading_2",
715
- heading_2: { rich_text: [{ type: "text", text: { content: "New Section" } }] },
716
- },
717
- {
718
- type: "paragraph",
719
- paragraph: { rich_text: [{ type: "text", text: { content: "Body text here." } }] },
720
- },
721
- {
722
- type: "to_do",
723
- to_do: {
724
- rich_text: [{ type: "text", text: { content: "Checklist item" } }],
725
- checked: false,
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
- paragraph: { rich_text: [{ type: "text", text: { content: "Updated text" } }] },
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
- query: "tasks",
750
- filter: { value: "database", property: "object" },
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
- filter: { value: "page", property: "object" },
756
- sort: { direction: "descending", timestamp: "last_edited_time" },
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 | Example | Description |
777
- |-----------|---------|-------------|
778
- | **Vue 3** | [`examples/notion/vue-blog.vue`](https://github.com/senseikatana/katanakit-js/tree/main/examples/notion/vue-blog.vue) | Blog listing with `useQuery` composable |
779
- | **Vue 3** | [`examples/notion/vue-post.vue`](https://github.com/senseikatana/katanakit-js/tree/main/examples/notion/vue-post.vue) | Single post view |
780
- | **Nuxt 3** | [`examples/notion/nuxt-blog.vue`](https://github.com/senseikatana/katanakit-js/tree/main/examples/notion/nuxt-blog.vue) | SSR blog listing with `useAsyncData` |
781
- | **Nuxt 3** | [`examples/notion/nuxt-post.vue`](https://github.com/senseikatana/katanakit-js/tree/main/examples/notion/nuxt-post.vue) | SSR single post view |
782
- | **Astro** | [`examples/notion/astro-blog.astro`](https://github.com/senseikatana/katanakit-js/tree/main/examples/notion/astro-blog.astro) | Static blog listing |
783
- | **Astro** | [`examples/notion/astro-[slug].astro`](https://github.com/senseikatana/katanakit-js/tree/main/examples/notion/astro-[slug].astro) | Dynamic `[slug]` route |
784
- | **Next.js** | [`examples/notion/next-blog.tsx`](https://github.com/senseikatana/katanakit-js/tree/main/examples/notion/next-blog.tsx) | Server component blog listing |
785
- | **Next.js** | [`examples/notion/next-[slug].tsx`](https://github.com/senseikatana/katanakit-js/tree/main/examples/notion/next-[slug].tsx) | Dynamic `[slug]` page |
786
- | **Node.js** | [`examples/notion/demo.ts`](https://github.com/senseikatana/katanakit-js/tree/main/examples/notion/demo.ts) | Runnable demo covering all Notion operations |
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
- baseUrl: "https://mysite.com",
802
- auth: { type: "application-passwords", username: "admin", password: "xxxx xxxx xxxx" },
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
- baseUrl: "https://mysite.com",
808
- auth: { type: "jwt", token: "eyJhbGci..." },
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
- baseUrl: "https://mysite.com",
814
- auth: { type: "nonce", nonce: "abc123" },
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
- useWpGetPosts,
823
- useWpGetPost,
824
- useWpCreatePost,
825
- useWpUpdatePost,
826
- useWpDeletePost,
827
- useWpListAllPosts,
828
- useWpSearchAllPosts,
829
- useWpFindPostBySlug,
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
- per_page: 5,
835
- status: "publish",
836
- orderby: "date",
837
- order: "desc",
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
- title: "My New Post",
847
- content: "<p>Hello World!</p>",
848
- status: "publish", // "draft" | "pending" | "publish"
849
- categories: [1, 3],
850
- tags: [5, 8],
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); // move to trash
858
- await useWpDeletePost(42, true); // permanently delete
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
- useWpGetPages,
878
- useWpGetPage,
879
- useWpCreatePage,
880
- useWpUpdatePage,
881
- useWpDeletePage,
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
- title: "About Us",
891
- content: "<p>Welcome to our site!</p>",
892
- status: "publish",
893
- parent: 0, // top-level page (set a page ID for child pages)
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
- useWpGetMedia,
905
- useWpGetMediaItem,
906
- useWpUploadMedia,
907
- useWpUpdateMedia,
908
- useWpDeleteMedia,
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
- title: "My Image",
922
- alt_text: "Description for accessibility",
923
- caption: "Image caption",
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
- useWpGetCategories, useWpGetCategory, useWpCreateCategory,
944
- useWpUpdateCategory, useWpDeleteCategory,
945
- useWpGetTags, useWpGetTag, useWpCreateTag,
946
- useWpUpdateTag, useWpDeleteTag,
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
- useWpGetComments, useWpGetComment, useWpCreateComment,
967
- useWpUpdateComment, useWpDeleteComment,
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
- post: 42,
976
- content: "Great article!",
977
- author_name: "John",
978
- author_email: "john@example.com",
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
- useWpGetUsers, useWpGetUser, useWpGetCurrentUser,
991
- useWpCreateUser, useWpUpdateUser, useWpDeleteUser,
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
- username: "johndoe",
999
- email: "john@example.com",
1000
- password: "secure-password",
1001
- roles: ["editor"],
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
- useWpGetCustomPosts, useWpGetCustomPost,
1013
- useWpCreateCustomPost, useWpUpdateCustomPost, useWpDeleteCustomPost,
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
- title: "Widget",
1022
- content: "<p>A great widget</p>",
1023
- status: "publish",
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
- { method: "GET", path: "/wp/v2/posts?per_page=2" },
1037
- { method: "GET", path: "/wp/v2/pages?per_page=2" },
1038
- { method: "GET", path: "/wp/v2/categories?per_page=5" },
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
- result.data.responses.forEach(resp => console.log(resp.status));
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
- per_page: 20,
1055
- _fields: "id,title,link,slug,date",
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
- _embed: "author,wp:featuredmedia",
1163
+ _embed: "author,wp:featuredmedia",
1071
1164
  });
1072
1165
 
1073
1166
  // Access embedded data
1074
1167
  if (posts.ok) {
1075
- for (const post of posts.data) {
1076
- const author = post._embedded?.author?.[0]?.name;
1077
- const image = post._embedded?.["wp:featuredmedia"]?.[0]?.source_url;
1078
- const thumbnail = post._embedded?.["wp:featuredmedia"]?.[0]?.media_details?.sizes?.thumbnail?.source_url;
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
- per_page: 5,
1093
- _fields: "id,title,acf",
1186
+ per_page: 5,
1187
+ _fields: "id,title,acf",
1094
1188
  });
1095
1189
 
1096
1190
  if (posts.ok) {
1097
- for (const post of posts.data) {
1098
- if (post.acf) {
1099
- // ACF fields are dynamic — access by field name
1100
- console.log(post.acf.my_field_name);
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
- per_page: 5,
1108
- _fields: "id,title,link,slug,date,acf",
1109
- _embed: "author,wp:featuredmedia",
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 | Example | Description |
1116
- |-----------|---------|-------------|
1117
- | **Vue 3** | [`examples/wordpress/vue-blog.vue`](https://github.com/senseikatana/katanakit-js/tree/main/examples/wordpress/vue-blog.vue) | Blog listing with categories, featured images, `_embed` |
1118
- | **Vue 3** | [`examples/wordpress/vue-post.vue`](https://github.com/senseikatana/katanakit-js/tree/main/examples/wordpress/vue-post.vue) | Single post view with embedded author |
1119
- | **Nuxt 3** | [`examples/wordpress/nuxt-blog.vue`](https://github.com/senseikatana/katanakit-js/tree/main/examples/wordpress/nuxt-blog.vue) | SSR blog listing with `useAsyncData` |
1120
- | **Nuxt 3** | [`examples/wordpress/nuxt-post.vue`](https://github.com/senseikatana/katanakit-js/tree/main/examples/wordpress/nuxt-post.vue) | SSR single post view |
1121
- | **Astro** | [`examples/wordpress/astro-blog.astro`](https://github.com/senseikatana/katanakit-js/tree/main/examples/wordpress/astro-blog.astro) | Static blog listing |
1122
- | **Astro** | [`examples/wordpress/astro-[slug].astro`](https://github.com/senseikatana/katanakit-js/tree/main/examples/wordpress/astro-[slug].astro) | Dynamic `[slug]` route |
1123
- | **Next.js** | [`examples/wordpress/next-blog.tsx`](https://github.com/senseikatana/katanakit-js/tree/main/examples/wordpress/next-blog.tsx) | Server component blog listing |
1124
- | **Next.js** | [`examples/wordpress/next-[slug].tsx`](https://github.com/senseikatana/katanakit-js/tree/main/examples/wordpress/next-[slug].tsx) | Dynamic `[slug]` page |
1125
- | **Node.js** | [`examples/wordpress/demo.ts`](https://github.com/senseikatana/katanakit-js/tree/main/examples/wordpress/demo.ts) | Runnable demo covering all WP operations, `_fields`, `_embed`, ACF |
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