katanakit-js 3.2.1 → 4.0.0

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 (130) hide show
  1. package/README.md +142 -35
  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/express/products.controller.d.ts.map +1 -1
  25. package/dist/adapters/express/products.controller.js +3 -1
  26. package/dist/adapters/express/products.controller.js.map +1 -1
  27. package/dist/adapters/notion/notion.service.d.ts +4 -2
  28. package/dist/adapters/notion/notion.service.d.ts.map +1 -1
  29. package/dist/adapters/notion/notion.service.js +19 -5
  30. package/dist/adapters/notion/notion.service.js.map +1 -1
  31. package/dist/adapters/react/fetch.d.ts +44 -0
  32. package/dist/adapters/react/fetch.d.ts.map +1 -0
  33. package/dist/adapters/react/fetch.js +74 -0
  34. package/dist/adapters/react/fetch.js.map +1 -0
  35. package/dist/adapters/react/index.d.ts +4 -0
  36. package/dist/adapters/react/index.d.ts.map +1 -0
  37. package/dist/adapters/react/index.js +4 -0
  38. package/dist/adapters/react/index.js.map +1 -0
  39. package/dist/adapters/react/query.d.ts +104 -0
  40. package/dist/adapters/react/query.d.ts.map +1 -0
  41. package/dist/adapters/react/query.js +200 -0
  42. package/dist/adapters/react/query.js.map +1 -0
  43. package/dist/adapters/react/watch.d.ts +37 -0
  44. package/dist/adapters/react/watch.d.ts.map +1 -0
  45. package/dist/adapters/react/watch.js +39 -0
  46. package/dist/adapters/react/watch.js.map +1 -0
  47. package/dist/adapters/solid/fetch.d.ts +43 -0
  48. package/dist/adapters/solid/fetch.d.ts.map +1 -0
  49. package/dist/adapters/solid/fetch.js +72 -0
  50. package/dist/adapters/solid/fetch.js.map +1 -0
  51. package/dist/adapters/solid/index.d.ts +4 -0
  52. package/dist/adapters/solid/index.d.ts.map +1 -0
  53. package/dist/adapters/solid/index.js +4 -0
  54. package/dist/adapters/solid/index.js.map +1 -0
  55. package/dist/adapters/solid/query.d.ts +102 -0
  56. package/dist/adapters/solid/query.d.ts.map +1 -0
  57. package/dist/adapters/solid/query.js +163 -0
  58. package/dist/adapters/solid/query.js.map +1 -0
  59. package/dist/adapters/solid/watch.d.ts +32 -0
  60. package/dist/adapters/solid/watch.d.ts.map +1 -0
  61. package/dist/adapters/solid/watch.js +36 -0
  62. package/dist/adapters/solid/watch.js.map +1 -0
  63. package/dist/adapters/svelte/fetch.d.ts +40 -0
  64. package/dist/adapters/svelte/fetch.d.ts.map +1 -0
  65. package/dist/adapters/svelte/fetch.js +75 -0
  66. package/dist/adapters/svelte/fetch.js.map +1 -0
  67. package/dist/adapters/svelte/index.d.ts +4 -0
  68. package/dist/adapters/svelte/index.d.ts.map +1 -0
  69. package/dist/adapters/svelte/index.js +4 -0
  70. package/dist/adapters/svelte/index.js.map +1 -0
  71. package/dist/adapters/svelte/query.d.ts +108 -0
  72. package/dist/adapters/svelte/query.d.ts.map +1 -0
  73. package/dist/adapters/svelte/query.js +172 -0
  74. package/dist/adapters/svelte/query.js.map +1 -0
  75. package/dist/adapters/svelte/watch.d.ts +32 -0
  76. package/dist/adapters/svelte/watch.d.ts.map +1 -0
  77. package/dist/adapters/svelte/watch.js +41 -0
  78. package/dist/adapters/svelte/watch.js.map +1 -0
  79. package/dist/adapters/vue/query.d.ts.map +1 -1
  80. package/dist/adapters/vue/query.js +8 -6
  81. package/dist/adapters/vue/query.js.map +1 -1
  82. package/dist/adapters/vue/vue.service.d.ts +9 -5
  83. package/dist/adapters/vue/vue.service.d.ts.map +1 -1
  84. package/dist/adapters/vue/vue.service.js +7 -3
  85. package/dist/adapters/vue/vue.service.js.map +1 -1
  86. package/dist/adapters/wordpress/wordpress.service.d.ts +2 -1
  87. package/dist/adapters/wordpress/wordpress.service.d.ts.map +1 -1
  88. package/dist/adapters/wordpress/wordpress.service.js +10 -5
  89. package/dist/adapters/wordpress/wordpress.service.js.map +1 -1
  90. package/dist/core/services/agent.service.d.ts.map +1 -1
  91. package/dist/core/services/agent.service.js +9 -1
  92. package/dist/core/services/agent.service.js.map +1 -1
  93. package/dist/core/services/dates.service.d.ts.map +1 -1
  94. package/dist/core/services/dates.service.js +2 -1
  95. package/dist/core/services/dates.service.js.map +1 -1
  96. package/dist/core/services/http.service.d.ts +26 -5
  97. package/dist/core/services/http.service.d.ts.map +1 -1
  98. package/dist/core/services/http.service.js +30 -6
  99. package/dist/core/services/http.service.js.map +1 -1
  100. package/dist/core/services/query.service.d.ts +4 -0
  101. package/dist/core/services/query.service.d.ts.map +1 -1
  102. package/dist/core/services/query.service.js +66 -55
  103. package/dist/core/services/query.service.js.map +1 -1
  104. package/dist/core/services/reactive.service.d.ts.map +1 -1
  105. package/dist/core/services/reactive.service.js +0 -3
  106. package/dist/core/services/reactive.service.js.map +1 -1
  107. package/dist/core/services/utils.service.d.ts.map +1 -1
  108. package/dist/core/services/utils.service.js +3 -0
  109. package/dist/core/services/utils.service.js.map +1 -1
  110. package/dist/infrastructure/dom/dom.service.d.ts.map +1 -1
  111. package/dist/infrastructure/dom/dom.service.js +15 -3
  112. package/dist/infrastructure/dom/dom.service.js.map +1 -1
  113. package/dist/infrastructure/storage/storage.service.d.ts.map +1 -1
  114. package/dist/infrastructure/storage/storage.service.js +12 -2
  115. package/dist/infrastructure/storage/storage.service.js.map +1 -1
  116. package/dist/prisma/assistant.store.d.ts +3 -0
  117. package/dist/prisma/assistant.store.d.ts.map +1 -1
  118. package/dist/prisma/assistant.store.js +12 -6
  119. package/dist/prisma/assistant.store.js.map +1 -1
  120. package/dist/prisma/use-prisma.d.ts +5 -4
  121. package/dist/prisma/use-prisma.d.ts.map +1 -1
  122. package/dist/prisma/use-prisma.js +5 -4
  123. package/dist/prisma/use-prisma.js.map +1 -1
  124. package/dist/types/index.d.ts +0 -1
  125. package/dist/types/index.d.ts.map +1 -1
  126. package/package.json +249 -185
  127. package/dist/prisma/db.d.ts +0 -4
  128. package/dist/prisma/db.d.ts.map +0 -1
  129. package/dist/prisma/db.js +0 -12
  130. 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)
