katanakit-js 4.0.0 → 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.
Files changed (76) hide show
  1. package/README.md +326 -305
  2. package/dist/adapters/bun/dummyjson.service.d.ts +88 -0
  3. package/dist/adapters/bun/dummyjson.service.d.ts.map +1 -0
  4. package/dist/adapters/bun/dummyjson.service.js +149 -0
  5. package/dist/adapters/bun/dummyjson.service.js.map +1 -0
  6. package/dist/adapters/bun/index.d.ts +5 -0
  7. package/dist/adapters/bun/index.d.ts.map +1 -0
  8. package/dist/adapters/bun/index.js +5 -0
  9. package/dist/adapters/bun/index.js.map +1 -0
  10. package/dist/adapters/bun/main.d.ts +2 -0
  11. package/dist/adapters/bun/main.d.ts.map +1 -0
  12. package/dist/adapters/bun/main.js +6 -0
  13. package/dist/adapters/bun/main.js.map +1 -0
  14. package/dist/adapters/bun/routes.d.ts +62 -0
  15. package/dist/adapters/bun/routes.d.ts.map +1 -0
  16. package/dist/adapters/bun/routes.js +60 -0
  17. package/dist/adapters/bun/routes.js.map +1 -0
  18. package/dist/adapters/bun/seed.d.ts +65 -0
  19. package/dist/adapters/bun/seed.d.ts.map +1 -0
  20. package/dist/adapters/bun/seed.js +117 -0
  21. package/dist/adapters/bun/seed.js.map +1 -0
  22. package/dist/adapters/bun/server.d.ts +53 -0
  23. package/dist/adapters/bun/server.d.ts.map +1 -0
  24. package/dist/adapters/bun/server.js +64 -0
  25. package/dist/adapters/bun/server.js.map +1 -0
  26. package/dist/adapters/express/app.d.ts +2 -1
  27. package/dist/adapters/express/app.d.ts.map +1 -1
  28. package/dist/adapters/express/app.js +14 -2
  29. package/dist/adapters/express/app.js.map +1 -1
  30. package/dist/adapters/express/server.js +2 -2
  31. package/dist/adapters/express/server.js.map +1 -1
  32. package/dist/adapters/notion/notion.service.d.ts +2 -2
  33. package/dist/adapters/notion/notion.service.d.ts.map +1 -1
  34. package/dist/adapters/notion/notion.service.js +6 -6
  35. package/dist/adapters/notion/notion.service.js.map +1 -1
  36. package/dist/adapters/telegram/telegram.service.js +1 -1
  37. package/dist/adapters/telegram/telegram.service.js.map +1 -1
  38. package/dist/adapters/vue/query.d.ts +1 -2
  39. package/dist/adapters/vue/query.d.ts.map +1 -1
  40. package/dist/adapters/vue/query.js +1 -3
  41. package/dist/adapters/vue/query.js.map +1 -1
  42. package/dist/adapters/whatsapp/whatsapp.service.js +1 -1
  43. package/dist/adapters/whatsapp/whatsapp.service.js.map +1 -1
  44. package/dist/adapters/wordpress/wordpress.service.d.ts +1 -1
  45. package/dist/adapters/wordpress/wordpress.service.d.ts.map +1 -1
  46. package/dist/adapters/wordpress/wordpress.service.js +3 -3
  47. package/dist/adapters/wordpress/wordpress.service.js.map +1 -1
  48. package/dist/core/services/dates.service.js +1 -1
  49. package/dist/core/services/dates.service.js.map +1 -1
  50. package/dist/core/services/error.service.d.ts +1 -1
  51. package/dist/core/services/error.service.js +1 -1
  52. package/dist/core/services/logger.service.d.ts +10 -67
  53. package/dist/core/services/logger.service.d.ts.map +1 -1
  54. package/dist/core/services/logger.service.js +13 -139
  55. package/dist/core/services/logger.service.js.map +1 -1
  56. package/dist/core/services/query.service.d.ts.map +1 -1
  57. package/dist/core/services/query.service.js +9 -8
  58. package/dist/core/services/query.service.js.map +1 -1
  59. package/dist/core/services/reactive.service.js +7 -7
  60. package/dist/core/services/reactive.service.js.map +1 -1
  61. package/dist/core/services/timing.service.js +8 -8
  62. package/dist/core/services/timing.service.js.map +1 -1
  63. package/dist/index.d.ts +0 -1
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +0 -1
  66. package/dist/index.js.map +1 -1
  67. package/dist/infrastructure/dom/dom.service.d.ts.map +1 -1
  68. package/dist/infrastructure/dom/dom.service.js +7 -5
  69. package/dist/infrastructure/dom/dom.service.js.map +1 -1
  70. package/dist/infrastructure/observer/observer.service.js +2 -2
  71. package/dist/infrastructure/observer/observer.service.js.map +1 -1
  72. package/dist/infrastructure/sensors/sensors.service.js +6 -6
  73. package/dist/infrastructure/sensors/sensors.service.js.map +1 -1
  74. package/dist/types/index.d.ts +2 -5
  75. package/dist/types/index.d.ts.map +1 -1
  76. package/package.json +17 -22
package/README.md CHANGED
@@ -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,14 +158,14 @@ 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
 
