@nexushub/client 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1105 -0
- package/dist/index.d.mts +356 -0
- package/dist/index.d.ts +356 -0
- package/dist/index.js +4301 -0
- package/dist/index.mjs +4281 -0
- package/package.json +76 -0
package/README.md
ADDED
|
@@ -0,0 +1,1105 @@
|
|
|
1
|
+
# NexusHub Client SDK
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@nexushub/client)
|
|
6
|
+
[](https://bundlephobia.com/package/@nexushub/client)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://reactjs.org/)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
|
|
11
|
+
**The God-Tier Headless CMS SDK** — Everything you need to build blazing-fast websites with real-time content, analytics, and authentication.
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
## ✨ Features
|
|
16
|
+
|
|
17
|
+
### 🚀 **Performance First**
|
|
18
|
+
|
|
19
|
+
- **Multi-layer caching**: Memory → LocalStorage → Remote with LRU eviction
|
|
20
|
+
- **Request optimization**: Batching, deduplication, exponential backoff
|
|
21
|
+
- **Smart preloading**: Automatic content prediction and prefetching
|
|
22
|
+
- **< 5ms** cache hits, **< 100ms** cold starts
|
|
23
|
+
|
|
24
|
+
### 📊 **Built-in Analytics Engine**
|
|
25
|
+
|
|
26
|
+
- **Real-time tracking**: Page views, clicks, form submissions
|
|
27
|
+
- **Web Vitals**: Core Web Vitals with RUM (Real User Monitoring)
|
|
28
|
+
- **Session management**: 30-minute sessions with automatic renewal
|
|
29
|
+
- **User identification**: Anonymous → Known user handshake
|
|
30
|
+
- **E-commerce ready**: Track purchases, cart events, conversions
|
|
31
|
+
|
|
32
|
+
### 🔐 **Authentication System**
|
|
33
|
+
|
|
34
|
+
- **Full auth flow**: Register, login, logout, social login (Google)
|
|
35
|
+
- **Protected routes**: React hooks for auth state
|
|
36
|
+
- **Session persistence**: HttpOnly cookies for security
|
|
37
|
+
- **Role-based access**: User roles and permissions
|
|
38
|
+
|
|
39
|
+
### 📁 **Content Engine**
|
|
40
|
+
|
|
41
|
+
- **Type-safe content**: Full TypeScript support with generics
|
|
42
|
+
- **Advanced querying**: Filter, sort, paginate, search, include
|
|
43
|
+
- **Real-time updates**: WebSocket/SSE subscriptions
|
|
44
|
+
- **Collection support**: Dynamic content with rich relationships
|
|
45
|
+
- **Singleton pages**: Static content with smart caching
|
|
46
|
+
|
|
47
|
+
### 🛠 **Developer Experience**
|
|
48
|
+
|
|
49
|
+
- **Zero-config setup**: Auto-detects environment variables
|
|
50
|
+
- **Local development**: Offline mode with local cache
|
|
51
|
+
- **CLI tool**: Pull, seed, deploy, and manage content
|
|
52
|
+
- **Debug mode**: Detailed logs and performance metrics
|
|
53
|
+
- **Hot reload**: Instant updates during development
|
|
54
|
+
|
|
55
|
+
## 🚀 Quick Start
|
|
56
|
+
|
|
57
|
+
### 1. Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install @nexushub/client
|
|
61
|
+
# or
|
|
62
|
+
yarn add @nexushub/client
|
|
63
|
+
# or
|
|
64
|
+
pnpm add @nexushub/client
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 2. Environment Setup
|
|
68
|
+
|
|
69
|
+
Create `.env.local` in your project root:
|
|
70
|
+
|
|
71
|
+
```env
|
|
72
|
+
# Required
|
|
73
|
+
NEXT_PUBLIC_NEXUS_ID=your_project_id_here
|
|
74
|
+
NEXT_PUBLIC_NEXUS_KEY=your_api_key_here
|
|
75
|
+
|
|
76
|
+
# Optional (defaults to https://api.nexushub.com/v1)
|
|
77
|
+
NEXT_PUBLIC_NEXUS_API_URL=https://api.nexushub.com/v1
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 3. Wrap Your App (Next.js/React)
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
// app/layout.tsx (Next.js App Router)
|
|
84
|
+
import { NexusProvider } from '@nexushub/client'
|
|
85
|
+
|
|
86
|
+
export default function RootLayout({
|
|
87
|
+
children,
|
|
88
|
+
}: {
|
|
89
|
+
children: React.ReactNode
|
|
90
|
+
}) {
|
|
91
|
+
return (
|
|
92
|
+
<html lang="en">
|
|
93
|
+
<body>
|
|
94
|
+
<NexusProvider>
|
|
95
|
+
{children}
|
|
96
|
+
</NexusProvider>
|
|
97
|
+
</body>
|
|
98
|
+
</html>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 4. Use Content Anywhere
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
// app/page.tsx
|
|
107
|
+
import { nexus } from '@nexushub/client'
|
|
108
|
+
|
|
109
|
+
export default async function HomePage() {
|
|
110
|
+
const homePage = await nexus.getPage('home')
|
|
111
|
+
const blogPosts = await nexus.content.getCollection('blog_posts', {
|
|
112
|
+
page: 1,
|
|
113
|
+
limit: 10,
|
|
114
|
+
sort: 'createdAt',
|
|
115
|
+
order: 'desc',
|
|
116
|
+
filter: { published: true }
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<main>
|
|
121
|
+
<h1>{homePage.title}</h1>
|
|
122
|
+
<p>{homePage.content}</p>
|
|
123
|
+
|
|
124
|
+
<h2>Latest Posts</h2>
|
|
125
|
+
<ul>
|
|
126
|
+
{blogPosts.items.map(post => (
|
|
127
|
+
<li key={post.id}>
|
|
128
|
+
<h3>{post.title}</h3>
|
|
129
|
+
<p>{post.excerpt}</p>
|
|
130
|
+
</li>
|
|
131
|
+
))}
|
|
132
|
+
</ul>
|
|
133
|
+
</main>
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## 📖 Documentation
|
|
139
|
+
|
|
140
|
+
### Table of Contents
|
|
141
|
+
|
|
142
|
+
1. [Configuration](#configuration)
|
|
143
|
+
2. [Content Fetching](#content-fetching)
|
|
144
|
+
3. [Authentication](#authentication)
|
|
145
|
+
4. [Analytics](#analytics)
|
|
146
|
+
5. [Caching](#caching)
|
|
147
|
+
6. [CLI Tool](#cli-tool)
|
|
148
|
+
7. [API Reference](#api-reference)
|
|
149
|
+
8. [Examples](#examples)
|
|
150
|
+
9. [Troubleshooting](#troubleshooting)
|
|
151
|
+
|
|
152
|
+
## 🔧 Configuration
|
|
153
|
+
|
|
154
|
+
### Auto Configuration (Recommended)
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// Uses environment variables automatically
|
|
158
|
+
const client = nexus // Singleton instance
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Manual Configuration
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { createNexusClient } from '@nexushub/client'
|
|
165
|
+
|
|
166
|
+
const client = createNexusClient({
|
|
167
|
+
projectId: 'your-project-id',
|
|
168
|
+
apiKey: 'your-api-key',
|
|
169
|
+
apiUrl: 'https://api.nexushub.com/v1', // Optional
|
|
170
|
+
debug: true, // Enable debug logging
|
|
171
|
+
cacheStrategy: 'memory', // 'memory' | 'localStorage' | 'none'
|
|
172
|
+
revalidateTime: 60, // Cache revalidation in seconds
|
|
173
|
+
timeout: 10000, // Request timeout in ms
|
|
174
|
+
retries: 3, // Number of retry attempts
|
|
175
|
+
})
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### React Configuration
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
// app/NexusWrapper.tsx
|
|
182
|
+
'use client'
|
|
183
|
+
|
|
184
|
+
import { NexusProvider } from '@nexushub/client'
|
|
185
|
+
|
|
186
|
+
export function NexusWrapper({ children }: { children: React.ReactNode }) {
|
|
187
|
+
return (
|
|
188
|
+
<NexusProvider projectId="optional-override-id">
|
|
189
|
+
{children}
|
|
190
|
+
</NexusProvider>
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## 📁 Content Fetching
|
|
196
|
+
|
|
197
|
+
### Get a Single Page
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
// Basic usage
|
|
201
|
+
const page = await nexus.getPage('home')
|
|
202
|
+
|
|
203
|
+
// With options
|
|
204
|
+
const page = await nexus.getPage('about', {
|
|
205
|
+
revalidate: 300, // Revalidate every 5 minutes
|
|
206
|
+
tags: ['page:about'], // Cache tags for invalidation
|
|
207
|
+
forceRefresh: false, // Bypass cache
|
|
208
|
+
includeMetadata: true, // Include cache metadata
|
|
209
|
+
})
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Get Collections
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// Get all items from a collection
|
|
216
|
+
const posts = await nexus.content.getCollection('blog_posts')
|
|
217
|
+
|
|
218
|
+
// Advanced querying
|
|
219
|
+
const products = await nexus.content.getCollection('products', {
|
|
220
|
+
page: 1,
|
|
221
|
+
limit: 20,
|
|
222
|
+
sort: 'price',
|
|
223
|
+
order: 'asc',
|
|
224
|
+
filter: {
|
|
225
|
+
category: 'electronics',
|
|
226
|
+
price: { $lt: 1000 },
|
|
227
|
+
inStock: true
|
|
228
|
+
},
|
|
229
|
+
search: 'wireless headphones',
|
|
230
|
+
fields: ['id', 'name', 'price', 'image'],
|
|
231
|
+
include: ['category', 'reviews'],
|
|
232
|
+
})
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Get a Single Item
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
const product = await nexus.content.getItem('products', 'prod_12345', {
|
|
239
|
+
include: ['category', 'reviews', 'variants'],
|
|
240
|
+
tags: ['product:prod_12345'],
|
|
241
|
+
})
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Search Across Collections
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
const results = await nexus.content.search('wireless', {
|
|
248
|
+
collections: ['products', 'articles'],
|
|
249
|
+
fields: ['title', 'description', 'content'],
|
|
250
|
+
limit: 50,
|
|
251
|
+
})
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Get Global Settings
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
const globals = await nexus.content.getGlobals({
|
|
258
|
+
include: ['navigation', 'footer', 'social_links'],
|
|
259
|
+
revalidate: 3600, // Cache for 1 hour
|
|
260
|
+
})
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## 🔐 Authentication
|
|
264
|
+
|
|
265
|
+
### Setup Auth Provider
|
|
266
|
+
|
|
267
|
+
```tsx
|
|
268
|
+
// app/auth/layout.tsx
|
|
269
|
+
'use client'
|
|
270
|
+
|
|
271
|
+
import { useNexusAuth } from '@nexushub/client'
|
|
272
|
+
|
|
273
|
+
export default function AuthLayout({ children }) {
|
|
274
|
+
const { user, isLoading, isAuthenticated } = useNexusAuth()
|
|
275
|
+
|
|
276
|
+
if (isLoading) return <div>Loading...</div>
|
|
277
|
+
|
|
278
|
+
return (
|
|
279
|
+
<div>
|
|
280
|
+
{isAuthenticated ? (
|
|
281
|
+
<div>
|
|
282
|
+
<p>Welcome, {user.name}!</p>
|
|
283
|
+
{children}
|
|
284
|
+
</div>
|
|
285
|
+
) : (
|
|
286
|
+
<LoginForm />
|
|
287
|
+
)}
|
|
288
|
+
</div>
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Login/Register Forms
|
|
294
|
+
|
|
295
|
+
```tsx
|
|
296
|
+
'use client'
|
|
297
|
+
|
|
298
|
+
import { useNexusAuth } from '@nexushub/client'
|
|
299
|
+
import { useState } from 'react'
|
|
300
|
+
|
|
301
|
+
export function LoginForm() {
|
|
302
|
+
const { login, register, loginWithGoogle } = useNexusAuth()
|
|
303
|
+
const [email, setEmail] = useState('')
|
|
304
|
+
const [password, setPassword] = useState('')
|
|
305
|
+
|
|
306
|
+
const handleLogin = async () => {
|
|
307
|
+
try {
|
|
308
|
+
await login({ email, password })
|
|
309
|
+
// Redirect or show success
|
|
310
|
+
} catch (error) {
|
|
311
|
+
console.error('Login failed:', error)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const handleGoogleLogin = () => {
|
|
316
|
+
loginWithGoogle()
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return (
|
|
320
|
+
<div>
|
|
321
|
+
<input
|
|
322
|
+
type="email"
|
|
323
|
+
value={email}
|
|
324
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
325
|
+
placeholder="Email"
|
|
326
|
+
/>
|
|
327
|
+
<input
|
|
328
|
+
type="password"
|
|
329
|
+
value={password}
|
|
330
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
331
|
+
placeholder="Password"
|
|
332
|
+
/>
|
|
333
|
+
<button onClick={handleLogin}>Login</button>
|
|
334
|
+
<button onClick={handleGoogleLogin}>Login with Google</button>
|
|
335
|
+
</div>
|
|
336
|
+
)
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Protected Routes Hook
|
|
341
|
+
|
|
342
|
+
```tsx
|
|
343
|
+
'use client'
|
|
344
|
+
|
|
345
|
+
import { useNexusAuth } from '@nexushub/client'
|
|
346
|
+
import { useRouter } from 'next/navigation'
|
|
347
|
+
import { useEffect } from 'react'
|
|
348
|
+
|
|
349
|
+
export function useRequireAuth(redirectTo = '/login') {
|
|
350
|
+
const { user, isLoading, isAuthenticated } = useNexusAuth()
|
|
351
|
+
const router = useRouter()
|
|
352
|
+
|
|
353
|
+
useEffect(() => {
|
|
354
|
+
if (!isLoading && !isAuthenticated) {
|
|
355
|
+
router.push(redirectTo)
|
|
356
|
+
}
|
|
357
|
+
}, [isLoading, isAuthenticated, router, redirectTo])
|
|
358
|
+
|
|
359
|
+
return { user, isLoading }
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Usage
|
|
363
|
+
export default function DashboardPage() {
|
|
364
|
+
const { user, isLoading } = useRequireAuth('/login')
|
|
365
|
+
|
|
366
|
+
if (isLoading) return <div>Loading...</div>
|
|
367
|
+
|
|
368
|
+
return (
|
|
369
|
+
<div>
|
|
370
|
+
<h1>Dashboard</h1>
|
|
371
|
+
<p>Welcome, {user.name}!</p>
|
|
372
|
+
</div>
|
|
373
|
+
)
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## 📊 Analytics
|
|
378
|
+
|
|
379
|
+
### Automatic Tracking
|
|
380
|
+
|
|
381
|
+
Analytics starts automatically when you wrap your app with `NexusProvider`. It tracks:
|
|
382
|
+
|
|
383
|
+
- Page views and route changes
|
|
384
|
+
- Click events (links, buttons)
|
|
385
|
+
- Form submissions
|
|
386
|
+
- Web Vitals (CLS, LCP, FID, INP)
|
|
387
|
+
- Performance metrics
|
|
388
|
+
- Error tracking
|
|
389
|
+
|
|
390
|
+
### Manual Event Tracking
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
import { nexus } from '@nexushub/client'
|
|
394
|
+
|
|
395
|
+
// Get analytics instance (automatically initialized)
|
|
396
|
+
const analytics = nexus['analytics'] // Internal access
|
|
397
|
+
|
|
398
|
+
// Or track custom events
|
|
399
|
+
analytics.track('button_clicked', {
|
|
400
|
+
button_id: 'cta-primary',
|
|
401
|
+
page: '/home',
|
|
402
|
+
timestamp: new Date().toISOString()
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
// Track purchases
|
|
406
|
+
analytics.trackPurchase({
|
|
407
|
+
order_id: 'ORD_12345',
|
|
408
|
+
total: 99.99,
|
|
409
|
+
currency: 'USD',
|
|
410
|
+
items: [
|
|
411
|
+
{ id: 'prod_1', name: 'Product 1', price: 49.99, quantity: 2 }
|
|
412
|
+
]
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
// Identify users (link anonymous to known)
|
|
416
|
+
await analytics.identify('user_123', {
|
|
417
|
+
name: 'John Doe',
|
|
418
|
+
email: 'john@example.com',
|
|
419
|
+
plan: 'premium'
|
|
420
|
+
})
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Web Vitals Monitoring
|
|
424
|
+
|
|
425
|
+
Automatically tracks Core Web Vitals with detailed breakdowns:
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
// You can access vitals data through the analytics engine
|
|
429
|
+
const sessionId = analytics.getSessionId()
|
|
430
|
+
console.log('Session:', sessionId)
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Disable Analytics
|
|
434
|
+
|
|
435
|
+
```tsx
|
|
436
|
+
// If you need to disable analytics (GDPR compliance)
|
|
437
|
+
<NexusProvider disableAnalytics>
|
|
438
|
+
{children}
|
|
439
|
+
</NexusProvider>
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
## 💾 Caching System
|
|
443
|
+
|
|
444
|
+
### Cache Strategies
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
// 1. Memory Cache (Default - Fastest)
|
|
448
|
+
const client = createNexusClient({
|
|
449
|
+
cacheStrategy: 'memory', // In-memory cache, cleared on page refresh
|
|
450
|
+
revalidateTime: 60 // Seconds
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
// 2. LocalStorage Cache (Persistent)
|
|
454
|
+
const client = createNexusClient({
|
|
455
|
+
cacheStrategy: 'localStorage', // Persists across sessions
|
|
456
|
+
revalidateTime: 300 // 5 minutes
|
|
457
|
+
})
|
|
458
|
+
|
|
459
|
+
// 3. No Cache (Always Fresh)
|
|
460
|
+
const client = createNexusClient({
|
|
461
|
+
cacheStrategy: 'none' // Always fetch from network
|
|
462
|
+
})
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Cache Invalidation
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
// Invalidate by tags
|
|
469
|
+
nexus.content.invalidateCache(['page:home', 'collection:blog_posts'])
|
|
470
|
+
|
|
471
|
+
// Clear all cache
|
|
472
|
+
nexus.content.clearCache()
|
|
473
|
+
|
|
474
|
+
// Manual cache control
|
|
475
|
+
const page = await nexus.getPage('home', {
|
|
476
|
+
forceRefresh: true, // Skip cache
|
|
477
|
+
revalidate: 0 // No revalidation
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
// Get cache statistics
|
|
481
|
+
const stats = nexus.content.getCacheStats()
|
|
482
|
+
console.log(stats)
|
|
483
|
+
/*
|
|
484
|
+
{
|
|
485
|
+
memory: {
|
|
486
|
+
size: 15,
|
|
487
|
+
hits: 124,
|
|
488
|
+
misses: 12,
|
|
489
|
+
hitRate: 0.91,
|
|
490
|
+
evictions: 3
|
|
491
|
+
},
|
|
492
|
+
browser: { size: 24567 },
|
|
493
|
+
local: { loaded: true }
|
|
494
|
+
}
|
|
495
|
+
*/
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### Cache Tags
|
|
499
|
+
|
|
500
|
+
Auto-generated tags for smart invalidation:
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
// Content gets tagged automatically:
|
|
504
|
+
// - `nexus_project_${projectId}`
|
|
505
|
+
// - `content_${slug}`
|
|
506
|
+
// - `collection_${collectionId}`
|
|
507
|
+
// - `item_${itemId}`
|
|
508
|
+
// - `nexus_global_config`
|
|
509
|
+
|
|
510
|
+
// You can add custom tags:
|
|
511
|
+
const page = await nexus.getPage('home', {
|
|
512
|
+
tags: ['homepage', 'featured']
|
|
513
|
+
})
|
|
514
|
+
|
|
515
|
+
// Later, invalidate all homepage content:
|
|
516
|
+
nexus.content.invalidateCache(['homepage'])
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
## 🛠️ CLI Tool
|
|
520
|
+
|
|
521
|
+
### Installation
|
|
522
|
+
|
|
523
|
+
```bash
|
|
524
|
+
# If installed globally
|
|
525
|
+
npm install -g @nexushub/client
|
|
526
|
+
|
|
527
|
+
# Or use npx
|
|
528
|
+
npx @nexushub/client <command>
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### Available Commands
|
|
532
|
+
|
|
533
|
+
```bash
|
|
534
|
+
# Initialize project structure
|
|
535
|
+
npx nexus init
|
|
536
|
+
|
|
537
|
+
# Pull content from NexusHub to local cache
|
|
538
|
+
npx nexus pull --api-key YOUR_KEY --project-id YOUR_ID
|
|
539
|
+
|
|
540
|
+
# Seed content from local files to NexusHub
|
|
541
|
+
npx nexus seed --input .nexus/seed
|
|
542
|
+
|
|
543
|
+
# Trigger deployment
|
|
544
|
+
npx nexus deploy --environment production
|
|
545
|
+
|
|
546
|
+
# Check project status
|
|
547
|
+
npx nexus status
|
|
548
|
+
|
|
549
|
+
# Force overwrite local cache
|
|
550
|
+
npx nexus pull --force
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
### Project Structure
|
|
554
|
+
|
|
555
|
+
```
|
|
556
|
+
.nexus/
|
|
557
|
+
├── cache.json # Local cache (gitignored)
|
|
558
|
+
├── seed/ # Seed data for content
|
|
559
|
+
│ ├── pages.json # Static pages
|
|
560
|
+
│ ├── collections/ # Dynamic collections
|
|
561
|
+
│ │ ├── blog_posts.json
|
|
562
|
+
│ │ └── products.json
|
|
563
|
+
│ └── globals.json # Global settings
|
|
564
|
+
└── README.md
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### Environment Variables for CLI
|
|
568
|
+
|
|
569
|
+
```bash
|
|
570
|
+
# .env.local (for CLI)
|
|
571
|
+
NEXUS_API_KEY=your_api_key_here
|
|
572
|
+
NEXUS_PROJECT_ID=your_project_id_here
|
|
573
|
+
NEXUS_API_URL=https://api.nexushub.com/v1
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
## 📚 API Reference
|
|
577
|
+
|
|
578
|
+
### `NexusClient`
|
|
579
|
+
|
|
580
|
+
#### Constructor
|
|
581
|
+
|
|
582
|
+
```typescript
|
|
583
|
+
new NexusClient(config?: Partial<NexusConfig>)
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
#### Methods
|
|
587
|
+
|
|
588
|
+
- `getPage<T>(slug: string, options?)`: Get singleton page
|
|
589
|
+
- `getConfig()`: Get current config
|
|
590
|
+
- `content`: ContentEngine instance
|
|
591
|
+
- `analytics`: AnalyticsEngine instance (internal)
|
|
592
|
+
|
|
593
|
+
### `ContentEngine`
|
|
594
|
+
|
|
595
|
+
#### Core Methods
|
|
596
|
+
|
|
597
|
+
```typescript
|
|
598
|
+
// Content fetching
|
|
599
|
+
getPage<T>(slug: string, options?): Promise<T>
|
|
600
|
+
getCollection<T>(collectionId: string, query?, options?): Promise<CollectionResponse<T>>
|
|
601
|
+
getItem<T>(collectionId: string, itemId: string, options?): Promise<T>
|
|
602
|
+
getGlobals<T>(options?): Promise<T>
|
|
603
|
+
|
|
604
|
+
// Search
|
|
605
|
+
search<T>(query: string, options?): Promise<{ results: T[], total: number }>
|
|
606
|
+
|
|
607
|
+
// Cache management
|
|
608
|
+
invalidateCache(tags: string[]): void
|
|
609
|
+
clearCache(): void
|
|
610
|
+
getCacheStats(): CacheStats
|
|
611
|
+
|
|
612
|
+
// Real-time
|
|
613
|
+
subscribeToUpdates(callback): () => void
|
|
614
|
+
prefetch(urls: string[]): Promise<void>
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
### `useNexusAuth()` Hook
|
|
618
|
+
|
|
619
|
+
#### Returns
|
|
620
|
+
|
|
621
|
+
```typescript
|
|
622
|
+
{
|
|
623
|
+
user: SiteUser | null,
|
|
624
|
+
isLoading: boolean,
|
|
625
|
+
error: Error | null,
|
|
626
|
+
isAuthenticated: boolean,
|
|
627
|
+
|
|
628
|
+
// Methods
|
|
629
|
+
login: (credentials: LoginCredentials) => Promise<void>,
|
|
630
|
+
register: (credentials: RegisterCredentials) => Promise<void>,
|
|
631
|
+
logout: () => Promise<void>,
|
|
632
|
+
loginWithGoogle: () => void,
|
|
633
|
+
updateProfile: (data: Partial<SiteUser>) => Promise<void>
|
|
634
|
+
}
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
### Types
|
|
638
|
+
|
|
639
|
+
```typescript
|
|
640
|
+
interface NexusConfig {
|
|
641
|
+
projectId: string;
|
|
642
|
+
apiUrl: string;
|
|
643
|
+
apiKey?: string;
|
|
644
|
+
debug?: boolean;
|
|
645
|
+
cacheStrategy?: 'memory' | 'localStorage' | 'none';
|
|
646
|
+
revalidateTime?: number;
|
|
647
|
+
timeout?: number;
|
|
648
|
+
retries?: number;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
interface ContentResponse<T = any> {
|
|
652
|
+
id: string;
|
|
653
|
+
type: string;
|
|
654
|
+
data: T;
|
|
655
|
+
meta: {
|
|
656
|
+
version: number;
|
|
657
|
+
updatedAt: string;
|
|
658
|
+
locale?: string;
|
|
659
|
+
cacheStatus: 'hit' | 'miss' | 'stale';
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
interface CollectionResponse<T = any> {
|
|
664
|
+
items: T[];
|
|
665
|
+
total: number;
|
|
666
|
+
page: number;
|
|
667
|
+
limit: number;
|
|
668
|
+
totalPages: number;
|
|
669
|
+
hasNext: boolean;
|
|
670
|
+
hasPrev: boolean;
|
|
671
|
+
meta?: {
|
|
672
|
+
fetchedAt: string;
|
|
673
|
+
cacheStatus: string;
|
|
674
|
+
filters?: Record<string, any>;
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
## 🚦 Examples
|
|
680
|
+
|
|
681
|
+
### Next.js App Router
|
|
682
|
+
|
|
683
|
+
```tsx
|
|
684
|
+
// app/layout.tsx
|
|
685
|
+
import { NexusProvider } from '@nexushub/client'
|
|
686
|
+
|
|
687
|
+
export default function RootLayout({ children }) {
|
|
688
|
+
return (
|
|
689
|
+
<html lang="en">
|
|
690
|
+
<body>
|
|
691
|
+
<NexusProvider>
|
|
692
|
+
{children}
|
|
693
|
+
</NexusProvider>
|
|
694
|
+
</body>
|
|
695
|
+
</html>
|
|
696
|
+
)
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// app/blog/page.tsx
|
|
700
|
+
import { nexus } from '@nexushub/client'
|
|
701
|
+
|
|
702
|
+
export default async function BlogPage() {
|
|
703
|
+
const posts = await nexus.content.getCollection('blog_posts', {
|
|
704
|
+
page: 1,
|
|
705
|
+
limit: 10,
|
|
706
|
+
sort: 'publishedAt',
|
|
707
|
+
order: 'desc',
|
|
708
|
+
filter: { status: 'published' }
|
|
709
|
+
})
|
|
710
|
+
|
|
711
|
+
return (
|
|
712
|
+
<div>
|
|
713
|
+
<h1>Blog</h1>
|
|
714
|
+
{posts.items.map(post => (
|
|
715
|
+
<article key={post.id}>
|
|
716
|
+
<h2>{post.title}</h2>
|
|
717
|
+
<p>{post.excerpt}</p>
|
|
718
|
+
</article>
|
|
719
|
+
))}
|
|
720
|
+
</div>
|
|
721
|
+
)
|
|
722
|
+
}
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
### Next.js Pages Router
|
|
726
|
+
|
|
727
|
+
```tsx
|
|
728
|
+
// pages/_app.tsx
|
|
729
|
+
import { NexusProvider } from '@nexushub/client'
|
|
730
|
+
|
|
731
|
+
function MyApp({ Component, pageProps }) {
|
|
732
|
+
return (
|
|
733
|
+
<NexusProvider>
|
|
734
|
+
<Component {...pageProps} />
|
|
735
|
+
</NexusProvider>
|
|
736
|
+
)
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// pages/index.tsx
|
|
740
|
+
import { nexus } from '@nexushub/client'
|
|
741
|
+
|
|
742
|
+
export async function getServerSideProps() {
|
|
743
|
+
const page = await nexus.getPage('home')
|
|
744
|
+
return { props: { page } }
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
export default function Home({ page }) {
|
|
748
|
+
return (
|
|
749
|
+
<div>
|
|
750
|
+
<h1>{page.title}</h1>
|
|
751
|
+
<div dangerouslySetInnerHTML={{ __html: page.content }} />
|
|
752
|
+
</div>
|
|
753
|
+
)
|
|
754
|
+
}
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
### React (No Framework)
|
|
758
|
+
|
|
759
|
+
```tsx
|
|
760
|
+
// index.tsx
|
|
761
|
+
import React from 'react'
|
|
762
|
+
import ReactDOM from 'react-dom/client'
|
|
763
|
+
import { NexusProvider } from '@nexushub/client'
|
|
764
|
+
import App from './App'
|
|
765
|
+
|
|
766
|
+
const root = ReactDOM.createRoot(document.getElementById('root'))
|
|
767
|
+
root.render(
|
|
768
|
+
<React.StrictMode>
|
|
769
|
+
<NexusProvider projectId="your-project-id">
|
|
770
|
+
<App />
|
|
771
|
+
</NexusProvider>
|
|
772
|
+
</React.StrictMode>
|
|
773
|
+
)
|
|
774
|
+
|
|
775
|
+
// App.tsx
|
|
776
|
+
import { nexus, useNexusAuth } from '@nexushub/client'
|
|
777
|
+
import { useEffect, useState } from 'react'
|
|
778
|
+
|
|
779
|
+
function App() {
|
|
780
|
+
const [page, setPage] = useState(null)
|
|
781
|
+
const { user, login } = useNexusAuth()
|
|
782
|
+
|
|
783
|
+
useEffect(() => {
|
|
784
|
+
nexus.getPage('home').then(setPage)
|
|
785
|
+
}, [])
|
|
786
|
+
|
|
787
|
+
if (!page) return <div>Loading...</div>
|
|
788
|
+
|
|
789
|
+
return (
|
|
790
|
+
<div>
|
|
791
|
+
<h1>{page.title}</h1>
|
|
792
|
+
<p>{page.content}</p>
|
|
793
|
+
</div>
|
|
794
|
+
)
|
|
795
|
+
}
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
### E-commerce Example
|
|
799
|
+
|
|
800
|
+
```tsx
|
|
801
|
+
// components/ProductList.tsx
|
|
802
|
+
import { nexus } from '@nexushub/client'
|
|
803
|
+
|
|
804
|
+
export async function ProductList({ category }) {
|
|
805
|
+
const products = await nexus.content.getCollection('products', {
|
|
806
|
+
filter: { category },
|
|
807
|
+
sort: 'createdAt',
|
|
808
|
+
order: 'desc',
|
|
809
|
+
include: ['variants', 'reviews']
|
|
810
|
+
})
|
|
811
|
+
|
|
812
|
+
const handlePurchase = (product) => {
|
|
813
|
+
// Track purchase in analytics
|
|
814
|
+
nexus['analytics'].trackPurchase({
|
|
815
|
+
order_id: `temp_${Date.now()}`,
|
|
816
|
+
total: product.price,
|
|
817
|
+
currency: 'USD',
|
|
818
|
+
items: [{
|
|
819
|
+
id: product.id,
|
|
820
|
+
name: product.name,
|
|
821
|
+
price: product.price,
|
|
822
|
+
quantity: 1
|
|
823
|
+
}]
|
|
824
|
+
})
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
return (
|
|
828
|
+
<div className="grid grid-cols-4 gap-4">
|
|
829
|
+
{products.items.map(product => (
|
|
830
|
+
<div key={product.id} className="border p-4">
|
|
831
|
+
<img src={product.image} alt={product.name} />
|
|
832
|
+
<h3>{product.name}</h3>
|
|
833
|
+
<p>${product.price}</p>
|
|
834
|
+
<button
|
|
835
|
+
onClick={() => handlePurchase(product)}
|
|
836
|
+
className="bg-blue-500 text-white px-4 py-2"
|
|
837
|
+
>
|
|
838
|
+
Add to Cart
|
|
839
|
+
</button>
|
|
840
|
+
</div>
|
|
841
|
+
))}
|
|
842
|
+
</div>
|
|
843
|
+
)
|
|
844
|
+
}
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
## 🐛 Troubleshooting
|
|
848
|
+
|
|
849
|
+
### Common Issues
|
|
850
|
+
|
|
851
|
+
#### 1. "Missing projectId" Error
|
|
852
|
+
|
|
853
|
+
```bash
|
|
854
|
+
# Check your environment variables
|
|
855
|
+
echo $NEXT_PUBLIC_NEXUS_ID
|
|
856
|
+
|
|
857
|
+
# Ensure .env.local exists
|
|
858
|
+
ls -la .env.local
|
|
859
|
+
|
|
860
|
+
# Restart dev server after changing env vars
|
|
861
|
+
npm run dev
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
#### 2. Cache Not Updating
|
|
865
|
+
|
|
866
|
+
```typescript
|
|
867
|
+
// Force refresh
|
|
868
|
+
await nexus.getPage('home', { forceRefresh: true })
|
|
869
|
+
|
|
870
|
+
// Clear all cache
|
|
871
|
+
nexus.content.clearCache()
|
|
872
|
+
|
|
873
|
+
// Check cache stats
|
|
874
|
+
console.log(nexus.content.getCacheStats())
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
#### 3. Analytics Not Tracking
|
|
878
|
+
|
|
879
|
+
```typescript
|
|
880
|
+
// Check if analytics is enabled
|
|
881
|
+
const client = createNexusClient({ debug: true })
|
|
882
|
+
|
|
883
|
+
// Verify in console logs
|
|
884
|
+
// Should see: "[NexusHub] Analytics started"
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
#### 4. Authentication Issues
|
|
888
|
+
|
|
889
|
+
```typescript
|
|
890
|
+
// Check CORS settings in NexusHub dashboard
|
|
891
|
+
// Ensure your domain is whitelisted
|
|
892
|
+
|
|
893
|
+
// Verify HttpOnly cookies are supported
|
|
894
|
+
// Some ad blockers may interfere
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
#### 5. TypeScript Errors
|
|
898
|
+
|
|
899
|
+
```typescript
|
|
900
|
+
// If you get "Property 'content' does not exist"
|
|
901
|
+
// Make sure you're using the correct import:
|
|
902
|
+
import { nexus } from '@nexushub/client' // Correct
|
|
903
|
+
import nexus from '@nexushub/client' // Wrong
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
### Debug Mode
|
|
907
|
+
|
|
908
|
+
Enable debug logging to see detailed information:
|
|
909
|
+
|
|
910
|
+
```typescript
|
|
911
|
+
const client = createNexusClient({
|
|
912
|
+
debug: true,
|
|
913
|
+
cacheStrategy: 'memory'
|
|
914
|
+
})
|
|
915
|
+
|
|
916
|
+
// Check console for:
|
|
917
|
+
// - Cache hits/misses
|
|
918
|
+
// - Network requests
|
|
919
|
+
// - Performance metrics
|
|
920
|
+
// - Analytics events
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
### Performance Monitoring
|
|
924
|
+
|
|
925
|
+
```typescript
|
|
926
|
+
// Get performance metrics
|
|
927
|
+
const stats = nexus.content.getCacheStats()
|
|
928
|
+
console.log('Cache Hit Rate:', stats.memory.hitRate)
|
|
929
|
+
|
|
930
|
+
// Monitor Web Vitals in analytics dashboard
|
|
931
|
+
// Visit: https://dashboard.nexushub.com/analytics
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
## 🔧 Advanced Configuration
|
|
935
|
+
|
|
936
|
+
### Custom Cache Implementation
|
|
937
|
+
|
|
938
|
+
```typescript
|
|
939
|
+
import { MemoryCache, BrowserCache } from '@nexushub/client'
|
|
940
|
+
|
|
941
|
+
// Extend existing cache
|
|
942
|
+
class CustomMemoryCache extends MemoryCache {
|
|
943
|
+
constructor(options) {
|
|
944
|
+
super({ ...options, maxSize: 2000 })
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
set(key, data, options) {
|
|
948
|
+
// Add custom logic
|
|
949
|
+
console.log(`Caching: ${key}`)
|
|
950
|
+
super.set(key, data, options)
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// Use in your app
|
|
955
|
+
const customCache = new CustomMemoryCache({ ttl: 60000 })
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
### Middleware & Interceptors
|
|
959
|
+
|
|
960
|
+
```typescript
|
|
961
|
+
// Request/Response interceptors
|
|
962
|
+
const client = createNexusClient({
|
|
963
|
+
interceptors: {
|
|
964
|
+
request: (config) => {
|
|
965
|
+
// Add custom headers
|
|
966
|
+
config.headers['X-Custom-Header'] = 'value'
|
|
967
|
+
return config
|
|
968
|
+
},
|
|
969
|
+
response: (response) => {
|
|
970
|
+
// Transform response
|
|
971
|
+
return response.data
|
|
972
|
+
},
|
|
973
|
+
error: (error) => {
|
|
974
|
+
// Custom error handling
|
|
975
|
+
console.error('Request failed:', error)
|
|
976
|
+
throw error
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
})
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
### Multiple Projects
|
|
983
|
+
|
|
984
|
+
```typescript
|
|
985
|
+
import { createNexusClient } from '@nexushub/client'
|
|
986
|
+
|
|
987
|
+
const mainClient = createNexusClient({
|
|
988
|
+
projectId: 'main-project',
|
|
989
|
+
apiKey: 'main-key'
|
|
990
|
+
})
|
|
991
|
+
|
|
992
|
+
const secondaryClient = createNexusClient({
|
|
993
|
+
projectId: 'secondary-project',
|
|
994
|
+
apiKey: 'secondary-key',
|
|
995
|
+
apiUrl: 'https://api-2.nexushub.com/v1'
|
|
996
|
+
})
|
|
997
|
+
|
|
998
|
+
// Use both clients in your app
|
|
999
|
+
const [mainPage, secondaryPage] = await Promise.all([
|
|
1000
|
+
mainClient.getPage('home'),
|
|
1001
|
+
secondaryClient.getPage('home')
|
|
1002
|
+
])
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
## 📈 Performance Benchmarks
|
|
1006
|
+
|
|
1007
|
+
```
|
|
1008
|
+
Cache Performance:
|
|
1009
|
+
├── Memory Cache: 0.1ms - 1ms
|
|
1010
|
+
├── LocalStorage: 1ms - 5ms
|
|
1011
|
+
└── Network: 50ms - 300ms
|
|
1012
|
+
|
|
1013
|
+
Bundle Size:
|
|
1014
|
+
├── Core SDK: 15kb gzipped
|
|
1015
|
+
├── With Analytics: 28kb gzipped
|
|
1016
|
+
└── Full Package: 35kb gzipped
|
|
1017
|
+
|
|
1018
|
+
Memory Usage:
|
|
1019
|
+
├── Memory Cache: ~5MB max
|
|
1020
|
+
├── LocalStorage: ~5MB max
|
|
1021
|
+
└── Browser Memory: ~10MB typical
|
|
1022
|
+
```
|
|
1023
|
+
|
|
1024
|
+
## 🤝 Contributing
|
|
1025
|
+
|
|
1026
|
+
We welcome contributions! Here's how to get started:
|
|
1027
|
+
|
|
1028
|
+
1. **Fork the repository**
|
|
1029
|
+
2. **Clone your fork**
|
|
1030
|
+
|
|
1031
|
+
```bash
|
|
1032
|
+
git clone https://github.com/your-username/nexushub-client-sdk.git
|
|
1033
|
+
cd nexushub-client-sdk/packages/client-sdk
|
|
1034
|
+
```
|
|
1035
|
+
|
|
1036
|
+
1. **Install dependencies**
|
|
1037
|
+
|
|
1038
|
+
```bash
|
|
1039
|
+
npm install
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
1. **Run tests**
|
|
1043
|
+
|
|
1044
|
+
```bash
|
|
1045
|
+
npm test
|
|
1046
|
+
npm run test:coverage
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
1. **Make changes and test**
|
|
1050
|
+
|
|
1051
|
+
```bash
|
|
1052
|
+
npm run dev # Watch mode
|
|
1053
|
+
npm run build
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
1. **Submit a Pull Request**
|
|
1057
|
+
|
|
1058
|
+
### Development Scripts
|
|
1059
|
+
|
|
1060
|
+
```bash
|
|
1061
|
+
# Build
|
|
1062
|
+
npm run build
|
|
1063
|
+
|
|
1064
|
+
# Development (watch mode)
|
|
1065
|
+
npm run dev
|
|
1066
|
+
|
|
1067
|
+
# Type checking
|
|
1068
|
+
npm run type-check
|
|
1069
|
+
|
|
1070
|
+
# Linting
|
|
1071
|
+
npm run lint
|
|
1072
|
+
|
|
1073
|
+
# Testing
|
|
1074
|
+
npm test # Run all tests
|
|
1075
|
+
npm run test:watch # Watch mode
|
|
1076
|
+
npm run test:coverage # Coverage report
|
|
1077
|
+
npm run test:debug # Verbose output
|
|
1078
|
+
```
|
|
1079
|
+
|
|
1080
|
+
## 📄 License
|
|
1081
|
+
|
|
1082
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
1083
|
+
|
|
1084
|
+
## 🔗 Links
|
|
1085
|
+
|
|
1086
|
+
- [NexusHub Dashboard](https://dashboard.nexushub.com)
|
|
1087
|
+
- [Documentation](https://docs.nexushub.com)
|
|
1088
|
+
- [API Reference](https://docs.nexushub.com/api)
|
|
1089
|
+
- [GitHub Issues](https://github.com/nexushub/client-sdk/issues)
|
|
1090
|
+
- [Discord Community](https://discord.gg/nexushub)
|
|
1091
|
+
|
|
1092
|
+
## 🏆 Support
|
|
1093
|
+
|
|
1094
|
+
- **Community Support**: [GitHub Discussions](https://github.com/nexushub/client-sdk/discussions)
|
|
1095
|
+
- **Priority Support**: Available for enterprise plans
|
|
1096
|
+
- **Bug Reports**: [GitHub Issues](https://github.com/nexushub/client-sdk/issues)
|
|
1097
|
+
- **Feature Requests**: Submit via GitHub Issues
|
|
1098
|
+
|
|
1099
|
+
---
|
|
1100
|
+
|
|
1101
|
+
<div align="center">
|
|
1102
|
+
Made with ❤️ by the NexusHub Team
|
|
1103
|
+
<br/>
|
|
1104
|
+
<a href="https://nexushub.com">nexushub.com</a>
|
|
1105
|
+
</div>
|