@@ -171,14 +171,66 @@ if (result.ok) {
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" },
200
+ });
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("Will fetch:", useBuildUrl("myApi", "users", {
221
+ query: { page: 1, per_page: 20 },
222
+ }));
223
+ // → "https://api.myapp.com/v1/users?page=1&per_page=20"
224
+ ```
225
+
226
+ #### SSR: construct URLs server-side
227
+
228
+ ```ts
229
+ // In Astro, Next.js, or Nuxt server code
230
+ const apiUrl = useBuildUrl("notion", "database", {
231
+ params: { id: "db-123" },
180
232
  });
181
- // "https://jsonplaceholder.typicode.com/posts/7?_limit=3"
233
+ // Pass to client component or pre-render
182
234
  ```
183
235
 
184
236
  ### 7. FormData and raw bodies
@@ -195,7 +247,7 @@ await usePost("myApi", "upload", form);
195
247
 
196
248
  ### Full example
197
249
 
198
- See [`examples/api-manager/demo.ts`](https://github.com/senseikatana/katanakit-js/tree/main/examples/api-manager) for a runnable demo
250
+ See [`examples/api-manager/demo.ts`](https://github.com/senseikatana/katanakit/tree/main/examples/api-manager) for a runnable demo
199
251
  covering all CRUD operations, auth injection, URL building, and error handling
200
252
  against a real API (JSONPlaceholder).
201
253
 
@@ -286,6 +338,27 @@ when you want sessions, persistence, and channels (REST, Telegram, WhatsApp). Fa
286
338
  follow the Safe Result pattern: they **never throw**. Check `result.ok` and read `result.data`
287
339
  or `result.error`.
288
340
 
341
+ ### When to use Kitt
342
+
343
+ Kitt is a **lightweight, zero-dependency** wrapper for OpenAI-compatible APIs.
344
+ Use it when you need:
345
+
346
+ - **One-shot chat** — ask a question, get an answer (`useChat`)
347
+ - **Tool-calling agents** — autonomous loops that read/write/run (`useRunAgent`)
348
+ - **Session-aware bots** — REST, Telegram, WhatsApp channels (`useReply`)
349
+
350
+ ### When to use alternatives
351
+
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) |
356
+ | Multi-agent orchestration | [CrewAI](https://github.com/crewAIInc/crewAI) |
357
+ | Full chatbot framework | [Botpress](https://botpress.com) |
358
+
359
+ Kitt stays small (0 dependencies) because it does one thing well:
360
+ **chat completions with tool calls, using any OpenAI-compatible endpoint.**
361
+
289
362
  ### Low-level API
290
363
 
291
364
  ```ts
