miragedev-sdk 0.2.0 → 0.4.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.
- package/README.md +538 -10
- package/dist/ai/index.cjs +383 -0
- package/dist/ai/index.d.cts +14 -0
- package/dist/ai/index.d.ts +14 -0
- package/dist/ai/index.js +366 -0
- package/dist/ai-B4sdf-PH.d.cts +72 -0
- package/dist/ai-B4sdf-PH.d.ts +72 -0
- package/dist/analytics/client.cjs +93 -0
- package/dist/analytics/client.d.cts +19 -0
- package/dist/analytics/client.d.ts +19 -0
- package/dist/analytics/client.js +89 -0
- package/dist/analytics/index.cjs +146 -0
- package/dist/analytics/index.d.cts +9 -0
- package/dist/analytics/index.d.ts +9 -0
- package/dist/analytics/index.js +140 -0
- package/dist/analytics-KVIUba3B.d.cts +17 -0
- package/dist/analytics-KVIUba3B.d.ts +17 -0
- package/dist/auth/index.cjs +5 -4
- package/dist/auth/index.js +3 -2
- package/dist/auth/middleware.cjs +4 -3
- package/dist/auth/middleware.js +3 -2
- package/dist/billing/index.cjs +3 -2
- package/dist/billing/index.js +2 -1
- package/dist/billing/mobile.cjs +4 -3
- package/dist/billing/mobile.js +2 -1
- package/dist/billing/webhook.cjs +3 -2
- package/dist/billing/webhook.js +2 -1
- package/dist/chunk-57B3UIH2.js +167 -0
- package/dist/chunk-6YFK3S4F.js +21 -0
- package/dist/chunk-DF5ZFP6K.cjs +177 -0
- package/dist/chunk-EALJHNNK.cjs +184 -0
- package/dist/chunk-H2CCSTQQ.js +1 -0
- package/dist/{chunk-M3DPIKWT.js → chunk-KYWT4U6H.js} +1 -1
- package/dist/chunk-NAGQOCLL.js +177 -0
- package/dist/chunk-NIUFPKQE.cjs +4179 -0
- package/dist/chunk-SY4VNBJR.js +4176 -0
- package/dist/chunk-TALL2IL5.cjs +24 -0
- package/dist/chunk-VNG3IPWG.cjs +2 -0
- package/dist/{chunk-PHTUPKEM.cjs → chunk-XZ7EEGLT.cjs} +2 -2
- package/dist/email/index.cjs +3 -2
- package/dist/email/index.js +2 -1
- package/dist/index.cjs +7 -2
- package/dist/index.d.cts +264 -2
- package/dist/index.d.ts +264 -2
- package/dist/index.js +6 -1
- package/dist/jobs/client.cjs +107 -0
- package/dist/jobs/client.d.cts +15 -0
- package/dist/jobs/client.d.ts +15 -0
- package/dist/jobs/client.js +104 -0
- package/dist/jobs/define.cjs +38 -0
- package/dist/jobs/define.d.cts +6 -0
- package/dist/jobs/define.d.ts +6 -0
- package/dist/jobs/define.js +35 -0
- package/dist/jobs/index.cjs +34 -0
- package/dist/jobs/index.d.cts +10 -0
- package/dist/jobs/index.d.ts +10 -0
- package/dist/jobs/index.js +5 -0
- package/dist/jobs-DVrbjDgE.d.cts +73 -0
- package/dist/jobs-DVrbjDgE.d.ts +73 -0
- package/dist/limits/index.cjs +46 -0
- package/dist/limits/index.d.cts +13 -0
- package/dist/limits/index.d.ts +13 -0
- package/dist/limits/index.js +5 -0
- package/dist/limits-DAOZz-ua.d.cts +34 -0
- package/dist/limits-DAOZz-ua.d.ts +34 -0
- package/dist/storage/index.cjs +190 -0
- package/dist/storage/index.d.cts +10 -0
- package/dist/storage/index.d.ts +10 -0
- package/dist/storage/index.js +183 -0
- package/dist/storage-B87mGs3T.d.cts +34 -0
- package/dist/storage-B87mGs3T.d.ts +34 -0
- package/package.json +56 -3
- package/dist/chunk-5YXI4Q2K.js +0 -13813
- package/dist/chunk-E5YC2MHX.cjs +0 -13816
package/README.md
CHANGED
|
@@ -5,16 +5,27 @@ AI-first SDK for building SAAS applications with Next.js. Build production-ready
|
|
|
5
5
|
[](https://www.npmjs.com/package/miragedev-sdk)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
-
## Features
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
- 📧 **Email
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
8
|
+
## ✨ Features
|
|
9
|
+
|
|
10
|
+
### Core Modules
|
|
11
|
+
- 🔐 **Authentication**: Supabase (email, magic link, OAuth)
|
|
12
|
+
- 📧 **Email**: Resend (transactional emails, templates)
|
|
13
|
+
- 🤖 **AI**: OpenAI, Anthropic, Replicate (LLMs, image generation)
|
|
14
|
+
- 💳 **Payments**: Stripe (subscriptions, one-time payments)
|
|
15
|
+
- 📦 **Storage**: Cloudflare R2 (file upload, CDN integration)
|
|
16
|
+
- 📊 **Analytics**: PostHog (event tracking, usage limits)
|
|
17
|
+
- 🚦 **Limits**: Upstash Redis (rate limiting, circuit breaker)
|
|
18
|
+
- ⚙️ **Jobs**: Trigger.dev (background jobs, scheduling)
|
|
19
|
+
|
|
20
|
+
### Developer Experience
|
|
21
|
+
- 🎯 **Type-safe**: Full TypeScript support
|
|
22
|
+
- 🔧 **Zero-config**: Sensible defaults, minimal setup
|
|
23
|
+
- 🧩 **Modular**: Use only what you need
|
|
24
|
+
- 📚 **Well-documented**: Comprehensive guides and examples
|
|
25
|
+
- 🚀 **Production-ready**: Battle-tested patterns
|
|
26
|
+
- 📱 **PWA**: Progressive Web App with offline support
|
|
27
|
+
- 🎯 **Mobile-First**: Optimized hooks for mobile experiences
|
|
28
|
+
- 🤖 **AI-Friendly**: Comprehensive JSDoc for AI code generation
|
|
18
29
|
|
|
19
30
|
## Quick Start (2 minutes)
|
|
20
31
|
|
|
@@ -40,6 +51,17 @@ That's it! You now have a full-featured SAAS with:
|
|
|
40
51
|
```bash
|
|
41
52
|
# In your existing Next.js project
|
|
42
53
|
npm install miragedev-sdk
|
|
54
|
+
|
|
55
|
+
# Peer dependencies (install only what you need)
|
|
56
|
+
npm install @supabase/supabase-js # For authentication
|
|
57
|
+
npm install resend # For email
|
|
58
|
+
npm install openai @anthropic-ai/sdk replicate # For AI
|
|
59
|
+
npm install stripe # For payments
|
|
60
|
+
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner sharp # For storage
|
|
61
|
+
npm install posthog-node # For analytics
|
|
62
|
+
npm install @upstash/redis # For limits
|
|
63
|
+
npm install @trigger.dev/sdk # For jobs
|
|
64
|
+
|
|
43
65
|
npx miragedev init
|
|
44
66
|
```
|
|
45
67
|
|
|
@@ -267,6 +289,512 @@ function MyComponent() {
|
|
|
267
289
|
}
|
|
268
290
|
```
|
|
269
291
|
|
|
292
|
+
### AI Module
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
import { chat, chatStream, useChat, setAIConfig } from 'miragedev-sdk/ai'
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**Setup:**
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
// lib/miragedev.init.ts or app setup
|
|
302
|
+
import { initMirageDev } from 'miragedev-sdk'
|
|
303
|
+
|
|
304
|
+
initMirageDev({
|
|
305
|
+
ai: {
|
|
306
|
+
provider: 'openai',
|
|
307
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
308
|
+
defaultModel: 'gpt-4-turbo-preview',
|
|
309
|
+
temperature: 0.7,
|
|
310
|
+
}
|
|
311
|
+
})
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
**Server-Side Chat:**
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
import { chat } from 'miragedev-sdk/ai'
|
|
318
|
+
|
|
319
|
+
export async function POST(req: Request) {
|
|
320
|
+
const { message } = await req.json()
|
|
321
|
+
|
|
322
|
+
const response = await chat({
|
|
323
|
+
messages: [
|
|
324
|
+
{ role: 'system', content: 'You are a helpful assistant' },
|
|
325
|
+
{ role: 'user', content: message }
|
|
326
|
+
],
|
|
327
|
+
temperature: 0.7,
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
return Response.json({ reply: response.content })
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Streaming (Edge Runtime):**
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
import { chatStream, createEdgeStreamResponse } from 'miragedev-sdk/ai'
|
|
338
|
+
|
|
339
|
+
export const runtime = 'edge'
|
|
340
|
+
|
|
341
|
+
export async function POST(req: Request) {
|
|
342
|
+
const { messages } = await req.json()
|
|
343
|
+
|
|
344
|
+
return createEdgeStreamResponse(async (write) => {
|
|
345
|
+
await chatStream({ messages }, (chunk) => {
|
|
346
|
+
write(chunk.delta)
|
|
347
|
+
})
|
|
348
|
+
})
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**Client-Side Hook:**
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
'use client'
|
|
356
|
+
|
|
357
|
+
import { useChat } from 'miragedev-sdk/ai'
|
|
358
|
+
|
|
359
|
+
export function ChatComponent() {
|
|
360
|
+
const { messages, sendMessage, isLoading, streamingMessage } = useChat({
|
|
361
|
+
stream: true,
|
|
362
|
+
initialMessages: [
|
|
363
|
+
{ role: 'system', content: 'You are a helpful assistant' }
|
|
364
|
+
],
|
|
365
|
+
onError: (error) => console.error(error)
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
return (
|
|
369
|
+
<div>
|
|
370
|
+
{messages.map((msg, i) => (
|
|
371
|
+
<div key={i}>
|
|
372
|
+
<strong>{msg.role}:</strong> {msg.content}
|
|
373
|
+
</div>
|
|
374
|
+
))}
|
|
375
|
+
|
|
376
|
+
{streamingMessage && (
|
|
377
|
+
<div>
|
|
378
|
+
<strong>assistant:</strong> {streamingMessage}
|
|
379
|
+
</div>
|
|
380
|
+
)}
|
|
381
|
+
|
|
382
|
+
<button
|
|
383
|
+
onClick={() => sendMessage('Hello!')}
|
|
384
|
+
disabled={isLoading}
|
|
385
|
+
>
|
|
386
|
+
Send Message
|
|
387
|
+
</button>
|
|
388
|
+
</div>
|
|
389
|
+
)
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
**Embeddings:**
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
import { createEmbeddings } from 'miragedev-sdk/ai'
|
|
397
|
+
|
|
398
|
+
const response = await createEmbeddings({
|
|
399
|
+
input: 'Machine learning is fascinating',
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
console.log(response.embeddings[0]) // [0.123, -0.456, ...]
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**Environment Variables:**
|
|
406
|
+
|
|
407
|
+
```env
|
|
408
|
+
OPENAI_API_KEY=sk-...
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
**Supported Providers:**
|
|
412
|
+
- ✅ OpenAI (GPT-4, GPT-3.5, embeddings)
|
|
413
|
+
- 🔜 Anthropic (Claude) - Coming soon
|
|
414
|
+
|
|
415
|
+
### 📦 Storage Module
|
|
416
|
+
|
|
417
|
+
Upload and manage files with CDN integration.
|
|
418
|
+
|
|
419
|
+
**Features:**
|
|
420
|
+
- File upload with automatic optimization (resize, compress)
|
|
421
|
+
- CDN integration (Cloudflare R2)
|
|
422
|
+
- Pre-signed upload URLs for client-side uploads
|
|
423
|
+
- Public URL generation
|
|
424
|
+
- File metadata tracking
|
|
425
|
+
|
|
426
|
+
**Quick Start:**
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
import { uploadFile, getPublicUrl } from 'miragedev-sdk/storage'
|
|
430
|
+
|
|
431
|
+
// Upload a file
|
|
432
|
+
const result = await uploadFile({
|
|
433
|
+
file: imageBuffer,
|
|
434
|
+
key: 'avatars/user-123.jpg',
|
|
435
|
+
contentType: 'image/jpeg',
|
|
436
|
+
resize: { width: 800, height: 800, fit: 'cover' }
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
// Get public CDN URL
|
|
440
|
+
const url = await getPublicUrl(result.key)
|
|
441
|
+
console.log(url) // https://cdn.example.com/avatars/user-123.jpg
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
**Configuration:**
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
import { initMirageDev } from 'miragedev-sdk'
|
|
448
|
+
|
|
449
|
+
initMirageDev({
|
|
450
|
+
storage: {
|
|
451
|
+
provider: 'cloudflare-r2',
|
|
452
|
+
cloudflareR2: {
|
|
453
|
+
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
|
|
454
|
+
accessKeyId: process.env.CLOUDFLARE_ACCESS_KEY_ID!,
|
|
455
|
+
secretAccessKey: process.env.CLOUDFLARE_SECRET_ACCESS_KEY!,
|
|
456
|
+
bucketName: process.env.CLOUDFLARE_BUCKET_NAME!,
|
|
457
|
+
cdnUrl: 'https://cdn.example.com', // Optional
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
})
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
**Environment Variables:**
|
|
464
|
+
- `CLOUDFLARE_ACCOUNT_ID`
|
|
465
|
+
- `CLOUDFLARE_ACCESS_KEY_ID`
|
|
466
|
+
- `CLOUDFLARE_SECRET_ACCESS_KEY`
|
|
467
|
+
- `CLOUDFLARE_BUCKET_NAME`
|
|
468
|
+
|
|
469
|
+
### 📊 Analytics Module
|
|
470
|
+
|
|
471
|
+
Track events, identify users, and monitor usage limits.
|
|
472
|
+
|
|
473
|
+
**Features:**
|
|
474
|
+
- Event tracking with properties
|
|
475
|
+
- User identification and properties
|
|
476
|
+
- Usage tracking with Redis storage
|
|
477
|
+
- Automatic integration with Limits module
|
|
478
|
+
|
|
479
|
+
**Quick Start:**
|
|
480
|
+
|
|
481
|
+
```typescript
|
|
482
|
+
import { trackEvent, identifyUser, incrementUsage, getUsage } from 'miragedev-sdk/analytics'
|
|
483
|
+
|
|
484
|
+
// Track events
|
|
485
|
+
await trackEvent('image_generated', {
|
|
486
|
+
userId: 'user-123',
|
|
487
|
+
model: 'dalle-3',
|
|
488
|
+
credits: 10
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
// Identify users
|
|
492
|
+
await identifyUser('user-123', {
|
|
493
|
+
email: 'user@example.com',
|
|
494
|
+
plan: 'pro',
|
|
495
|
+
signupDate: '2026-01-01'
|
|
496
|
+
})
|
|
497
|
+
|
|
498
|
+
// Track usage limits
|
|
499
|
+
await incrementUsage('user-123', 'api_calls', 1)
|
|
500
|
+
const usage = await getUsage('user-123', 'api_calls')
|
|
501
|
+
console.log(`API calls: ${usage}`)
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
**React Hooks:**
|
|
505
|
+
|
|
506
|
+
```typescript
|
|
507
|
+
'use client'
|
|
508
|
+
import { useAnalytics, useUsageLimits, usePageTracking } from 'miragedev-sdk/analytics/client'
|
|
509
|
+
|
|
510
|
+
function Dashboard() {
|
|
511
|
+
const { trackEvent } = useAnalytics()
|
|
512
|
+
const { usage, increment, reset } = useUsageLimits('user-123', 'api_calls')
|
|
513
|
+
|
|
514
|
+
usePageTracking() // Auto-track page views
|
|
515
|
+
|
|
516
|
+
return <div>API Calls: {usage}</div>
|
|
517
|
+
}
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
**Configuration:**
|
|
521
|
+
|
|
522
|
+
```typescript
|
|
523
|
+
initMirageDev({
|
|
524
|
+
analytics: {
|
|
525
|
+
provider: 'posthog',
|
|
526
|
+
posthog: {
|
|
527
|
+
apiKey: process.env.POSTHOG_API_KEY!,
|
|
528
|
+
host: process.env.POSTHOG_HOST || 'https://app.posthog.com',
|
|
529
|
+
}
|
|
530
|
+
},
|
|
531
|
+
limits: { // Required for usage tracking
|
|
532
|
+
provider: 'upstash',
|
|
533
|
+
upstash: {
|
|
534
|
+
url: process.env.UPSTASH_REDIS_URL!,
|
|
535
|
+
token: process.env.UPSTASH_REDIS_TOKEN!,
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
})
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
**Environment Variables:**
|
|
542
|
+
- `POSTHOG_API_KEY`
|
|
543
|
+
- `POSTHOG_HOST` (optional)
|
|
544
|
+
|
|
545
|
+
### 🚦 Limits Module
|
|
546
|
+
|
|
547
|
+
Rate limiting and circuit breaker for cost control.
|
|
548
|
+
|
|
549
|
+
**Features:**
|
|
550
|
+
- Sliding window rate limiting
|
|
551
|
+
- Multi-tier rate limits
|
|
552
|
+
- Circuit breaker pattern
|
|
553
|
+
- Cost protection for AI APIs
|
|
554
|
+
- Redis-backed counters
|
|
555
|
+
|
|
556
|
+
**Quick Start:**
|
|
557
|
+
|
|
558
|
+
```typescript
|
|
559
|
+
import { checkRateLimit, checkCircuitBreaker, checkMultipleRateLimits } from 'miragedev-sdk/limits'
|
|
560
|
+
|
|
561
|
+
// Rate limiting
|
|
562
|
+
const result = await checkRateLimit({
|
|
563
|
+
key: 'api:user:123',
|
|
564
|
+
limit: 100,
|
|
565
|
+
window: 3600 // 1 hour
|
|
566
|
+
})
|
|
567
|
+
|
|
568
|
+
if (!result.allowed) {
|
|
569
|
+
throw new Error(`Rate limit exceeded. Try again in ${result.resetIn}s`)
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Multi-tier rate limits
|
|
573
|
+
const [ipLimit, userLimit] = await checkMultipleRateLimits([
|
|
574
|
+
{ key: 'api:ip:1.2.3.4', limit: 1000, window: 3600 },
|
|
575
|
+
{ key: 'api:user:123', limit: 100, window: 3600 },
|
|
576
|
+
])
|
|
577
|
+
|
|
578
|
+
if (!ipLimit.allowed || !userLimit.allowed) {
|
|
579
|
+
throw new Error('Rate limit exceeded')
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Circuit breaker for AI costs
|
|
583
|
+
const breaker = await checkCircuitBreaker({
|
|
584
|
+
key: 'ai:openai',
|
|
585
|
+
threshold: 1000, // $10 limit
|
|
586
|
+
window: 3600,
|
|
587
|
+
cooldown: 300
|
|
588
|
+
})
|
|
589
|
+
|
|
590
|
+
if (!breaker.allowed) {
|
|
591
|
+
throw new Error('AI spending limit reached')
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// After successful AI call
|
|
595
|
+
await breaker.increment(5) // $0.05 spent
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
**Configuration:**
|
|
599
|
+
|
|
600
|
+
```typescript
|
|
601
|
+
initMirageDev({
|
|
602
|
+
limits: {
|
|
603
|
+
provider: 'upstash',
|
|
604
|
+
upstash: {
|
|
605
|
+
url: process.env.UPSTASH_REDIS_URL!,
|
|
606
|
+
token: process.env.UPSTASH_REDIS_TOKEN!,
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
})
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
**Environment Variables:**
|
|
613
|
+
- `UPSTASH_REDIS_URL`
|
|
614
|
+
- `UPSTASH_REDIS_TOKEN`
|
|
615
|
+
|
|
616
|
+
### ⚙️ Jobs Module
|
|
617
|
+
|
|
618
|
+
Background jobs and scheduled tasks.
|
|
619
|
+
|
|
620
|
+
**Features:**
|
|
621
|
+
- Background job processing
|
|
622
|
+
- Cron scheduling
|
|
623
|
+
- Job status tracking
|
|
624
|
+
- Type-safe job definitions
|
|
625
|
+
- Automatic retries
|
|
626
|
+
|
|
627
|
+
**Quick Start:**
|
|
628
|
+
|
|
629
|
+
```typescript
|
|
630
|
+
import { enqueueJob, getJobStatus, scheduleJob } from 'miragedev-sdk/jobs'
|
|
631
|
+
import { defineJob, registerJobs } from 'miragedev-sdk/jobs/define'
|
|
632
|
+
|
|
633
|
+
// Define job handlers
|
|
634
|
+
const generateImageJob = defineJob(
|
|
635
|
+
'generate-image',
|
|
636
|
+
async (payload: { userId: string; prompt: string }, context) => {
|
|
637
|
+
const image = await generateImage(payload.prompt)
|
|
638
|
+
await uploadFile({ file: image, key: `images/${context.job.id}.png` })
|
|
639
|
+
await trackEvent('image_generated', { userId: payload.userId })
|
|
640
|
+
return { success: true }
|
|
641
|
+
}
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
// Register jobs (in your server startup)
|
|
645
|
+
registerJobs([generateImageJob])
|
|
646
|
+
|
|
647
|
+
// Enqueue jobs
|
|
648
|
+
const job = await enqueueJob('generate-image', {
|
|
649
|
+
userId: 'user-123',
|
|
650
|
+
prompt: 'A beautiful sunset'
|
|
651
|
+
})
|
|
652
|
+
|
|
653
|
+
// Check job status
|
|
654
|
+
const status = await getJobStatus(job.id)
|
|
655
|
+
console.log(status) // 'queued', 'running', 'success', 'failed'
|
|
656
|
+
|
|
657
|
+
// Schedule jobs
|
|
658
|
+
await scheduleJob('generate-image', '0 0 * * *', {
|
|
659
|
+
userId: 'system',
|
|
660
|
+
prompt: 'Daily digest image'
|
|
661
|
+
})
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
**React Hooks:**
|
|
665
|
+
|
|
666
|
+
```typescript
|
|
667
|
+
'use client'
|
|
668
|
+
import { useJobStatus, useUserJobs } from 'miragedev-sdk/jobs/client'
|
|
669
|
+
|
|
670
|
+
function JobTracker({ jobId }: { jobId: string }) {
|
|
671
|
+
const { status, result, error, isLoading } = useJobStatus(jobId)
|
|
672
|
+
|
|
673
|
+
if (isLoading) return <div>Loading...</div>
|
|
674
|
+
if (error) return <div>Error: {error.message}</div>
|
|
675
|
+
|
|
676
|
+
return <div>Job Status: {status}</div>
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
function UserJobs({ userId }: { userId: string }) {
|
|
680
|
+
const { jobs, isLoading } = useUserJobs(userId, { limit: 10 })
|
|
681
|
+
|
|
682
|
+
return (
|
|
683
|
+
<ul>
|
|
684
|
+
{jobs.map(job => (
|
|
685
|
+
<li key={job.id}>{job.name}: {job.status}</li>
|
|
686
|
+
))}
|
|
687
|
+
</ul>
|
|
688
|
+
)
|
|
689
|
+
}
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
**Configuration:**
|
|
693
|
+
|
|
694
|
+
```typescript
|
|
695
|
+
initMirageDev({
|
|
696
|
+
jobs: {
|
|
697
|
+
provider: 'trigger',
|
|
698
|
+
trigger: {
|
|
699
|
+
apiKey: process.env.TRIGGER_API_KEY!,
|
|
700
|
+
apiUrl: process.env.TRIGGER_API_URL || 'https://api.trigger.dev',
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
})
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
**Environment Variables:**
|
|
707
|
+
- `TRIGGER_API_KEY`
|
|
708
|
+
- `TRIGGER_API_URL` (optional)
|
|
709
|
+
|
|
710
|
+
### 🔗 Module Integration
|
|
711
|
+
|
|
712
|
+
Combine modules for powerful SaaS workflows.
|
|
713
|
+
|
|
714
|
+
**Example: AI Image Generation SaaS**
|
|
715
|
+
|
|
716
|
+
```typescript
|
|
717
|
+
import { checkRateLimit, checkCircuitBreaker } from 'miragedev-sdk/limits'
|
|
718
|
+
import { enqueueJob } from 'miragedev-sdk/jobs'
|
|
719
|
+
import { uploadFile } from 'miragedev-sdk/storage'
|
|
720
|
+
import { trackEvent, incrementUsage } from 'miragedev-sdk/analytics'
|
|
721
|
+
|
|
722
|
+
export async function POST(req: Request) {
|
|
723
|
+
const { userId, prompt } = await req.json()
|
|
724
|
+
|
|
725
|
+
// Step 1: Check rate limits
|
|
726
|
+
const userLimit = await checkRateLimit({
|
|
727
|
+
key: `generate:${userId}`,
|
|
728
|
+
limit: 10,
|
|
729
|
+
window: 3600 // 10 images per hour
|
|
730
|
+
})
|
|
731
|
+
|
|
732
|
+
if (!userLimit.allowed) {
|
|
733
|
+
return Response.json({ error: 'Rate limit exceeded' }, { status: 429 })
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// Step 2: Check AI cost circuit breaker
|
|
737
|
+
const aiBreaker = await checkCircuitBreaker({
|
|
738
|
+
key: 'ai:dalle',
|
|
739
|
+
threshold: 10000, // $100/hour limit
|
|
740
|
+
window: 3600,
|
|
741
|
+
cooldown: 300
|
|
742
|
+
})
|
|
743
|
+
|
|
744
|
+
if (!aiBreaker.allowed) {
|
|
745
|
+
return Response.json({ error: 'AI service temporarily unavailable' }, { status: 503 })
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// Step 3: Enqueue background job
|
|
749
|
+
const job = await enqueueJob('generate-image', { userId, prompt })
|
|
750
|
+
|
|
751
|
+
// Step 4: Track analytics
|
|
752
|
+
await trackEvent('image_requested', { userId, prompt })
|
|
753
|
+
await incrementUsage(userId, 'images_generated', 1)
|
|
754
|
+
|
|
755
|
+
// Step 5: Increment AI costs (after successful generation)
|
|
756
|
+
await aiBreaker.increment(40) // $0.40 per image
|
|
757
|
+
|
|
758
|
+
return Response.json({ jobId: job.id, status: 'queued' })
|
|
759
|
+
}
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
**Job Handler:**
|
|
763
|
+
|
|
764
|
+
```typescript
|
|
765
|
+
const generateImageJob = defineJob(
|
|
766
|
+
'generate-image',
|
|
767
|
+
async (payload: { userId: string; prompt: string }) => {
|
|
768
|
+
// Generate image
|
|
769
|
+
const image = await callDalleAPI(payload.prompt)
|
|
770
|
+
|
|
771
|
+
// Upload to CDN
|
|
772
|
+
const result = await uploadFile({
|
|
773
|
+
file: image,
|
|
774
|
+
key: `images/${payload.userId}/${Date.now()}.png`,
|
|
775
|
+
contentType: 'image/png',
|
|
776
|
+
resize: { width: 1024, height: 1024 }
|
|
777
|
+
})
|
|
778
|
+
|
|
779
|
+
// Track success
|
|
780
|
+
await trackEvent('image_generated', {
|
|
781
|
+
userId: payload.userId,
|
|
782
|
+
url: result.url
|
|
783
|
+
})
|
|
784
|
+
|
|
785
|
+
return { url: result.url }
|
|
786
|
+
}
|
|
787
|
+
)
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
This workflow demonstrates:
|
|
791
|
+
- ✅ Rate limiting per user
|
|
792
|
+
- ✅ Circuit breaker for cost control
|
|
793
|
+
- ✅ Background job processing
|
|
794
|
+
- ✅ CDN file storage
|
|
795
|
+
- ✅ Analytics tracking
|
|
796
|
+
```
|
|
797
|
+
|
|
270
798
|
## API Reference
|
|
271
799
|
|
|
272
800
|
Full API documentation with examples for every function is available in the code via JSDoc. Your AI assistant can read these to help you build faster.
|