@@ -208,7 +208,7 @@ const avatarUrl = useBuildUrl("myApi", "userAvatar", {
208
208
  ```ts
209
209
  // Charts, maps, analytics — libraries that fetch their own data
210
210
  const chartDataUrl = useBuildUrl("myApi", "analytics", {
211
- query: { range: "7d", metric: "visits" },
211
+ query: { range: "7d", metric: "visits" },
212
212
  });
213
213
  new Chart(canvas, { data: chartDataUrl });
214
214
  ```
@@ -217,9 +217,12 @@ new Chart(canvas, { data: chartDataUrl });
217
217
 
218
218
  ```ts
219
219
  // See the full URL before making the request
220
- console.log("Will fetch:", useBuildUrl("myApi", "users", {
221
- query: { page: 1, per_page: 20 },
222
- }));
220
+ console.log(
221
+ "Will fetch:",
222
+ useBuildUrl("myApi", "users", {
223
+ query: { page: 1, per_page: 20 },
224
+ }),
225
+ );
223
226
  // → "https://api.myapp.com/v1/users?page=1&per_page=20"
224
227
  ```
225
228
 
@@ -228,7 +231,7 @@ console.log("Will fetch:", useBuildUrl("myApi", "users", {
228
231
  ```ts
229
232
  // In Astro, Next.js, or Nuxt server code
230
233
  const apiUrl = useBuildUrl("notion", "database", {
231
- params: { id: "db-123" },
234
+ params: { id: "db-123" },
232
235
  });
233
236
  // Pass to client component or pre-render
234
237
  ```
@@ -266,11 +269,11 @@ const qc = useQueryClient();
266
269
 
267
270
  // Fetch with cache, stale-while-revalidate, retry, and dedup.
268
271
  const pokemon = await qc.fetchQuery<Pokemon>({
269
- queryKey: ["pokemon", 25],
270
- queryFn: () => useGetApi<Pokemon>("pokeapi", "pokemonById", { params: { id: 25 } }),
271
- staleTime: 60_000, // Cache is fresh for 60s.
272
- retry: 3, // Retry 3 times on failure.
273
- 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,
274
277
  });
275
278
  ```
276
279
 
@@ -349,12 +352,12 @@ Use it when you need:
349
352
 
350
353
  ### When to use alternatives
351
354
 
352
- | Need | Use instead |
353
- |------|------------|
354
- | Streaming token-by-token | [Vercel AI SDK](https://sdk.vercel.ai) |
355
- | RAG with embeddings | [LangChain.js](https://js.langchain.com) |
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) |
356
359
  | Multi-agent orchestration | [CrewAI](https://github.com/crewAIInc/crewAI) |
357
- | Full chatbot framework | [Botpress](https://botpress.com) |
360
+ | Full chatbot framework | [Botpress](https://botpress.com) |
358
361
 
359
362
  Kitt stays small (0 dependencies) because it does one thing well:
360
363
  **chat completions with tool calls, using any OpenAI-compatible endpoint.**
@@ -370,21 +373,21 @@ useInitAgent({ model: "qwen3.8-max" });
370
373
 
371
374
  // Assistant — single-shot review / question.
372
375
  const review = await useChat([
373
- { 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." },
374
377
  ]);
375
378
  if (review.ok) console.log(review.data);
376
379
 
377
380
  // Agent — autonomous tool-calling loop that can act (read/write/run).
378
381
  const result = await useRunAgent("Fix the type errors in src/", {
379
- tools: [
380
- {
381
- name: "readFile",
382
- description: "Returns file contents",
383
- parameters: { type: "object", properties: { path: { type: "string" } } },
384
- execute: ({ path }) => fs.readFile(path, "utf8"),
385
- },
386
- ],
387
- 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,
388
391
  });
389
392
  ```
390
393
 
@@ -404,20 +407,20 @@ import "dotenv/config";
404
407
  import { useInitAssistant, useReply } from "katanakit-js";
405
408
 
406
409
  useInitAssistant({
407
- // apiKey — process.env.DASHSCOPE_API_KEY
408
- // baseUrl — KITT_BASE_URL
409
- // model — "qwen3.8-max"
410
- // systemPrompt — KITT_SYSTEM_PROMPT
411
- // store — in-memory (useCreateMemoryStore)
412
- // tools — AiTool[]
413
- // 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
414
417
  });
415
418
 
416
419
  const result = await useReply(undefined, "What are your hours?");
417
420
  if (result.ok) {
418
- console.log(result.data.reply, result.data.sessionId);
421
+ console.log(result.data.reply, result.data.sessionId);
419
422
  } else {
420
- console.error(result.error.message);
423
+ console.error(result.error.message);
421
424
  }
422
425
  ```
423
426
 
@@ -426,12 +429,12 @@ if (result.ok) {
426
429
 
427
430
  Also on the main barrel:
428
431
 
429
- | Helper | Signature |
430
- |--------|-----------|
431
- | `useCreateSession` | `(channel?) => Promise<string>` |
432
- | `useGetHistory` | `(sessionId) => Promise<AiMessage[]>` |
433
- | `useResetSession` | `(sessionId) => Promise<void>` |
434
- | `useCreateMemoryStore` | `() => ConversationStore` |
432
+ | Helper | Signature |
433
+ | ---------------------- | ------------------------------------- |
434
+ | `useCreateSession` | `(channel?) => Promise<string>` |
435
+ | `useGetHistory` | `(sessionId) => Promise<AiMessage[]>` |
436
+ | `useResetSession` | `(sessionId) => Promise<void>` |
437
+ | `useCreateMemoryStore` | `() => ConversationStore` |
435
438
 
436
439
  Then start a channel (see below). Copy keys from [`.env.example`](https://github.com/senseikatana/katanakit/blob/main/.env.example):
437
440
 
@@ -450,11 +453,11 @@ DATABASE_URL=
450
453
 
451
454
  ### Run standalone (this repo)
452
455
 
453
- | Script | Starts |
454
- |--------|--------|
455
- | `bun run assistant:dev` | REST assistant. Uses Prisma when `DATABASE_URL` is set. |
456
- | `bun run telegram:dev` | Telegram long polling. Requires `TELEGRAM_BOT_TOKEN`. |
457
- | `bun run whatsapp:dev` | WhatsApp webhook. Requires `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_*`. |
458
461
  | `bun run assistant:demo` | Demo in `examples/assistant/`. Set `KITT_CHANNEL=rest\|telegram\|whatsapp`. |
459
462
 
460
463
  ### REST
@@ -466,21 +469,21 @@ useStartAssistant(); // port?, host?, mountPath = "/assistant", options?
466
469
  // or mount useCreateAssistantRouter() on an existing Express app
467
470
  ```
468
471
 
469
- | Method | Path | Body | Response |
470
- |--------|------|------|----------|
471
- | `POST` | `/assistant/chat` | `{ sessionId?, message }` | `{ ok, data: { reply, sessionId }, error }` |
472
- | `GET` | `/assistant/sessions/:id` | — | `{ sessionId, messages }` or `404` |
473
- | `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` |
474
477
 
475
478
  The endpoints are **public by default**. In production pass an auth guard (applied to every
476
479
  route). Unknown session ids return `404`, and `useReply` rejects a `sessionId` that does not exist.
477
480
 
478
481
  ```ts
479
482
  useStartAssistant(3000, "localhost", "/assistant", {
480
- guard: (req, res, next) =>
481
- req.header("authorization") === `Bearer ${process.env.ASSISTANT_API_KEY}`
482
- ? next()
483
- : 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(),
484
487
  });
485
488
  ```
486
489
 
@@ -495,9 +498,12 @@ curl -X POST http://localhost:3000/assistant/chat \
495
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:
496
499
 
497
500
  ```ts
498
- app.use("/assistant", useCreateAssistantRouter({
499
- rateLimit: rateLimit({ windowMs: 60_000, max: 30 }),
500
- }));
501
+ app.use(
502
+ "/assistant",
503
+ useCreateAssistantRouter({
504
+ rateLimit: rateLimit({ windowMs: 60_000, max: 30 }),
505
+ }),
506
+ );
501
507
  ```
502
508
 
503
509
  ### Telegram (BotFather)
@@ -526,10 +532,10 @@ Webhook: `GET` / `POST` `/whatsapp/webhook`. Session ids are `wa:<phone>`.
526
532
  import { useInitWhatsApp, useStartWhatsApp } from "katanakit-js/adapters/whatsapp";
527
533
 
528
534
  useInitWhatsApp({
529
- token: process.env.WHATSAPP_TOKEN,
530
- phoneNumberId: process.env.WHATSAPP_PHONE_NUMBER_ID,
531
- verifyToken: process.env.WHATSAPP_VERIFY_TOKEN,
532
- 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,
533
539
  });
534
540
  useStartWhatsApp();
535
541
  ```
@@ -613,14 +619,14 @@ const result = await useGetApi("pokeapi", "pokemonById", { params: { id: 25 } })
613
619
 
614
620
  ```ts
615
621
  import { useLogger, useInitApis, useGetApi } from "katanakit-js";
616
- import { useRequest } from "katanakit-js/adapters/vue"; // Vue only
617
- 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
618
624
  ```
619
625
 
620
626
  ```html
621
627
  <!-- vanilla -->
622
628
  <script type="module">
623
- import { useLogger } from "https://cdn.jsdelivr.net/npm/katanakit-js/+esm";
629
+ import { useLogger } from "https://cdn.jsdelivr.net/npm/katanakit-js/+esm";
624
630
  </script>
625
631
  ```
626
632
 
@@ -628,24 +634,24 @@ See [Getting Started](https://senseikatana.com/katanakit-js/docs/guides/getting-
628
634
 
629
635
  ## Framework Adapters
630
636
 
631
- | Adapter | Import | Description |
632
- |---------|--------|-------------|
633
- | **Express** | `katanakit-js/adapters/express` | Reference server with CORS and hardened headers |
634
- | **Nuxt** | `katanakit-js/adapters/nuxt` | `useUnwrap`, `useSafeResponse`, `useEventResponse` |
635
- | **Vue** | `katanakit-js/adapters/vue` | `useRequest` composable with reactivity |
636
- | **Astro** | `katanakit-js` or `katanakit-js/adapters/astro` | `AstroService`, `RssService` |
637
- | **Assistant** | `katanakit-js/adapters/assistant` | REST digital assistant (`useStartAssistant`) |
638
- | **Telegram** | `katanakit-js/adapters/telegram` | BotFather bot (`useInitTelegram`, `useStartTelegramPolling`) |
639
- | **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`) |
640
646
 
641
647
  ## REST API Adapters
642
648
 
643
649
  Typed adapters for popular REST APIs with auth, pagination helpers, and full TypeScript types.
644
650
  All functions return `FetchResult<T>` — the same Safe Result pattern used by the HTTP client.
645
651
 
646
- | Adapter | Import | Description |
647
- |---------|--------|-------------|
648
- | **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 |
649
655
  | **WordPress** | `katanakit-js/adapters/wordpress` | Posts, pages, media, categories, tags, comments, users, batch ops |
650
656
 
651
657
  ### Notion
@@ -666,10 +672,10 @@ useInitNotion({ token: process.env.NOTION_TOKEN });
666
672
 
667
673
  ```ts
668
674
  import {
669
- useNotionGetPage,
670
- useNotionCreatePage,
671
- useNotionUpdatePage,
672
- useNotionArchivePage,
675
+ useNotionGetPage,
676
+ useNotionCreatePage,
677
+ useNotionUpdatePage,
678
+ useNotionArchivePage,
673
679
  } from "katanakit-js/adapters/notion";
674
680
 
675
681
  // Get a single page with all its properties
@@ -678,24 +684,24 @@ if (page.ok) console.log(page.data.properties);
678
684
 
679
685
  // Create a page inside a database
680
686
  const created = await useNotionCreatePage(
681
- { type: "database_id", database_id: "db-id" },
682
- {
683
- Name: { title: [{ type: "text", text: { content: "My Task" } }] },
684
- Status: { select: { name: "To Do" } },
685
- },
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
+ },
686
692
  );
687
693
 
688
694
  // Create a child page with content blocks
689
695
  const child = await useNotionCreatePage(
690
- { type: "page_id", page_id: "parent-id" },
691
- { title: { title: [{ type: "text", text: { content: "Child Page" } }] } },
692
- [{ 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!" } }] } }],
693
699
  );
694
700
 
695
701
  // Update page properties (only changed fields)
696
702
  await useNotionUpdatePage("page-id", {
697
- Status: { select: { name: "Done" } },
698
- DueDate: { date: { start: "2025-12-31" } },
703
+ Status: { select: { name: "Done" } },
704
+ DueDate: { date: { start: "2025-12-31" } },
699
705
  });
700
706
 
701
707
  // Archive (soft-delete) a page
@@ -706,26 +712,26 @@ await useNotionArchivePage("page-id");
706
712
 
707
713
  ```ts
708
714
  import {
709
- useNotionGetDatabase,
710
- useNotionQueryDatabase,
711
- useNotionCreateDatabase,
712
- useNotionUpdateDatabase,
713
- useNotionListAllDatabasePages,
715
+ useNotionGetDatabase,
716
+ useNotionQueryDatabase,
717
+ useNotionCreateDatabase,
718
+ useNotionUpdateDatabase,
719
+ useNotionListAllDatabasePages,
714
720
  } from "katanakit-js/adapters/notion";
715
721
 
716
722
  // Inspect database schema (property names, types, options)
717
723
  const schema = await useNotionGetDatabase("db-id");
718
724
  if (schema.ok) {
719
- Object.entries(schema.data.properties).forEach(([name, prop]) => {
720
- console.log(`${name}: ${prop.type}`);
721
- });
725
+ Object.entries(schema.data.properties).forEach(([name, prop]) => {
726
+ console.log(`${name}: ${prop.type}`);
727
+ });
722
728
  }
723
729
 
724
730
  // Query with filter + sort (single page of results)
725
731
  const page1 = await useNotionQueryDatabase("db-id", {
726
- filter: { property: "Status", select: { equals: "Published" } },
727
- sorts: [{ property: "Date", direction: "descending" }],
728
- page_size: 10,
732
+ filter: { property: "Status", select: { equals: "Published" } },
733
+ sorts: [{ property: "Date", direction: "descending" }],
734
+ page_size: 10,
729
735
  });
730
736
 
731
737
  // Get ALL pages (auto-pagination — handles cursors internally)
@@ -733,76 +739,74 @@ const all = await useNotionListAllDatabasePages("db-id");
733
739
 
734
740
  // With filter + sort
735
741
  const published = await useNotionListAllDatabasePages(
736
- "db-id",
737
- { property: "Status", select: { equals: "Published" } },
738
- [{ property: "Date", direction: "descending" }],
742
+ "db-id",
743
+ { property: "Status", select: { equals: "Published" } },
744
+ [{ property: "Date", direction: "descending" }],
739
745
  );
740
746
 
741
747
  // Create a new database
742
748
  await useNotionCreateDatabase(
743
- { type: "page_id", page_id: "parent-id" },
744
- [{ type: "text", text: { content: "My Tasks" } }],
745
- {
746
- Name: { title: {} },
747
- Status: { select: { options: [{ name: "To Do" }, { name: "Done" }] } },
748
- Priority: { select: { options: [{ name: "Low" }, { name: "High" }] } },
749
- },
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
+ },
750
756
  );
751
757
 
752
758
  // Rename a database
753
- await useNotionUpdateDatabase("db-id", [
754
- { type: "text", text: { content: "Renamed Database" } },
755
- ]);
759
+ await useNotionUpdateDatabase("db-id", [{ type: "text", text: { content: "Renamed Database" } }]);
756
760
  ```
757
761
 
758
762
  #### Blocks — read, write, append, delete page content
759
763
 
760
764
  ```ts
761
765
  import {
762
- useNotionGetBlock,
763
- useNotionGetBlockChildren,
764
- useNotionListAllBlockChildren,
765
- useNotionAppendBlocks,
766
- useNotionUpdateBlock,
767
- useNotionDeleteBlock,
766
+ useNotionGetBlock,
767
+ useNotionGetBlockChildren,
768
+ useNotionListAllBlockChildren,
769
+ useNotionAppendBlocks,
770
+ useNotionUpdateBlock,
771
+ useNotionDeleteBlock,
768
772
  } from "katanakit-js/adapters/notion";
769
773
 
770
774
  // Get ALL blocks of a page (auto-pagination)
771
775
  const blocks = await useNotionListAllBlockChildren("page-id");
772
776
  if (blocks.ok) {
773
- 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.
774
778
  }
775
779
 
776
780
  // Manual pagination (for fine-grained cursor control)
777
781
  const page = await useNotionGetBlockChildren("page-id", { page_size: 50 });
778
782
  if (page.ok) {
779
- console.log(page.data.results); // blocks
780
- console.log(page.data.has_more); // true if more pages exist
781
- 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
782
786
  }
783
787
 
784
788
  // Append content blocks to a page
785
789
  await useNotionAppendBlocks("page-id", [
786
- {
787
- type: "heading_2",
788
- heading_2: { rich_text: [{ type: "text", text: { content: "New Section" } }] },
789
- },
790
- {
791
- type: "paragraph",
792
- paragraph: { rich_text: [{ type: "text", text: { content: "Body text here." } }] },
793
- },
794
- {
795
- type: "to_do",
796
- to_do: {
797
- rich_text: [{ type: "text", text: { content: "Checklist item" } }],
798
- checked: false,
799
- },
800
- },
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
+ },
801
805
  ]);
802
806
 
803
807
  // Update a block's content
804
808
  await useNotionUpdateBlock("block-id", {
805
- paragraph: { rich_text: [{ type: "text", text: { content: "Updated text" } }] },
809
+ paragraph: { rich_text: [{ type: "text", text: { content: "Updated text" } }] },
806
810
  });
807
811
 
808
812
  // Delete (archive) a block
@@ -819,14 +823,14 @@ const found = await useNotionSearchContent({ query: "meeting notes" });
819
823
 
820
824
  // Search only databases
821
825
  const dbs = await useNotionSearchContent({
822
- query: "tasks",
823
- filter: { value: "database", property: "object" },
826
+ query: "tasks",
827
+ filter: { value: "database", property: "object" },
824
828
  });
825
829
 
826
830
  // Search only pages, sorted by recently edited
827
831
  const pages = await useNotionSearchContent({
828
- filter: { value: "page", property: "object" },
829
- sort: { direction: "descending", timestamp: "last_edited_time" },
832
+ filter: { value: "page", property: "object" },
833
+ sort: { direction: "descending", timestamp: "last_edited_time" },
830
834
  });
831
835
  ```
832
836
 
@@ -841,22 +845,22 @@ if (user.ok) console.log(user.data.name);
841
845
 
842
846
  // List all workspace members + bots
843
847
  const users = await useNotionListUsers();
844
- 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));
845
849
  ```
846
850
 
847
851
  #### Framework examples
848
852
 
849
- | Framework | Example | Description |
850
- |-----------|---------|-------------|
851
- | **Vue 3** | [`examples/notion/vue-blog.vue`](https://github.com/senseikatana/katanakit/tree/main/examples/notion/vue-blog.vue) | Blog listing with `useQuery` composable |
852
- | **Vue 3** | [`examples/notion/vue-post.vue`](https://github.com/senseikatana/katanakit/tree/main/examples/notion/vue-post.vue) | Single post view |
853
- | **Nuxt 3** | [`examples/notion/nuxt-blog.vue`](https://github.com/senseikatana/katanakit/tree/main/examples/notion/nuxt-blog.vue) | SSR blog listing with `useAsyncData` |
854
- | **Nuxt 3** | [`examples/notion/nuxt-post.vue`](https://github.com/senseikatana/katanakit/tree/main/examples/notion/nuxt-post.vue) | SSR single post view |
855
- | **Astro** | [`examples/notion/astro-blog.astro`](https://github.com/senseikatana/katanakit/tree/main/examples/notion/astro-blog.astro) | Static blog listing |
856
- | **Astro** | [`examples/notion/astro-[slug].astro`](https://github.com/senseikatana/katanakit/tree/main/examples/notion/astro-[slug].astro) | Dynamic `[slug]` route |
857
- | **Next.js** | [`examples/notion/next-blog.tsx`](https://github.com/senseikatana/katanakit/tree/main/examples/notion/next-blog.tsx) | Server component blog listing |
858
- | **Next.js** | [`examples/notion/next-[slug].tsx`](https://github.com/senseikatana/katanakit/tree/main/examples/notion/next-[slug].tsx) | Dynamic `[slug]` page |
859
- | **Node.js** | [`examples/notion/demo.ts`](https://github.com/senseikatana/katanakit/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 |
860
864
 
861
865
  ### WordPress
862
866
 
@@ -871,20 +875,20 @@ import { useInitWordPress } from "katanakit-js/adapters/wordpress";
871
875
 
872
876
  // Application Passwords (recommended) — WP Admin → Users → Your Profile → Application Passwords
873
877
  useInitWordPress({
874
- baseUrl: "https://mysite.com",
875
- 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" },
876
880
  });
877
881
 
878
882
  // JWT tokens
879
883
  useInitWordPress({
880
- baseUrl: "https://mysite.com",
881
- auth: { type: "jwt", token: "eyJhbGci..." },
884
+ baseUrl: "https://mysite.com",
885
+ auth: { type: "jwt", token: "eyJhbGci..." },
882
886
  });
883
887
 
884
888
  // Nonce-based (for WP themes)
885
889
  useInitWordPress({
886
- baseUrl: "https://mysite.com",
887
- auth: { type: "nonce", nonce: "abc123" },
890
+ baseUrl: "https://mysite.com",
891
+ auth: { type: "nonce", nonce: "abc123" },
888
892
  });
889
893
  ```
890
894
 
@@ -892,22 +896,22 @@ useInitWordPress({
892
896
 
893
897
  ```ts
894
898
  import {
895
- useWpGetPosts,
896
- useWpGetPost,
897
- useWpCreatePost,
898
- useWpUpdatePost,
899
- useWpDeletePost,
900
- useWpListAllPosts,
901
- useWpSearchAllPosts,
902
- useWpFindPostBySlug,
899
+ useWpGetPosts,
900
+ useWpGetPost,
901
+ useWpCreatePost,
902
+ useWpUpdatePost,
903
+ useWpDeletePost,
904
+ useWpListAllPosts,
905
+ useWpSearchAllPosts,
906
+ useWpFindPostBySlug,
903
907
  } from "katanakit-js/adapters/wordpress";
904
908
 
905
909
  // List published posts (paginated)
906
910
  const posts = await useWpGetPosts({
907
- per_page: 5,
908
- status: "publish",
909
- orderby: "date",
910
- order: "desc",
911
+ per_page: 5,
912
+ status: "publish",
913
+ orderby: "date",
914
+ order: "desc",
911
915
  });
912
916
 
913
917
  // Get a single post with embedded resources
@@ -916,19 +920,19 @@ if (post.ok) console.log(post.data.title.rendered);
916
920
 
917
921
  // Create a post
918
922
  await useWpCreatePost({
919
- title: "My New Post",
920
- content: "<p>Hello World!</p>",
921
- status: "publish", // "draft" | "pending" | "publish"
922
- categories: [1, 3],
923
- 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],
924
928
  });
925
929
 
926
930
  // Update a post
927
931
  await useWpUpdatePost(42, { title: "Updated Title", status: "publish" });
928
932
 
929
933
  // Delete (trash or permanent)
930
- await useWpDeletePost(42); // move to trash
931
- await useWpDeletePost(42, true); // permanently delete
934
+ await useWpDeletePost(42); // move to trash
935
+ await useWpDeletePost(42, true); // permanently delete
932
936
 
933
937
  // Get ALL posts (auto-pagination — loops until exhausted)
934
938
  const all = await useWpListAllPosts({ status: "publish" });
@@ -936,7 +940,7 @@ if (all.ok) console.log(`Total: ${all.data.length}`);
936
940
 
937
941
  // Search ALL posts by keyword
938
942
  const found = await useWpSearchAllPosts("tutorial");
939
- 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));
940
944
 
941
945
  // Find post by slug (for dynamic routes like /blog/:slug)
942
946
  const bySlug = await useWpFindPostBySlug("hello-world");
@@ -947,11 +951,11 @@ if (bySlug.ok && bySlug.data) console.log(bySlug.data.title.rendered);
947
951
 
948
952
  ```ts
949
953
  import {
950
- useWpGetPages,
951
- useWpGetPage,
952
- useWpCreatePage,
953
- useWpUpdatePage,
954
- useWpDeletePage,
954
+ useWpGetPages,
955
+ useWpGetPage,
956
+ useWpCreatePage,
957
+ useWpUpdatePage,
958
+ useWpDeletePage,
955
959
  } from "katanakit-js/adapters/wordpress";
956
960
 
957
961
  const pages = await useWpGetPages({ per_page: 20 });
@@ -960,10 +964,10 @@ const page = await useWpGetPage(10);
960
964
  if (page.ok) console.log(page.data.title.rendered);
961
965
 
962
966
  await useWpCreatePage({
963
- title: "About Us",
964
- content: "<p>Welcome to our site!</p>",
965
- status: "publish",
966
- 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)
967
971
  });
968
972
 
969
973
  await useWpUpdatePage(10, { title: "Updated About" });
@@ -974,11 +978,11 @@ await useWpDeletePage(10); // trash
974
978
 
975
979
  ```ts
976
980
  import {
977
- useWpGetMedia,
978
- useWpGetMediaItem,
979
- useWpUploadMedia,
980
- useWpUpdateMedia,
981
- useWpDeleteMedia,
981
+ useWpGetMedia,
982
+ useWpGetMediaItem,
983
+ useWpUploadMedia,
984
+ useWpUpdateMedia,
985
+ useWpDeleteMedia,
982
986
  } from "katanakit-js/adapters/wordpress";
983
987
 
984
988
  // List media items
@@ -991,9 +995,9 @@ if (item.ok) console.log(item.data.source_url);
991
995
  // Upload from browser (File from <input type="file">)
992
996
  const file = document.querySelector("input[type=file]").files[0];
993
997
  const uploaded = await useWpUploadMedia(file, {
994
- title: "My Image",
995
- alt_text: "Description for accessibility",
996
- caption: "Image caption",
998
+ title: "My Image",
999
+ alt_text: "Description for accessibility",
1000
+ caption: "Image caption",
997
1001
  });
998
1002
  if (uploaded.ok) console.log(uploaded.data.source_url); // URL to use in content
999
1003
 
@@ -1013,10 +1017,16 @@ await useWpDeleteMedia(42, true); // permanent
1013
1017
 
1014
1018
  ```ts
1015
1019
  import {
1016
- useWpGetCategories, useWpGetCategory, useWpCreateCategory,
1017
- useWpUpdateCategory, useWpDeleteCategory,
1018
- useWpGetTags, useWpGetTag, useWpCreateTag,
1019
- useWpUpdateTag, useWpDeleteTag,
1020
+ useWpGetCategories,
1021
+ useWpGetCategory,
1022
+ useWpCreateCategory,
1023
+ useWpUpdateCategory,
1024
+ useWpDeleteCategory,
1025
+ useWpGetTags,
1026
+ useWpGetTag,
1027
+ useWpCreateTag,
1028
+ useWpUpdateTag,
1029
+ useWpDeleteTag,
1020
1030
  } from "katanakit-js/adapters/wordpress";
1021
1031
 
1022
1032
  // Categories
@@ -1036,8 +1046,11 @@ await useWpDeleteTag(12);
1036
1046
 
1037
1047
  ```ts
1038
1048
  import {
1039
- useWpGetComments, useWpGetComment, useWpCreateComment,
1040
- useWpUpdateComment, useWpDeleteComment,
1049
+ useWpGetComments,
1050
+ useWpGetComment,
1051
+ useWpCreateComment,
1052
+ useWpUpdateComment,
1053
+ useWpDeleteComment,
1041
1054
  } from "katanakit-js/adapters/wordpress";
1042
1055
 
1043
1056
  // Get comments for a post
@@ -1045,10 +1058,10 @@ const comments = await useWpGetComments({ post: 42, per_page: 10 });
1045
1058
 
1046
1059
  // Create a comment (public or authenticated)
1047
1060
  await useWpCreateComment({
1048
- post: 42,
1049
- content: "Great article!",
1050
- author_name: "John",
1051
- author_email: "john@example.com",
1061
+ post: 42,
1062
+ content: "Great article!",
1063
+ author_name: "John",
1064
+ author_email: "john@example.com",
1052
1065
  });
1053
1066
 
1054
1067
  // Update / delete
@@ -1060,18 +1073,22 @@ await useWpDeleteComment(7);
1060
1073
 
1061
1074
  ```ts
1062
1075
  import {
1063
- useWpGetUsers, useWpGetUser, useWpGetCurrentUser,
1064
- useWpCreateUser, useWpUpdateUser, useWpDeleteUser,
1076
+ useWpGetUsers,
1077
+ useWpGetUser,
1078
+ useWpGetCurrentUser,
1079
+ useWpCreateUser,
1080
+ useWpUpdateUser,
1081
+ useWpDeleteUser,
1065
1082
  } from "katanakit-js/adapters/wordpress";
1066
1083
 
1067
1084
  const users = await useWpGetUsers({ roles: "editor" });
1068
1085
  const me = await useWpGetCurrentUser(); // authenticated user
1069
1086
 
1070
1087
  await useWpCreateUser({
1071
- username: "johndoe",
1072
- email: "john@example.com",
1073
- password: "secure-password",
1074
- roles: ["editor"],
1088
+ username: "johndoe",
1089
+ email: "john@example.com",
1090
+ password: "secure-password",
1091
+ roles: ["editor"],
1075
1092
  });
1076
1093
 
1077
1094
  await useWpUpdateUser(2, { name: "John Smith" });
@@ -1082,8 +1099,11 @@ await useWpDeleteUser(2, 1); // reassign content to user 1
1082
1099
 
1083
1100
  ```ts
1084
1101
  import {
1085
- useWpGetCustomPosts, useWpGetCustomPost,
1086
- useWpCreateCustomPost, useWpUpdateCustomPost, useWpDeleteCustomPost,
1102
+ useWpGetCustomPosts,
1103
+ useWpGetCustomPost,
1104
+ useWpCreateCustomPost,
1105
+ useWpUpdateCustomPost,
1106
+ useWpDeleteCustomPost,
1087
1107
  } from "katanakit-js/adapters/wordpress";
1088
1108
 
1089
1109
  // Works with any registered CPT: "product", "portfolio", "event", etc.
@@ -1091,9 +1111,9 @@ const products = await useWpGetCustomPosts("product", { per_page: 10 });
1091
1111
  const product = await useWpGetCustomPost("product", 15);
1092
1112
 
1093
1113
  await useWpCreateCustomPost("product", {
1094
- title: "Widget",
1095
- content: "<p>A great widget</p>",
1096
- status: "publish",
1114
+ title: "Widget",
1115
+ content: "<p>A great widget</p>",
1116
+ status: "publish",
1097
1117
  });
1098
1118
 
1099
1119
  await useWpUpdateCustomPost("product", 15, { title: "Updated Widget" });
@@ -1106,13 +1126,13 @@ await useWpDeleteCustomPost("product", 15, true);
1106
1126
  import { useWpBatch } from "katanakit-js/adapters/wordpress";
1107
1127
 
1108
1128
  const result = await useWpBatch([
1109
- { method: "GET", path: "/wp/v2/posts?per_page=2" },
1110
- { method: "GET", path: "/wp/v2/pages?per_page=2" },
1111
- { 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" },
1112
1132
  ]);
1113
1133
 
1114
1134
  if (result.ok) {
1115
- result.data.responses.forEach(resp => console.log(resp.status));
1135
+ result.data.responses.forEach((resp) => console.log(resp.status));
1116
1136
  }
1117
1137
  ```
1118
1138
 
@@ -1124,8 +1144,8 @@ significantly for list views.
1124
1144
  ```ts
1125
1145
  // Only fetch id, title, link, slug, and date
1126
1146
  const posts = await useWpGetPosts({
1127
- per_page: 20,
1128
- _fields: "id,title,link,slug,date",
1147
+ per_page: 20,
1148
+ _fields: "id,title,link,slug,date",
1129
1149
  });
1130
1150
  ```
1131
1151
 
@@ -1140,16 +1160,17 @@ const posts = await useWpGetPosts({ _embed: true });
1140
1160
 
1141
1161
  // Embed only specific resources
1142
1162
  const posts = await useWpGetPosts({
1143
- _embed: "author,wp:featuredmedia",
1163
+ _embed: "author,wp:featuredmedia",
1144
1164
  });
1145
1165
 
1146
1166
  // Access embedded data
1147
1167
  if (posts.ok) {
1148
- for (const post of posts.data) {
1149
- const author = post._embedded?.author?.[0]?.name;
1150
- const image = post._embedded?.["wp:featuredmedia"]?.[0]?.source_url;
1151
- const thumbnail = post._embedded?.["wp:featuredmedia"]?.[0]?.media_details?.sizes?.thumbnail?.source_url;
1152
- }
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
+ }
1153
1174
  }
1154
1175
  ```
1155
1176
 
@@ -1162,40 +1183,40 @@ media item, or custom post type entry.
1162
1183
  ```ts
1163
1184
  // Request ACF fields explicitly with _fields
1164
1185
  const posts = await useWpGetPosts({
1165
- per_page: 5,
1166
- _fields: "id,title,acf",
1186
+ per_page: 5,
1187
+ _fields: "id,title,acf",
1167
1188
  });
1168
1189
 
1169
1190
  if (posts.ok) {
1170
- for (const post of posts.data) {
1171
- if (post.acf) {
1172
- // ACF fields are dynamic — access by field name
1173
- console.log(post.acf.my_field_name);
1174
- }
1175
- }
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
+ }
1176
1197
  }
1177
1198
 
1178
1199
  // Combine _fields + _embed + ACF for full-featured list views
1179
1200
  const full = await useWpGetPosts({
1180
- per_page: 5,
1181
- _fields: "id,title,link,slug,date,acf",
1182
- _embed: "author,wp:featuredmedia",
1201
+ per_page: 5,
1202
+ _fields: "id,title,link,slug,date,acf",
1203
+ _embed: "author,wp:featuredmedia",
1183
1204
  });
1184
1205
  ```
1185
1206
 
1186
1207
  #### Framework examples
1187
1208
 
1188
- | Framework | Example | Description |
1189
- |-----------|---------|-------------|
1190
- | **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` |
1191
- | **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 |
1192
- | **Nuxt 3** | [`examples/wordpress/nuxt-blog.vue`](https://github.com/senseikatana/katanakit/tree/main/examples/wordpress/nuxt-blog.vue) | SSR blog listing with `useAsyncData` |
1193
- | **Nuxt 3** | [`examples/wordpress/nuxt-post.vue`](https://github.com/senseikatana/katanakit/tree/main/examples/wordpress/nuxt-post.vue) | SSR single post view |
1194
- | **Astro** | [`examples/wordpress/astro-blog.astro`](https://github.com/senseikatana/katanakit/tree/main/examples/wordpress/astro-blog.astro) | Static blog listing |
1195
- | **Astro** | [`examples/wordpress/astro-[slug].astro`](https://github.com/senseikatana/katanakit/tree/main/examples/wordpress/astro-[slug].astro) | Dynamic `[slug]` route |
1196
- | **Next.js** | [`examples/wordpress/next-blog.tsx`](https://github.com/senseikatana/katanakit/tree/main/examples/wordpress/next-blog.tsx) | Server component blog listing |
1197
- | **Next.js** | [`examples/wordpress/next-[slug].tsx`](https://github.com/senseikatana/katanakit/tree/main/examples/wordpress/next-[slug].tsx) | Dynamic `[slug]` page |
1198
- | **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 |
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 |
1199
1220
 
1200
1221
  ## Contributing
1201
1222