@@ -360,7 +433,7 @@ Also on the main barrel:
360
433
  | `useResetSession` | `(sessionId) => Promise<void>` |
361
434
  | `useCreateMemoryStore` | `() => ConversationStore` |
362
435
 
363
- Then start a channel (see below). Copy keys from [`.env.example`](https://github.com/senseikatana/katanakit-js/blob/main/.env.example):
436
+ Then start a channel (see below). Copy keys from [`.env.example`](https://github.com/senseikatana/katanakit/blob/main/.env.example):
364
437
 
365
438
  ```env
366
439
  DASHSCOPE_API_KEY=
@@ -379,10 +452,10 @@ DATABASE_URL=
379
452
 
380
453
  | Script | Starts |
381
454
  |--------|--------|
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`. |
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_*`. |
458
+ | `bun run assistant:demo` | Demo in `examples/assistant/`. Set `KITT_CHANNEL=rest\|telegram\|whatsapp`. |
386
459
 
387
460
  ### REST
388
461
 
@@ -442,7 +515,7 @@ await useStartTelegramPolling();
442
515
  2. Send `/newbot` — choose a name and a username.
443
516
  3. Copy the token.
444
517
  4. Set `TELEGRAM_BOT_TOKEN`.
445
- 5. Run `pnpm telegram:dev` (long polling, no public URL).
518
+ 5. Run `bun run telegram:dev` (long polling, no public URL).
446
519
  6. Optional webhook: expose HTTPS and call `useHandleTelegramUpdate(update)` on inbound updates.
447
520
 
448
521
  ### WhatsApp (Meta Cloud API)
@@ -465,7 +538,7 @@ useStartWhatsApp();
465
538
  2. Add the WhatsApp product.
466
539
  3. Copy the temporary or permanent token, the Phone number ID, and the **App Secret**.
467
540
  4. Set `WHATSAPP_TOKEN`, `WHATSAPP_PHONE_NUMBER_ID`, `WHATSAPP_VERIFY_TOKEN`, and `WHATSAPP_APP_SECRET`.
468
- 5. Run `pnpm whatsapp:dev`.
541
+ 5. Run `bun run whatsapp:dev`.
469
542
  6. Expose public HTTPS (Cloudflare Tunnel or ngrok) to `GET`/`POST` `/whatsapp/webhook`.
470
543
  7. In Meta, set the webhook URL and verify token; subscribe to `messages`.
471
544
 
@@ -496,11 +569,11 @@ owns the database, run `prisma contract emit` then `prisma db init` in that app.
496
569
 
497
570
  ### Real use case
498
571
 
499
- [`examples/assistant/`](https://github.com/senseikatana/katanakit-js/tree/main/examples/assistant) is a generic digital assistant with two demo tools:
572
+ [`examples/assistant/`](https://github.com/senseikatana/katanakit/tree/main/examples/assistant) is a generic digital assistant with two demo tools:
500
573
  `readFile` on `knowledge-base.md` and `saveNote` to `notes.jsonl`.
501
574
 
502
575
  ```bash
503
- pnpm assistant:demo
576
+ bun run assistant:demo
504
577
  # KITT_CHANNEL=rest|telegram|whatsapp
505
578
  ```
506
579
 
@@ -540,7 +613,7 @@ const result = await useGetApi("pokeapi", "pokemonById", { params: { id: 25 } })
540
613
 
541
614
  ```ts
542
615
  import { useLogger, useInitApis, useGetApi } from "katanakit-js";
543
- import { useKatanaFetch } from "katanakit-js/adapters/vue"; // Vue only
616
+ import { useRequest } from "katanakit-js/adapters/vue"; // Vue only
544
617
  import { useUnwrap } from "katanakit-js/adapters/nuxt"; // Nuxt only
545
618
  ```
546
619
 
@@ -559,7 +632,7 @@ See [Getting Started](https://senseikatana.com/katanakit-js/docs/guides/getting-
559
632
  |---------|--------|-------------|
560
633
  | **Express** | `katanakit-js/adapters/express` | Reference server with CORS and hardened headers |
561
634
  | **Nuxt** | `katanakit-js/adapters/nuxt` | `useUnwrap`, `useSafeResponse`, `useEventResponse` |
562
- | **Vue** | `katanakit-js/adapters/vue` | `useKatanaFetch` composable with reactivity |
635
+ | **Vue** | `katanakit-js/adapters/vue` | `useRequest` composable with reactivity |
563
636
  | **Astro** | `katanakit-js` or `katanakit-js/adapters/astro` | `AstroService`, `RssService` |
564
637
  | **Assistant** | `katanakit-js/adapters/assistant` | REST digital assistant (`useStartAssistant`) |
565
638
  | **Telegram** | `katanakit-js/adapters/telegram` | BotFather bot (`useInitTelegram`, `useStartTelegramPolling`) |
@@ -775,15 +848,15 @@ if (users.ok) users.data.results.forEach(u => console.log(u.name));
775
848
 
776
849
  | Framework | Example | Description |
777
850
  |-----------|---------|-------------|
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 |
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 |
787
860
 
788
861
  ### WordPress
789
862
 
@@ -1114,15 +1187,49 @@ const full = await useWpGetPosts({
1114
1187
 
1115
1188
  | Framework | Example | Description |
1116
1189
  |-----------|---------|-------------|
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 |
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 |
1199
+
1200
+ ## Contributing
1201
+
1202
+ We welcome contributions from the community! Whether it's fixing a bug, adding
1203
+ a feature, or improving documentation, every contribution helps make KatanaKit
1204
+ better for everyone.
1205
+
1206
+ ### Quick start
1207
+
1208
+ ```bash
1209
+ git clone https://github.com/senseikatana/katanakit.git
1210
+ cd katanakit-js
1211
+ git checkout dev
1212
+ bun install
1213
+ bun run check # verify everything works
1214
+ ```
1215
+
1216
+ ### Ways to contribute
1217
+
1218
+ - **Report bugs** — [Open an issue](https://github.com/senseikatana/katanakit/issues) with a clear description and reproduction steps
1219
+ - **Suggest features** — [Start a discussion](https://github.com/senseikatana/katanakit/discussions) to propose new ideas
1220
+ - **Submit a PR** — Fork the repo, create a branch from `dev`, make your changes, and open a PR
1221
+ - **Improve docs** — Fix typos, add examples, or clarify explanations
1222
+ - **Add adapters** — Build integrations for new APIs or frameworks
1223
+
1224
+ ### PR guidelines
1225
+
1226
+ 1. Branch from `dev` (not `main`)
1227
+ 2. Follow the `use*` naming convention
1228
+ 3. Add TypeScript types in `src/types/index.ts`
1229
+ 4. Run `bun run check` before submitting
1230
+ 5. Update `CHANGELOG.md` if the public API changed
1231
+
1232
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the full development contract.
1126
1233
 
1127
1234
  ## Documentation
1128
1235
 
@@ -0,0 +1,37 @@
1
+ import { type Signal } from "@angular/core";
2
+ import type { ApiError, UrlOptions } from "../../types/index.js";
3
+ /** Reactive state exposed by the {@link useRequest} factory (Angular signals). */
4
+ export interface RequestState<T = unknown> {
5
+ data: Signal<T | null>;
6
+ error: Signal<ApiError | null>;
7
+ loading: Signal<boolean>;
8
+ refetch: () => Promise<void>;
9
+ }
10
+ /**
11
+ * Angular signal factory that wraps KatanaKit's HTTP GET. Bridges the Safe
12
+ * Result pattern to Angular signals and never throws on HTTP errors.
13
+ *
14
+ * Must be called within an injection context (component field initializer,
15
+ * constructor, or service) so it can abort in-flight requests via `DestroyRef`.
16
+ * Stale responses are ignored via a request version counter.
17
+ *
18
+ * @param apiName - Name of the registered API (see `useInitApis`).
19
+ * @param endpointName - Name of the endpoint inside that API.
20
+ * @param options - Optional `UrlOptions` (path/query params).
21
+ * @returns Signals `{ data, error, loading }` plus `refetch`.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { Component } from "@angular/core";
26
+ * import { useRequest } from "katanakit-js/adapters/angular";
27
+ *
28
+ * @Component({ selector: "app-pokemon", template: `@if (loading()) { Loading… } @else { {{ data()?.name }} }` })
29
+ * export class PokemonComponent {
30
+ * readonly result = useRequest<{ name: string }>("pokeapi", "pokemonById", { params: { id: 25 } });
31
+ * readonly data = this.result.data;
32
+ * readonly loading = this.result.loading;
33
+ * }
34
+ * ```
35
+ */
36
+ export declare function useRequest<T>(apiName: string, endpointName: string, options?: UrlOptions): RequestState<T>;
37
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../src/adapters/angular/fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,MAAM,EAAU,MAAM,eAAe,CAAC;AAGxE,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEjE,kFAAkF;AAClF,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,OAAO;IACxC,IAAI,EAAE,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACzB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAC3B,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,UAAU,GAClB,YAAY,CAAC,CAAC,CAAC,CA6CjB"}
@@ -0,0 +1,67 @@
1
+ import { DestroyRef, inject, signal } from "@angular/core";
2
+ import { useFetch } from "../../core/services/http.service.js";
3
+ /**
4
+ * Angular signal factory that wraps KatanaKit's HTTP GET. Bridges the Safe
5
+ * Result pattern to Angular signals and never throws on HTTP errors.
6
+ *
7
+ * Must be called within an injection context (component field initializer,
8
+ * constructor, or service) so it can abort in-flight requests via `DestroyRef`.
9
+ * Stale responses are ignored via a request version counter.
10
+ *
11
+ * @param apiName - Name of the registered API (see `useInitApis`).
12
+ * @param endpointName - Name of the endpoint inside that API.
13
+ * @param options - Optional `UrlOptions` (path/query params).
14
+ * @returns Signals `{ data, error, loading }` plus `refetch`.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { Component } from "@angular/core";
19
+ * import { useRequest } from "katanakit-js/adapters/angular";
20
+ *
21
+ * @Component({ selector: "app-pokemon", template: `@if (loading()) { Loading… } @else { {{ data()?.name }} }` })
22
+ * export class PokemonComponent {
23
+ * readonly result = useRequest<{ name: string }>("pokeapi", "pokemonById", { params: { id: 25 } });
24
+ * readonly data = this.result.data;
25
+ * readonly loading = this.result.loading;
26
+ * }
27
+ * ```
28
+ */
29
+ export function useRequest(apiName, endpointName, options) {
30
+ const destroyRef = inject(DestroyRef);
31
+ const data = signal(null);
32
+ const error = signal(null);
33
+ const loading = signal(true);
34
+ let version = 0;
35
+ let controller = null;
36
+ let disposed = false;
37
+ const refetch = async () => {
38
+ const current = ++version;
39
+ controller?.abort();
40
+ const next = new AbortController();
41
+ controller = next;
42
+ loading.set(true);
43
+ error.set(null);
44
+ const result = await useFetch(apiName, endpointName, {
45
+ method: "GET",
46
+ urlOptions: options,
47
+ signal: next.signal,
48
+ });
49
+ if (disposed || current !== version)
50
+ return;
51
+ if (result.ok) {
52
+ data.set(result.data);
53
+ error.set(null);
54
+ }
55
+ else if (!next.signal.aborted) {
56
+ error.set(result.error);
57
+ }
58
+ loading.set(false);
59
+ };
60
+ destroyRef.onDestroy(() => {
61
+ disposed = true;
62
+ controller?.abort();
63
+ });
64
+ void refetch();
65
+ return { data, error, loading, refetch };
66
+ }
67
+ //# sourceMappingURL=fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../../src/adapters/angular/fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAe,MAAM,EAAE,MAAM,eAAe,CAAC;AAExE,OAAO,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AAW/D;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,UAAU,CACzB,OAAe,EACf,YAAoB,EACpB,OAAoB;IAEpB,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IAEtC,MAAM,IAAI,GAAG,MAAM,CAAW,IAAI,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,MAAM,CAAkB,IAAI,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAE7B,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,UAAU,GAA2B,IAAI,CAAC;IAC9C,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,MAAM,OAAO,GAAG,KAAK,IAAmB,EAAE;QACzC,MAAM,OAAO,GAAG,EAAE,OAAO,CAAC;QAC1B,UAAU,EAAE,KAAK,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;QACnC,UAAU,GAAG,IAAI,CAAC;QAElB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClB,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAEhB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAI,OAAO,EAAE,YAAY,EAAE;YACvD,MAAM,EAAE,KAAK;YACb,UAAU,EAAE,OAAO;YACnB,MAAM,EAAE,IAAI,CAAC,MAAM;SACnB,CAAC,CAAC;QAEH,IAAI,QAAQ,IAAI,OAAO,KAAK,OAAO;YAAE,OAAO;QAE5C,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACtB,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjB,CAAC;aAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACjC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC,CAAC;IAEF,UAAU,CAAC,SAAS,CAAC,GAAG,EAAE;QACzB,QAAQ,GAAG,IAAI,CAAC;QAChB,UAAU,EAAE,KAAK,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,KAAK,OAAO,EAAE,CAAC;IAEf,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from "./fetch.js";
2
+ export * from "./query.js";
3
+ export * from "./watch.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/angular/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from "./fetch.js";
2
+ export * from "./query.js";
3
+ export * from "./watch.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/adapters/angular/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,107 @@
1
+ import { type Signal } from "@angular/core";
2
+ import { type QueryClient, type QueryKey, type QueryState } from "../../core/services/query.service.js";
3
+ import type { ApiError, FetchResult } from "../../types/index.js";
4
+ /** Configuration for `useQuery` (Angular signal factory). */
5
+ export interface UseQueryConfig<T = unknown> {
6
+ /** Cache key (stable array). */
7
+ queryKey: QueryKey;
8
+ /** Fetcher function returning a Safe Result. */
9
+ queryFn: () => Promise<FetchResult<T>>;
10
+ /** Time in ms before cached data is stale (default: 0). */
11
+ staleTime?: number;
12
+ /** Time in ms to keep unused data (default: 5 min). */
13
+ cacheTime?: number;
14
+ /** Retry count on failure (default: 3). */
15
+ retry?: number;
16
+ /** Base delay for exponential backoff (default: 1000). */
17
+ retryDelay?: number;
18
+ /** Whether the query is enabled (default: true). */
19
+ enabled?: boolean;
20
+ }
21
+ /** Reactive return value of `useQuery` (Angular signals). */
22
+ export interface UseQueryReturn<T = unknown> {
23
+ data: Signal<T | null>;
24
+ error: Signal<ApiError | null>;
25
+ isLoading: Signal<boolean>;
26
+ isSuccess: Signal<boolean>;
27
+ isError: Signal<boolean>;
28
+ isStale: Signal<boolean>;
29
+ status: Signal<QueryState<T>["status"]>;
30
+ /** Manually refetch the query. */
31
+ refetch: () => Promise<void>;
32
+ }
33
+ /** Configuration for `useMutation`. */
34
+ export interface UseMutationConfig<TData = unknown, TVariables = unknown> {
35
+ mutationFn: (variables: TVariables) => Promise<FetchResult<TData>>;
36
+ onSuccess?: (data: TData, variables: TVariables) => void;
37
+ onError?: (error: ApiError, variables: TVariables) => void;
38
+ onSettled?: (data: TData | null, error: ApiError | null, variables: TVariables) => void;
39
+ }
40
+ /** Reactive return value of `useMutation`. */
41
+ export interface UseMutationReturn<TData = unknown, TVariables = unknown> {
42
+ data: Signal<TData | null>;
43
+ error: Signal<ApiError | null>;
44
+ isLoading: Signal<boolean>;
45
+ isSuccess: Signal<boolean>;
46
+ isError: Signal<boolean>;
47
+ status: Signal<"idle" | "loading" | "success" | "error">;
48
+ /** Execute the mutation. */
49
+ mutate: (variables: TVariables) => Promise<void>;
50
+ /** Reset the mutation state. */
51
+ reset: () => void;
52
+ }
53
+ /**
54
+ * Angular signal factory that wraps the {@link QueryClient}.
55
+ * Integrates with the API manager — your `queryFn` typically calls `useGetApi`
56
+ * or `useFetch` and returns the Safe Result.
57
+ *
58
+ * Must be called within an injection context (component field initializer,
59
+ * constructor, or a service) so the returned query can auto-cleanup via
60
+ * `DestroyRef`. Requires Angular 16+ signals.
61
+ *
62
+ * @param config - Query configuration.
63
+ * @param client - Optional QueryClient instance (uses global singleton by default).
64
+ * @returns Signals `{ data, error, isLoading, isSuccess, isError, isStale, status }` plus `refetch`.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * import { Component, inject } from "@angular/core";
69
+ * import { useQuery } from "katanakit-js/adapters/angular";
70
+ * import { useGetApi } from "katanakit-js";
71
+ *
72
+ * @Component({ selector: "app-pokemon", template: `@if (isLoading()) { Loading… } @else { {{ data()?.name }} }` })
73
+ * export class PokemonComponent {
74
+ * readonly data = useQuery({ queryKey: ["pokemon", 25], queryFn: () => useGetApi("pokeapi", "pokemonById", { params: { id: 25 } }) }).data;
75
+ * }
76
+ * ```
77
+ */
78
+ export declare function useQuery<T>(config: UseQueryConfig<T>, client?: QueryClient): UseQueryReturn<T>;
79
+ /**
80
+ * Angular signal factory for mutations (POST/PUT/PATCH/DELETE).
81
+ * Does not cache — returns a `mutate` function that executes the mutation.
82
+ *
83
+ * Must be called within an injection context. Requires Angular 16+ signals.
84
+ *
85
+ * @param config - Mutation configuration.
86
+ * @returns Signals plus `{ mutate, reset }`.
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * import { Component } from "@angular/core";
91
+ * import { useMutation, useQueryClient } from "katanakit-js/adapters/angular";
92
+ * import { usePost } from "katanakit-js";
93
+ *
94
+ * @Component({ selector: "app-create", template: `<button [disabled]="isLoading()" (click)="mutate('Ada')">Create</button>` })
95
+ * export class CreateUserComponent {
96
+ * private readonly qc = useQueryClient();
97
+ * readonly result = useMutation({
98
+ * mutationFn: (name: string) => usePost("api", "createUser", { name }),
99
+ * onSuccess: () => this.qc.invalidateQueries({ queryKey: ["users"] }),
100
+ * });
101
+ * readonly isLoading = this.result.isLoading;
102
+ * readonly mutate = this.result.mutate;
103
+ * }
104
+ * ```
105
+ */
106
+ export declare function useMutation<TData = unknown, TVariables = unknown>(config: UseMutationConfig<TData, TVariables>): UseMutationReturn<TData, TVariables>;
107
+ //# sourceMappingURL=query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../src/adapters/angular/query.ts"],"names":[],"mappings":"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"}