@trokky/client 2.0.3

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 (86) hide show
  1. package/README.md +833 -0
  2. package/dist/cache/manager.d.ts +47 -0
  3. package/dist/cache/manager.d.ts.map +1 -0
  4. package/dist/cache/manager.js +100 -0
  5. package/dist/cache/manager.js.map +1 -0
  6. package/dist/cli/index.d.ts +7 -0
  7. package/dist/cli/index.d.ts.map +1 -0
  8. package/dist/cli/index.js +159 -0
  9. package/dist/cli/index.js.map +1 -0
  10. package/dist/client.d.ts +221 -0
  11. package/dist/client.d.ts.map +1 -0
  12. package/dist/client.js +310 -0
  13. package/dist/client.js.map +1 -0
  14. package/dist/document/client.d.ts +57 -0
  15. package/dist/document/client.d.ts.map +1 -0
  16. package/dist/document/client.js +176 -0
  17. package/dist/document/client.js.map +1 -0
  18. package/dist/generator/document-generator.d.ts +50 -0
  19. package/dist/generator/document-generator.d.ts.map +1 -0
  20. package/dist/generator/document-generator.js +329 -0
  21. package/dist/generator/document-generator.js.map +1 -0
  22. package/dist/generator/index.d.ts +121 -0
  23. package/dist/generator/index.d.ts.map +1 -0
  24. package/dist/generator/index.js +559 -0
  25. package/dist/generator/index.js.map +1 -0
  26. package/dist/http/client.d.ts +117 -0
  27. package/dist/http/client.d.ts.map +1 -0
  28. package/dist/http/client.js +411 -0
  29. package/dist/http/client.js.map +1 -0
  30. package/dist/index.d.ts +22 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +21 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/media/helper.d.ts +89 -0
  35. package/dist/media/helper.d.ts.map +1 -0
  36. package/dist/media/helper.js +197 -0
  37. package/dist/media/helper.js.map +1 -0
  38. package/dist/media/url-builder.d.ts +142 -0
  39. package/dist/media/url-builder.d.ts.map +1 -0
  40. package/dist/media/url-builder.js +280 -0
  41. package/dist/media/url-builder.js.map +1 -0
  42. package/dist/query/builder.d.ts +270 -0
  43. package/dist/query/builder.d.ts.map +1 -0
  44. package/dist/query/builder.js +731 -0
  45. package/dist/query/builder.js.map +1 -0
  46. package/dist/query/index.d.ts +6 -0
  47. package/dist/query/index.d.ts.map +1 -0
  48. package/dist/query/index.js +5 -0
  49. package/dist/query/index.js.map +1 -0
  50. package/dist/react/hooks.d.ts +42 -0
  51. package/dist/react/hooks.d.ts.map +1 -0
  52. package/dist/react/hooks.js +59 -0
  53. package/dist/react/hooks.js.map +1 -0
  54. package/dist/react/index.d.ts +6 -0
  55. package/dist/react/index.d.ts.map +1 -0
  56. package/dist/react/index.js +6 -0
  57. package/dist/react/index.js.map +1 -0
  58. package/dist/server/index.d.ts +9 -0
  59. package/dist/server/index.d.ts.map +1 -0
  60. package/dist/server/index.js +8 -0
  61. package/dist/server/index.js.map +1 -0
  62. package/dist/server/media-proxy.d.ts +102 -0
  63. package/dist/server/media-proxy.d.ts.map +1 -0
  64. package/dist/server/media-proxy.js +160 -0
  65. package/dist/server/media-proxy.js.map +1 -0
  66. package/dist/shortcodes/index.d.ts +11 -0
  67. package/dist/shortcodes/index.d.ts.map +1 -0
  68. package/dist/shortcodes/index.js +10 -0
  69. package/dist/shortcodes/index.js.map +1 -0
  70. package/dist/shortcodes/parser.d.ts +32 -0
  71. package/dist/shortcodes/parser.d.ts.map +1 -0
  72. package/dist/shortcodes/parser.js +88 -0
  73. package/dist/shortcodes/parser.js.map +1 -0
  74. package/dist/shortcodes/resolver.d.ts +57 -0
  75. package/dist/shortcodes/resolver.d.ts.map +1 -0
  76. package/dist/shortcodes/resolver.js +97 -0
  77. package/dist/shortcodes/resolver.js.map +1 -0
  78. package/dist/shortcodes/types.d.ts +17 -0
  79. package/dist/shortcodes/types.d.ts.map +1 -0
  80. package/dist/shortcodes/types.js +6 -0
  81. package/dist/shortcodes/types.js.map +1 -0
  82. package/dist/types/index.d.ts +134 -0
  83. package/dist/types/index.d.ts.map +1 -0
  84. package/dist/types/index.js +6 -0
  85. package/dist/types/index.js.map +1 -0
  86. package/package.json +101 -0
package/README.md ADDED
@@ -0,0 +1,833 @@
1
+ # @trokky/client
2
+
3
+ **TypeScript-native client SDK for Trokky CMS with automatic type generation**
4
+
5
+ The official client library for interacting with Trokky CMS APIs. Features comprehensive TypeScript support, automatic type generation from schemas, smart caching, and framework-agnostic design.
6
+
7
+ ## Features
8
+
9
+ - 🔥 **TypeScript Native** - Full type safety with automatic type generation from schemas
10
+ - 🚀 **Framework Agnostic** - Works with React, Vue, Svelte, Node.js, and serverless
11
+ - 🔐 **Authentication Built-in** - JWT token management with automatic refresh
12
+ - ⚡ **Smart Caching** - TTL-based caching with intelligent invalidation
13
+ - 🛡️ **Error Handling** - Comprehensive error handling with retry logic
14
+ - 📁 **Media Management** - File upload support with progress tracking
15
+ - 🔍 **Advanced Querying** - Filtering, sorting, pagination, and search
16
+ - 🧪 **Well Tested** - 88 comprehensive tests covering all functionality
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @trokky/client
22
+ ```
23
+
24
+ ## Quick Start
25
+
26
+ ### Basic Setup
27
+
28
+ ```typescript
29
+ import { TrokkyClient } from '@trokky/client'
30
+
31
+ const client = new TrokkyClient({
32
+ baseUrl: 'https://your-trokky-api.com',
33
+ apiVersion: 'v1'
34
+ })
35
+
36
+ // Authenticate
37
+ await client.authenticate({
38
+ username: 'user@example.com',
39
+ password: 'your-password'
40
+ })
41
+
42
+ // Use the client
43
+ const posts = await client.queryDocuments('post', {
44
+ filter: { published: true },
45
+ sort: { createdAt: -1 },
46
+ limit: 10
47
+ })
48
+ ```
49
+
50
+ ### TypeScript Type Generation
51
+
52
+ Generate TypeScript types from your Trokky schemas using the CLI or programmatically.
53
+
54
+ #### CLI Usage
55
+
56
+ ```bash
57
+ # From local compiled schemas (recommended for development)
58
+ npx trokky-client generate-types --schema-path ../cms/dist/schemas -o ./src/types/cms
59
+
60
+ # From remote API (requires running CMS server)
61
+ npx trokky-client generate-types \
62
+ --schema-url http://localhost:3000/api/collections \
63
+ --auth-token YOUR_API_TOKEN \
64
+ -o ./src/types/cms
65
+ ```
66
+
67
+ **CLI Options:**
68
+ | Option | Description |
69
+ |--------|-------------|
70
+ | `-p, --schema-path <path>` | Local path to compiled schemas directory |
71
+ | `-u, --schema-url <url>` | API URL to fetch schemas from |
72
+ | `-o, --output-dir <dir>` | Output directory (default: `./src/types/trokky`) |
73
+ | `-t, --auth-token <token>` | API token for authentication |
74
+ | `-n, --namespace <name>` | TypeScript namespace (default: `Trokky`) |
75
+ | `-e, --extension <ext>` | File extension: `ts` or `d.ts` (default: `ts`) |
76
+ | `--no-validation` | Skip validation schema generation |
77
+
78
+ > **Note:** The `--schema-path` option requires compiled JS files. Run `npm run build` in your CMS project first.
79
+
80
+ #### Programmatic Usage
81
+
82
+ ```typescript
83
+ import { generateTypes, generateTypesFromPath } from '@trokky/client/generator'
84
+
85
+ // From local schemas
86
+ await generateTypesFromPath({
87
+ schemaPath: '../cms/dist/schemas',
88
+ outputDir: './src/types/cms',
89
+ namespace: 'Trokky',
90
+ includeValidation: true
91
+ })
92
+
93
+ // From remote API
94
+ await generateTypes({
95
+ schemaUrl: 'https://your-api.com/api/collections',
96
+ outputDir: './src/types/trokky',
97
+ authToken: 'your-token',
98
+ includeValidation: true
99
+ })
100
+ ```
101
+
102
+ This generates TypeScript interfaces like:
103
+
104
+ ```typescript
105
+ // Generated types
106
+ export interface PostDocument extends BaseDocument {
107
+ _type: 'post'
108
+ title: string
109
+ content: string
110
+ slug: string
111
+ published?: boolean
112
+ author?: string | UserDocument
113
+ tags?: string[]
114
+ }
115
+ ```
116
+
117
+ ### Framework Examples
118
+
119
+ #### React Hook
120
+
121
+ ```typescript
122
+ import { useState, useEffect } from 'react'
123
+ import { TrokkyClient } from '@trokky/client'
124
+ import type { PostDocument } from './types/trokky'
125
+
126
+ const usePosts = (client: TrokkyClient) => {
127
+ const [posts, setPosts] = useState<PostDocument[]>([])
128
+ const [loading, setLoading] = useState(true)
129
+
130
+ useEffect(() => {
131
+ const fetchPosts = async () => {
132
+ try {
133
+ const result = await client.queryDocuments<PostDocument>('post', {
134
+ filter: { published: true },
135
+ sort: { createdAt: -1 }
136
+ })
137
+ setPosts(result.data.map(doc => doc.data))
138
+ } catch (error) {
139
+ console.error('Failed to fetch posts:', error)
140
+ } finally {
141
+ setLoading(false)
142
+ }
143
+ }
144
+
145
+ fetchPosts()
146
+ }, [client])
147
+
148
+ return { posts, loading }
149
+ }
150
+ ```
151
+
152
+ #### Vue Composable
153
+
154
+ ```typescript
155
+ import { ref, onMounted } from 'vue'
156
+ import { TrokkyClient } from '@trokky/client'
157
+ import type { PostDocument } from './types/trokky'
158
+
159
+ export const usePosts = (client: TrokkyClient) => {
160
+ const posts = ref<PostDocument[]>([])
161
+ const loading = ref(true)
162
+
163
+ const fetchPosts = async () => {
164
+ try {
165
+ const result = await client.queryDocuments<PostDocument>('post', {
166
+ filter: { published: true },
167
+ sort: { createdAt: -1 }
168
+ })
169
+ posts.value = result.data.map(doc => doc.data)
170
+ } catch (error) {
171
+ console.error('Failed to fetch posts:', error)
172
+ } finally {
173
+ loading.value = false
174
+ }
175
+ }
176
+
177
+ onMounted(fetchPosts)
178
+
179
+ return { posts, loading, refetch: fetchPosts }
180
+ }
181
+ ```
182
+
183
+ #### Svelte Store
184
+
185
+ ```typescript
186
+ import { writable } from 'svelte/store'
187
+ import { TrokkyClient } from '@trokky/client'
188
+ import type { PostDocument } from './types/trokky'
189
+
190
+ const createPostStore = (client: TrokkyClient) => {
191
+ const { subscribe, set, update } = writable<PostDocument[]>([])
192
+
193
+ return {
194
+ subscribe,
195
+ async load() {
196
+ const result = await client.queryDocuments<PostDocument>('post', {
197
+ filter: { published: true },
198
+ sort: { createdAt: -1 }
199
+ })
200
+ set(result.data.map(doc => doc.data))
201
+ },
202
+ async create(post: Omit<PostDocument, '_id' | '_type' | '_createdAt' | '_updatedAt' | '_version'>) {
203
+ const newPost = await client.createDocument('post', post)
204
+ update(posts => [newPost.data, ...posts])
205
+ return newPost
206
+ }
207
+ }
208
+ }
209
+
210
+ export const posts = createPostStore(client)
211
+ ```
212
+
213
+ ## API Reference
214
+
215
+ ### TrokkyClient
216
+
217
+ The main client class for interacting with Trokky APIs.
218
+
219
+ #### Constructor
220
+
221
+ ```typescript
222
+ new TrokkyClient(config: ClientConfig)
223
+ ```
224
+
225
+ ```typescript
226
+ interface ClientConfig {
227
+ baseUrl: string
228
+ apiVersion?: string
229
+ token?: string
230
+ refreshToken?: string
231
+ timeout?: number
232
+ retries?: number
233
+ enableCache?: boolean
234
+ cacheMaxAge?: number
235
+ debug?: boolean
236
+ }
237
+ ```
238
+
239
+ #### Authentication Methods
240
+
241
+ ```typescript
242
+ // Authenticate with credentials
243
+ await client.authenticate({ username: 'user@example.com', password: 'password' })
244
+
245
+ // Check authentication status
246
+ client.isAuthenticated() // boolean
247
+
248
+ // Get current tokens
249
+ client.getTokens() // AuthTokens | null
250
+
251
+ // Set tokens manually
252
+ client.setTokens({ accessToken: 'token', refreshToken: 'refresh' })
253
+
254
+ // Logout
255
+ await client.logout()
256
+ ```
257
+
258
+ #### Document Methods
259
+
260
+ ```typescript
261
+ // Get document by ID
262
+ const post = await client.getDocument<PostDocument>('post', 'post-id')
263
+
264
+ // Get document with reference expansion
265
+ const post = await client.getDocument<PostDocument>('post', 'post-id', {
266
+ expand: 'author,categories[]'
267
+ })
268
+
269
+ // Query documents
270
+ const posts = await client.queryDocuments<PostDocument>('post', {
271
+ filter: { published: true },
272
+ sort: { createdAt: -1 },
273
+ limit: 10,
274
+ offset: 0
275
+ })
276
+
277
+ // Create document
278
+ const newPost = await client.createDocument('post', {
279
+ title: 'New Post',
280
+ content: 'Post content...',
281
+ published: false
282
+ })
283
+
284
+ // Update document
285
+ const updated = await client.updateDocument('post', 'post-id', {
286
+ title: 'Updated Title'
287
+ })
288
+
289
+ // Replace document
290
+ const replaced = await client.replaceDocument('post', 'post-id', {
291
+ title: 'New Title',
292
+ content: 'New content...',
293
+ published: true
294
+ })
295
+
296
+ // Delete document
297
+ await client.deleteDocument('post', 'post-id')
298
+
299
+ // Search documents
300
+ const results = await client.searchDocuments('post', 'search term', ['title', 'content'])
301
+
302
+ // Count documents
303
+ const count = await client.countDocuments('post', { published: true })
304
+
305
+ // Check if document exists
306
+ const exists = await client.documentExists('post', 'post-id')
307
+ ```
308
+
309
+ #### Media Methods
310
+
311
+ ```typescript
312
+ // Upload file
313
+ const media = await client.uploadFile(file, 'filename.jpg')
314
+
315
+ // Get media
316
+ const media = await client.getMedia('media-id')
317
+
318
+ // Delete media
319
+ await client.deleteMedia('media-id')
320
+ ```
321
+
322
+ #### Utility Methods
323
+
324
+ ```typescript
325
+ // Test API connection
326
+ await client.ping()
327
+
328
+ // Get API health status
329
+ await client.health()
330
+
331
+ // Clear cache
332
+ client.clearCache()
333
+
334
+ // Get cache stats
335
+ client.getCacheStats()
336
+
337
+ // Cleanup resources
338
+ client.destroy()
339
+ ```
340
+
341
+ ### Query Options
342
+
343
+ ```typescript
344
+ interface QueryOptions {
345
+ filter?: Record<string, any>
346
+ sort?: Record<string, 1 | -1>
347
+ limit?: number
348
+ offset?: number
349
+ select?: string[]
350
+ }
351
+
352
+ // Examples
353
+ const options: QueryOptions = {
354
+ filter: {
355
+ published: true,
356
+ author: 'user-id',
357
+ tags: { $in: ['tech', 'programming'] }
358
+ },
359
+ sort: { createdAt: -1, title: 1 },
360
+ limit: 20,
361
+ offset: 0,
362
+ select: ['title', 'slug', 'author']
363
+ }
364
+ ```
365
+
366
+ ### Type Generation
367
+
368
+ Generate TypeScript types from your Trokky schemas:
369
+
370
+ ```typescript
371
+ import { generateTypes, generateTypesFromSchema } from '@trokky/client/generator'
372
+
373
+ // Generate from URL
374
+ await generateTypes({
375
+ schemaUrl: 'https://api.example.com/schema',
376
+ outputDir: './src/types',
377
+ namespace: 'Trokky',
378
+ fileExtension: 'ts', // or 'd.ts'
379
+ includeValidation: true
380
+ })
381
+
382
+ // Generate from schema object
383
+ await generateTypesFromSchema(schema, {
384
+ outputDir: './src/types',
385
+ namespace: 'MyApp'
386
+ })
387
+ ```
388
+
389
+ ## Advanced Usage
390
+
391
+ ### Reference Expansion
392
+
393
+ Expand reference fields to include full document data instead of just reference IDs. References are resolved and the full document replaces the reference object.
394
+
395
+ #### Before Expansion (Reference)
396
+
397
+ ```json
398
+ {
399
+ "_id": "homepage",
400
+ "documentation": {
401
+ "featuredDocument": {
402
+ "_ref": "doc-legal-legalText-abc123",
403
+ "_type": "document"
404
+ }
405
+ }
406
+ }
407
+ ```
408
+
409
+ #### After Expansion (Resolved Document)
410
+
411
+ ```json
412
+ {
413
+ "_id": "homepage",
414
+ "documentation": {
415
+ "featuredDocument": {
416
+ "_id": "doc-legal-legalText-abc123",
417
+ "_collection": "document",
418
+ "title": "Terms of Service",
419
+ "slug": "terms-of-service",
420
+ "content": "...",
421
+ "_status": "published"
422
+ }
423
+ }
424
+ }
425
+ ```
426
+
427
+ #### Basic Expansion
428
+
429
+ ```typescript
430
+ // Direct API - expand specific fields
431
+ const post = await client.getDocument<PostDocument>('post', 'post-id', {
432
+ expand: 'author,categories[]' // Use [] suffix for array references
433
+ })
434
+
435
+ // Expand all reference fields
436
+ const post = await client.getDocument<PostDocument>('post', 'post-id', {
437
+ expand: '*'
438
+ })
439
+ ```
440
+
441
+ #### Fluent Query Builder
442
+
443
+ ```typescript
444
+ // Query collection with reference expansion
445
+ const posts = await client
446
+ .from('post')
447
+ .published()
448
+ .expand('author') // Single reference field
449
+ .expand('categories[]') // Array reference field (use [] suffix)
450
+ .sort({ _createdAt: 'desc' })
451
+ .limit(10)
452
+ .fetch()
453
+ ```
454
+
455
+ #### Singleton with Expansion
456
+
457
+ ```typescript
458
+ // Fetch singleton with expanded references
459
+ const homepage = await client
460
+ .singleton('homepage')
461
+ .expand('hero.backgroundImage') // Nested single reference
462
+ .expand('documentation.featuredDocument') // Nested single reference
463
+ .expand('sections.relatedArticles[]') // Nested array reference
464
+ .fetch()
465
+ ```
466
+
467
+ #### Nested Field Paths
468
+
469
+ The `expand()` method supports dot notation for nested fields:
470
+
471
+ ```typescript
472
+ // Top-level field
473
+ .expand('author')
474
+
475
+ // Nested field (one level deep)
476
+ .expand('content.featuredImage')
477
+
478
+ // Deeply nested field
479
+ .expand('sections.hero.backgroundImage')
480
+
481
+ // Multiple nested expansions
482
+ const homepage = await client
483
+ .singleton('homepage')
484
+ .expand('hero.backgroundImage')
485
+ .expand('documentation.featuredDocument')
486
+ .expand('footer.socialLinks[]')
487
+ .fetch()
488
+
489
+ // Access the expanded data directly
490
+ console.log(homepage.documentation.featuredDocument.title)
491
+ // Output: "Terms of Service"
492
+ ```
493
+
494
+ #### Expand Parameter Formats
495
+
496
+ | Format | Description |
497
+ |--------|-------------|
498
+ | `author` | Expand single reference field |
499
+ | `categories[]` | Expand array reference field |
500
+ | `content.author` | Expand nested single reference |
501
+ | `sections.items[]` | Expand nested array reference |
502
+ | `a.b.c` | Expand deeply nested reference |
503
+ | `author,tags[]` | Expand multiple fields |
504
+ | `*` | Expand all reference fields |
505
+
506
+ ### Frontend Integration Examples
507
+
508
+ #### Next.js App Router
509
+
510
+ ```typescript
511
+ // app/lib/trokky.ts
512
+ import { TrokkyClient } from '@trokky/client'
513
+
514
+ export const trokky = new TrokkyClient({
515
+ baseUrl: process.env.TROKKY_API_URL || 'http://localhost:3000/api'
516
+ })
517
+
518
+ // app/lib/queries.ts
519
+ export async function getHomepage() {
520
+ return trokky
521
+ .singleton('homepage')
522
+ .expand('documentation.featuredDocument')
523
+ .expand('hero.backgroundImage')
524
+ .fetch()
525
+ }
526
+
527
+ export async function getPosts() {
528
+ return trokky
529
+ .from('post')
530
+ .published()
531
+ .expand('author')
532
+ .expand('category')
533
+ .newest()
534
+ .limit(10)
535
+ .fetch()
536
+ }
537
+
538
+ // app/page.tsx
539
+ import { getHomepage } from './lib/queries'
540
+
541
+ export default async function HomePage() {
542
+ const homepage = await getHomepage()
543
+
544
+ return (
545
+ <div>
546
+ <h1>{homepage.hero.title}</h1>
547
+
548
+ {/* Access expanded nested reference directly */}
549
+ {homepage.documentation.featuredDocument && (
550
+ <div className="featured">
551
+ <h2>{homepage.documentation.featuredDocument.title}</h2>
552
+ <p>{homepage.documentation.featuredDocument.description}</p>
553
+ </div>
554
+ )}
555
+ </div>
556
+ )
557
+ }
558
+ ```
559
+
560
+ #### React with React Query
561
+
562
+ ```typescript
563
+ // hooks/useTrokky.ts
564
+ import { useQuery } from '@tanstack/react-query'
565
+ import { TrokkyClient } from '@trokky/client'
566
+
567
+ const client = new TrokkyClient({
568
+ baseUrl: 'http://localhost:3000/api'
569
+ })
570
+
571
+ export function useHomepage() {
572
+ return useQuery({
573
+ queryKey: ['homepage'],
574
+ queryFn: () =>
575
+ client
576
+ .singleton('homepage')
577
+ .expand('documentation.featuredDocument')
578
+ .expand('hero.backgroundImage')
579
+ .fetch()
580
+ })
581
+ }
582
+
583
+ export function usePosts() {
584
+ return useQuery({
585
+ queryKey: ['posts'],
586
+ queryFn: () =>
587
+ client
588
+ .from('post')
589
+ .published()
590
+ .expand('author')
591
+ .newest()
592
+ .limit(10)
593
+ .fetch()
594
+ })
595
+ }
596
+
597
+ // components/HomePage.tsx
598
+ import { useHomepage } from '../hooks/useTrokky'
599
+
600
+ export function HomePage() {
601
+ const { data: homepage, isLoading, error } = useHomepage()
602
+
603
+ if (isLoading) return <div>Loading...</div>
604
+ if (error) return <div>Error loading page</div>
605
+ if (!homepage) return null
606
+
607
+ const featuredDoc = homepage.documentation?.featuredDocument
608
+
609
+ return (
610
+ <main>
611
+ <h1>{homepage.hero?.title}</h1>
612
+
613
+ {featuredDoc && (
614
+ <article>
615
+ <h2>{featuredDoc.title}</h2>
616
+ <p>{featuredDoc.excerpt}</p>
617
+ <a href={`/docs/${featuredDoc.slug}`}>Read more</a>
618
+ </article>
619
+ )}
620
+ </main>
621
+ )
622
+ }
623
+ ```
624
+
625
+ #### Vue 3 Composition API
626
+
627
+ ```typescript
628
+ // composables/useTrokky.ts
629
+ import { ref, onMounted } from 'vue'
630
+ import { TrokkyClient } from '@trokky/client'
631
+
632
+ const client = new TrokkyClient({
633
+ baseUrl: 'http://localhost:3000/api'
634
+ })
635
+
636
+ export function useHomepage() {
637
+ const homepage = ref(null)
638
+ const loading = ref(true)
639
+ const error = ref(null)
640
+
641
+ onMounted(async () => {
642
+ try {
643
+ homepage.value = await client
644
+ .singleton('homepage')
645
+ .expand('documentation.featuredDocument')
646
+ .expand('hero.backgroundImage')
647
+ .fetch()
648
+ } catch (e) {
649
+ error.value = e
650
+ } finally {
651
+ loading.value = false
652
+ }
653
+ })
654
+
655
+ return { homepage, loading, error }
656
+ }
657
+
658
+ // components/HomePage.vue
659
+ <script setup lang="ts">
660
+ import { useHomepage } from '../composables/useTrokky'
661
+
662
+ const { homepage, loading, error } = useHomepage()
663
+ </script>
664
+
665
+ <template>
666
+ <div v-if="loading">Loading...</div>
667
+ <div v-else-if="error">Error: {{ error.message }}</div>
668
+ <main v-else-if="homepage">
669
+ <h1>{{ homepage.hero?.title }}</h1>
670
+
671
+ <article v-if="homepage.documentation?.featuredDocument">
672
+ <h2>{{ homepage.documentation.featuredDocument.title }}</h2>
673
+ <p>{{ homepage.documentation.featuredDocument.excerpt }}</p>
674
+ </article>
675
+ </main>
676
+ </template>
677
+ ```
678
+
679
+ #### TypeScript Types with Expansion
680
+
681
+ When using TypeScript, you can define types that reflect expanded references:
682
+
683
+ ```typescript
684
+ // types/cms.ts
685
+ import type { BaseDocument } from '@trokky/client'
686
+
687
+ interface DocumentReference {
688
+ _ref: string
689
+ _type: string
690
+ }
691
+
692
+ interface LegalDocument extends BaseDocument {
693
+ title: string
694
+ slug: string
695
+ content: string
696
+ category: string
697
+ }
698
+
699
+ // Before expansion: reference object
700
+ interface HomepageRaw extends BaseDocument {
701
+ documentation: {
702
+ featuredDocument: DocumentReference
703
+ }
704
+ }
705
+
706
+ // After expansion: full document
707
+ interface HomepageExpanded extends BaseDocument {
708
+ documentation: {
709
+ featuredDocument: LegalDocument
710
+ }
711
+ }
712
+
713
+ // Usage with explicit expanded type
714
+ const homepage = await client
715
+ .singleton<HomepageExpanded>('homepage')
716
+ .expand('documentation.featuredDocument')
717
+ .fetch()
718
+
719
+ // TypeScript knows featuredDocument has title, slug, etc.
720
+ console.log(homepage.documentation.featuredDocument.title)
721
+ ```
722
+
723
+ ### Custom HTTP Client
724
+
725
+ Access the underlying HTTP client for advanced usage:
726
+
727
+ ```typescript
728
+ const client = new TrokkyClient({ baseUrl: 'https://api.example.com' })
729
+
730
+ // Direct HTTP requests
731
+ const response = await client.http.get('/custom-endpoint')
732
+ const result = await client.http.post('/custom-endpoint', { data: 'value' })
733
+ ```
734
+
735
+ ### Cache Management
736
+
737
+ ```typescript
738
+ // Configure caching
739
+ const client = new TrokkyClient({
740
+ baseUrl: 'https://api.example.com',
741
+ enableCache: true,
742
+ cacheMaxAge: 300000 // 5 minutes
743
+ })
744
+
745
+ // Cache-specific operations
746
+ client.clearCache()
747
+ const stats = client.getCacheStats()
748
+
749
+ // Disable cache for specific requests
750
+ const freshData = await client.queryDocuments('post', {}, false) // useCache = false
751
+ ```
752
+
753
+ ### Error Handling
754
+
755
+ ```typescript
756
+ import type { ApiError } from '@trokky/client'
757
+
758
+ try {
759
+ await client.getDocument('post', 'invalid-id')
760
+ } catch (error) {
761
+ if (error.status === 404) {
762
+ console.log('Document not found')
763
+ } else if (error.status === 401) {
764
+ console.log('Authentication required')
765
+ await client.authenticate(credentials)
766
+ } else {
767
+ console.error('API Error:', error.message)
768
+ if (error.validation) {
769
+ console.log('Validation errors:', error.validation)
770
+ }
771
+ }
772
+ }
773
+ ```
774
+
775
+ ## Configuration
776
+
777
+ ### Environment Variables
778
+
779
+ ```bash
780
+ # API Configuration
781
+ TROKKY_API_URL=https://your-api.com
782
+ TROKKY_API_VERSION=v1
783
+
784
+ # Authentication
785
+ TROKKY_TOKEN=your-jwt-token
786
+ TROKKY_REFRESH_TOKEN=your-refresh-token
787
+
788
+ # Performance
789
+ TROKKY_CACHE_ENABLED=true
790
+ TROKKY_CACHE_MAX_AGE=300000
791
+ TROKKY_REQUEST_TIMEOUT=30000
792
+ TROKKY_REQUEST_RETRIES=3
793
+ ```
794
+
795
+ ### Client Configuration
796
+
797
+ ```typescript
798
+ const client = new TrokkyClient({
799
+ baseUrl: process.env.TROKKY_API_URL || 'http://localhost:3000',
800
+ apiVersion: process.env.TROKKY_API_VERSION || 'v1',
801
+ token: process.env.TROKKY_TOKEN,
802
+ refreshToken: process.env.TROKKY_REFRESH_TOKEN,
803
+ timeout: parseInt(process.env.TROKKY_REQUEST_TIMEOUT || '30000'),
804
+ retries: parseInt(process.env.TROKKY_REQUEST_RETRIES || '3'),
805
+ enableCache: process.env.TROKKY_CACHE_ENABLED === 'true',
806
+ cacheMaxAge: parseInt(process.env.TROKKY_CACHE_MAX_AGE || '300000'),
807
+ debug: process.env.NODE_ENV === 'development'
808
+ })
809
+ ```
810
+
811
+ ## Examples
812
+
813
+ Check out complete examples in the [examples directory](./examples):
814
+
815
+ - [React Blog](./examples/react-blog) - Complete blog application with React
816
+ - [Vue Portfolio](./examples/vue-portfolio) - Portfolio site with Vue 3
817
+ - [Svelte News](./examples/svelte-news) - News site with SvelteKit
818
+ - [Node.js API](./examples/node-api) - Server-side usage with Express
819
+ - [Next.js App](./examples/nextjs-app) - Full-stack Next.js application
820
+
821
+ ## TypeScript Support
822
+
823
+ This package includes full TypeScript definitions and supports:
824
+
825
+ - Strict type checking
826
+ - Generic document types
827
+ - Auto-completion in IDEs
828
+ - Type-safe query building
829
+ - Generated types from schemas
830
+
831
+ ## License
832
+
833
+ MIT