miragedev-sdk 0.3.0 → 0.5.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 +494 -11
- package/dist/ai/index.cjs +184 -0
- package/dist/ai/index.d.cts +37 -3
- package/dist/ai/index.d.ts +37 -3
- package/dist/ai/index.js +182 -1
- package/dist/{ai-DTlWq4Xf.d.cts → ai-CdJK6ox8.d.cts} +21 -1
- package/dist/{ai-DTlWq4Xf.d.ts → ai-CdJK6ox8.d.ts} +21 -1
- 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-DF5ZFP6K.cjs +177 -0
- package/dist/chunk-EALJHNNK.cjs +184 -0
- package/dist/chunk-H2CCSTQQ.js +1 -0
- package/dist/{chunk-ONCJKP65.js → chunk-KYWT4U6H.js} +1 -1
- package/dist/chunk-NAGQOCLL.js +177 -0
- package/dist/{chunk-7ZDFU6CA.cjs → chunk-NIUFPKQE.cjs} +85 -1
- package/dist/{chunk-J7DLQ6NM.js → chunk-SY4VNBJR.js} +85 -1
- package/dist/chunk-VNG3IPWG.cjs +2 -0
- package/dist/{chunk-BKLMAYJY.cjs → chunk-XZ7EEGLT.cjs} +2 -2
- package/dist/email/index.cjs +3 -2
- package/dist/email/index.js +2 -1
- package/dist/index.cjs +3 -2
- package/dist/index.d.cts +262 -3
- package/dist/index.d.ts +262 -3
- package/dist/index.js +2 -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 +49 -2
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
|
|
|
@@ -380,15 +402,476 @@ const response = await createEmbeddings({
|
|
|
380
402
|
console.log(response.embeddings[0]) // [0.123, -0.456, ...]
|
|
381
403
|
```
|
|
382
404
|
|
|
405
|
+
**Image Generation:**
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
import { generateImage } from 'miragedev-sdk/ai'
|
|
409
|
+
|
|
410
|
+
const result = await generateImage({
|
|
411
|
+
prompt: 'A serene mountain landscape at sunset',
|
|
412
|
+
model: 'dall-e-3',
|
|
413
|
+
size: '1024x1024',
|
|
414
|
+
quality: 'standard'
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
console.log(result.images[0].url) // https://oaidalleapiprodscus.blob...
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
**Client-Side Hook:**
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
'use client'
|
|
424
|
+
import { useImageGeneration } from 'miragedev-sdk/ai'
|
|
425
|
+
|
|
426
|
+
export function ImageGenerator() {
|
|
427
|
+
const { generate, isLoading, result } = useImageGeneration()
|
|
428
|
+
|
|
429
|
+
return (
|
|
430
|
+
<div>
|
|
431
|
+
<button onClick={() => generate('A sunset')}>
|
|
432
|
+
{isLoading ? 'Generating...' : 'Generate'}
|
|
433
|
+
</button>
|
|
434
|
+
{result && <img src={result.images[0].url} alt="Generated" />}
|
|
435
|
+
</div>
|
|
436
|
+
)
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
**With Rate Limiting:**
|
|
441
|
+
|
|
442
|
+
```typescript
|
|
443
|
+
import { generateImage } from 'miragedev-sdk/ai'
|
|
444
|
+
import { checkRateLimit } from 'miragedev-sdk/limits'
|
|
445
|
+
|
|
446
|
+
const limit = await checkRateLimit({
|
|
447
|
+
key: `images:${userId}`,
|
|
448
|
+
limit: 10,
|
|
449
|
+
window: 3600
|
|
450
|
+
})
|
|
451
|
+
|
|
452
|
+
if (!limit.allowed) {
|
|
453
|
+
throw new Error('Rate limit exceeded')
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const result = await generateImage({ prompt: 'A sunset' })
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**Save to Permanent Storage:**
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
import { generateImage } from 'miragedev-sdk/ai'
|
|
463
|
+
import { uploadFile } from 'miragedev-sdk/storage'
|
|
464
|
+
|
|
465
|
+
const result = await generateImage({ prompt: 'A sunset' })
|
|
466
|
+
|
|
467
|
+
// Download and upload to R2
|
|
468
|
+
const response = await fetch(result.images[0].url)
|
|
469
|
+
const buffer = await response.arrayBuffer()
|
|
470
|
+
|
|
471
|
+
await uploadFile({
|
|
472
|
+
file: Buffer.from(buffer),
|
|
473
|
+
key: `images/${userId}/${Date.now()}.png`,
|
|
474
|
+
contentType: 'image/png'
|
|
475
|
+
})
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
**Cost Warning:** DALL-E 3 costs $0.04-$0.12 per image. Always implement rate limiting.
|
|
479
|
+
|
|
383
480
|
**Environment Variables:**
|
|
384
481
|
|
|
385
482
|
```env
|
|
386
483
|
OPENAI_API_KEY=sk-...
|
|
387
484
|
```
|
|
388
485
|
|
|
486
|
+
**Supported Models:**
|
|
487
|
+
- ✅ DALL-E 2 (256x256, 512x512, 1024x1024)
|
|
488
|
+
- ✅ DALL-E 3 (1024x1024, 1792x1024, 1024x1792, HD quality)
|
|
489
|
+
|
|
389
490
|
**Supported Providers:**
|
|
390
|
-
- ✅ OpenAI (GPT-4, GPT-3.5, embeddings)
|
|
491
|
+
- ✅ OpenAI (GPT-4, GPT-3.5, embeddings, DALL-E)
|
|
391
492
|
- 🔜 Anthropic (Claude) - Coming soon
|
|
493
|
+
|
|
494
|
+
### 📦 Storage Module
|
|
495
|
+
|
|
496
|
+
Upload and manage files with CDN integration.
|
|
497
|
+
|
|
498
|
+
**Features:**
|
|
499
|
+
- File upload with automatic optimization (resize, compress)
|
|
500
|
+
- CDN integration (Cloudflare R2)
|
|
501
|
+
- Pre-signed upload URLs for client-side uploads
|
|
502
|
+
- Public URL generation
|
|
503
|
+
- File metadata tracking
|
|
504
|
+
|
|
505
|
+
**Quick Start:**
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
import { uploadFile, getPublicUrl } from 'miragedev-sdk/storage'
|
|
509
|
+
|
|
510
|
+
// Upload a file
|
|
511
|
+
const result = await uploadFile({
|
|
512
|
+
file: imageBuffer,
|
|
513
|
+
key: 'avatars/user-123.jpg',
|
|
514
|
+
contentType: 'image/jpeg',
|
|
515
|
+
resize: { width: 800, height: 800, fit: 'cover' }
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
// Get public CDN URL
|
|
519
|
+
const url = await getPublicUrl(result.key)
|
|
520
|
+
console.log(url) // https://cdn.example.com/avatars/user-123.jpg
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
**Configuration:**
|
|
524
|
+
|
|
525
|
+
```typescript
|
|
526
|
+
import { initMirageDev } from 'miragedev-sdk'
|
|
527
|
+
|
|
528
|
+
initMirageDev({
|
|
529
|
+
storage: {
|
|
530
|
+
provider: 'cloudflare-r2',
|
|
531
|
+
cloudflareR2: {
|
|
532
|
+
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
|
|
533
|
+
accessKeyId: process.env.CLOUDFLARE_ACCESS_KEY_ID!,
|
|
534
|
+
secretAccessKey: process.env.CLOUDFLARE_SECRET_ACCESS_KEY!,
|
|
535
|
+
bucketName: process.env.CLOUDFLARE_BUCKET_NAME!,
|
|
536
|
+
cdnUrl: 'https://cdn.example.com', // Optional
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
})
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
**Environment Variables:**
|
|
543
|
+
- `CLOUDFLARE_ACCOUNT_ID`
|
|
544
|
+
- `CLOUDFLARE_ACCESS_KEY_ID`
|
|
545
|
+
- `CLOUDFLARE_SECRET_ACCESS_KEY`
|
|
546
|
+
- `CLOUDFLARE_BUCKET_NAME`
|
|
547
|
+
|
|
548
|
+
### 📊 Analytics Module
|
|
549
|
+
|
|
550
|
+
Track events, identify users, and monitor usage limits.
|
|
551
|
+
|
|
552
|
+
**Features:**
|
|
553
|
+
- Event tracking with properties
|
|
554
|
+
- User identification and properties
|
|
555
|
+
- Usage tracking with Redis storage
|
|
556
|
+
- Automatic integration with Limits module
|
|
557
|
+
|
|
558
|
+
**Quick Start:**
|
|
559
|
+
|
|
560
|
+
```typescript
|
|
561
|
+
import { trackEvent, identifyUser, incrementUsage, getUsage } from 'miragedev-sdk/analytics'
|
|
562
|
+
|
|
563
|
+
// Track events
|
|
564
|
+
await trackEvent('image_generated', {
|
|
565
|
+
userId: 'user-123',
|
|
566
|
+
model: 'dalle-3',
|
|
567
|
+
credits: 10
|
|
568
|
+
})
|
|
569
|
+
|
|
570
|
+
// Identify users
|
|
571
|
+
await identifyUser('user-123', {
|
|
572
|
+
email: 'user@example.com',
|
|
573
|
+
plan: 'pro',
|
|
574
|
+
signupDate: '2026-01-01'
|
|
575
|
+
})
|
|
576
|
+
|
|
577
|
+
// Track usage limits
|
|
578
|
+
await incrementUsage('user-123', 'api_calls', 1)
|
|
579
|
+
const usage = await getUsage('user-123', 'api_calls')
|
|
580
|
+
console.log(`API calls: ${usage}`)
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
**React Hooks:**
|
|
584
|
+
|
|
585
|
+
```typescript
|
|
586
|
+
'use client'
|
|
587
|
+
import { useAnalytics, useUsageLimits, usePageTracking } from 'miragedev-sdk/analytics/client'
|
|
588
|
+
|
|
589
|
+
function Dashboard() {
|
|
590
|
+
const { trackEvent } = useAnalytics()
|
|
591
|
+
const { usage, increment, reset } = useUsageLimits('user-123', 'api_calls')
|
|
592
|
+
|
|
593
|
+
usePageTracking() // Auto-track page views
|
|
594
|
+
|
|
595
|
+
return <div>API Calls: {usage}</div>
|
|
596
|
+
}
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
**Configuration:**
|
|
600
|
+
|
|
601
|
+
```typescript
|
|
602
|
+
initMirageDev({
|
|
603
|
+
analytics: {
|
|
604
|
+
provider: 'posthog',
|
|
605
|
+
posthog: {
|
|
606
|
+
apiKey: process.env.POSTHOG_API_KEY!,
|
|
607
|
+
host: process.env.POSTHOG_HOST || 'https://app.posthog.com',
|
|
608
|
+
}
|
|
609
|
+
},
|
|
610
|
+
limits: { // Required for usage tracking
|
|
611
|
+
provider: 'upstash',
|
|
612
|
+
upstash: {
|
|
613
|
+
url: process.env.UPSTASH_REDIS_URL!,
|
|
614
|
+
token: process.env.UPSTASH_REDIS_TOKEN!,
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
})
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
**Environment Variables:**
|
|
621
|
+
- `POSTHOG_API_KEY`
|
|
622
|
+
- `POSTHOG_HOST` (optional)
|
|
623
|
+
|
|
624
|
+
### 🚦 Limits Module
|
|
625
|
+
|
|
626
|
+
Rate limiting and circuit breaker for cost control.
|
|
627
|
+
|
|
628
|
+
**Features:**
|
|
629
|
+
- Sliding window rate limiting
|
|
630
|
+
- Multi-tier rate limits
|
|
631
|
+
- Circuit breaker pattern
|
|
632
|
+
- Cost protection for AI APIs
|
|
633
|
+
- Redis-backed counters
|
|
634
|
+
|
|
635
|
+
**Quick Start:**
|
|
636
|
+
|
|
637
|
+
```typescript
|
|
638
|
+
import { checkRateLimit, checkCircuitBreaker, checkMultipleRateLimits } from 'miragedev-sdk/limits'
|
|
639
|
+
|
|
640
|
+
// Rate limiting
|
|
641
|
+
const result = await checkRateLimit({
|
|
642
|
+
key: 'api:user:123',
|
|
643
|
+
limit: 100,
|
|
644
|
+
window: 3600 // 1 hour
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
if (!result.allowed) {
|
|
648
|
+
throw new Error(`Rate limit exceeded. Try again in ${result.resetIn}s`)
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// Multi-tier rate limits
|
|
652
|
+
const [ipLimit, userLimit] = await checkMultipleRateLimits([
|
|
653
|
+
{ key: 'api:ip:1.2.3.4', limit: 1000, window: 3600 },
|
|
654
|
+
{ key: 'api:user:123', limit: 100, window: 3600 },
|
|
655
|
+
])
|
|
656
|
+
|
|
657
|
+
if (!ipLimit.allowed || !userLimit.allowed) {
|
|
658
|
+
throw new Error('Rate limit exceeded')
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Circuit breaker for AI costs
|
|
662
|
+
const breaker = await checkCircuitBreaker({
|
|
663
|
+
key: 'ai:openai',
|
|
664
|
+
threshold: 1000, // $10 limit
|
|
665
|
+
window: 3600,
|
|
666
|
+
cooldown: 300
|
|
667
|
+
})
|
|
668
|
+
|
|
669
|
+
if (!breaker.allowed) {
|
|
670
|
+
throw new Error('AI spending limit reached')
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// After successful AI call
|
|
674
|
+
await breaker.increment(5) // $0.05 spent
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
**Configuration:**
|
|
678
|
+
|
|
679
|
+
```typescript
|
|
680
|
+
initMirageDev({
|
|
681
|
+
limits: {
|
|
682
|
+
provider: 'upstash',
|
|
683
|
+
upstash: {
|
|
684
|
+
url: process.env.UPSTASH_REDIS_URL!,
|
|
685
|
+
token: process.env.UPSTASH_REDIS_TOKEN!,
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
})
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
**Environment Variables:**
|
|
692
|
+
- `UPSTASH_REDIS_URL`
|
|
693
|
+
- `UPSTASH_REDIS_TOKEN`
|
|
694
|
+
|
|
695
|
+
### ⚙️ Jobs Module
|
|
696
|
+
|
|
697
|
+
Background jobs and scheduled tasks.
|
|
698
|
+
|
|
699
|
+
**Features:**
|
|
700
|
+
- Background job processing
|
|
701
|
+
- Cron scheduling
|
|
702
|
+
- Job status tracking
|
|
703
|
+
- Type-safe job definitions
|
|
704
|
+
- Automatic retries
|
|
705
|
+
|
|
706
|
+
**Quick Start:**
|
|
707
|
+
|
|
708
|
+
```typescript
|
|
709
|
+
import { enqueueJob, getJobStatus, scheduleJob } from 'miragedev-sdk/jobs'
|
|
710
|
+
import { defineJob, registerJobs } from 'miragedev-sdk/jobs/define'
|
|
711
|
+
|
|
712
|
+
// Define job handlers
|
|
713
|
+
const generateImageJob = defineJob(
|
|
714
|
+
'generate-image',
|
|
715
|
+
async (payload: { userId: string; prompt: string }, context) => {
|
|
716
|
+
const image = await generateImage(payload.prompt)
|
|
717
|
+
await uploadFile({ file: image, key: `images/${context.job.id}.png` })
|
|
718
|
+
await trackEvent('image_generated', { userId: payload.userId })
|
|
719
|
+
return { success: true }
|
|
720
|
+
}
|
|
721
|
+
)
|
|
722
|
+
|
|
723
|
+
// Register jobs (in your server startup)
|
|
724
|
+
registerJobs([generateImageJob])
|
|
725
|
+
|
|
726
|
+
// Enqueue jobs
|
|
727
|
+
const job = await enqueueJob('generate-image', {
|
|
728
|
+
userId: 'user-123',
|
|
729
|
+
prompt: 'A beautiful sunset'
|
|
730
|
+
})
|
|
731
|
+
|
|
732
|
+
// Check job status
|
|
733
|
+
const status = await getJobStatus(job.id)
|
|
734
|
+
console.log(status) // 'queued', 'running', 'success', 'failed'
|
|
735
|
+
|
|
736
|
+
// Schedule jobs
|
|
737
|
+
await scheduleJob('generate-image', '0 0 * * *', {
|
|
738
|
+
userId: 'system',
|
|
739
|
+
prompt: 'Daily digest image'
|
|
740
|
+
})
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
**React Hooks:**
|
|
744
|
+
|
|
745
|
+
```typescript
|
|
746
|
+
'use client'
|
|
747
|
+
import { useJobStatus, useUserJobs } from 'miragedev-sdk/jobs/client'
|
|
748
|
+
|
|
749
|
+
function JobTracker({ jobId }: { jobId: string }) {
|
|
750
|
+
const { status, result, error, isLoading } = useJobStatus(jobId)
|
|
751
|
+
|
|
752
|
+
if (isLoading) return <div>Loading...</div>
|
|
753
|
+
if (error) return <div>Error: {error.message}</div>
|
|
754
|
+
|
|
755
|
+
return <div>Job Status: {status}</div>
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
function UserJobs({ userId }: { userId: string }) {
|
|
759
|
+
const { jobs, isLoading } = useUserJobs(userId, { limit: 10 })
|
|
760
|
+
|
|
761
|
+
return (
|
|
762
|
+
<ul>
|
|
763
|
+
{jobs.map(job => (
|
|
764
|
+
<li key={job.id}>{job.name}: {job.status}</li>
|
|
765
|
+
))}
|
|
766
|
+
</ul>
|
|
767
|
+
)
|
|
768
|
+
}
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
**Configuration:**
|
|
772
|
+
|
|
773
|
+
```typescript
|
|
774
|
+
initMirageDev({
|
|
775
|
+
jobs: {
|
|
776
|
+
provider: 'trigger',
|
|
777
|
+
trigger: {
|
|
778
|
+
apiKey: process.env.TRIGGER_API_KEY!,
|
|
779
|
+
apiUrl: process.env.TRIGGER_API_URL || 'https://api.trigger.dev',
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
})
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
**Environment Variables:**
|
|
786
|
+
- `TRIGGER_API_KEY`
|
|
787
|
+
- `TRIGGER_API_URL` (optional)
|
|
788
|
+
|
|
789
|
+
### 🔗 Module Integration
|
|
790
|
+
|
|
791
|
+
Combine modules for powerful SaaS workflows.
|
|
792
|
+
|
|
793
|
+
**Example: AI Image Generation SaaS**
|
|
794
|
+
|
|
795
|
+
```typescript
|
|
796
|
+
import { checkRateLimit, checkCircuitBreaker } from 'miragedev-sdk/limits'
|
|
797
|
+
import { enqueueJob } from 'miragedev-sdk/jobs'
|
|
798
|
+
import { uploadFile } from 'miragedev-sdk/storage'
|
|
799
|
+
import { trackEvent, incrementUsage } from 'miragedev-sdk/analytics'
|
|
800
|
+
|
|
801
|
+
export async function POST(req: Request) {
|
|
802
|
+
const { userId, prompt } = await req.json()
|
|
803
|
+
|
|
804
|
+
// Step 1: Check rate limits
|
|
805
|
+
const userLimit = await checkRateLimit({
|
|
806
|
+
key: `generate:${userId}`,
|
|
807
|
+
limit: 10,
|
|
808
|
+
window: 3600 // 10 images per hour
|
|
809
|
+
})
|
|
810
|
+
|
|
811
|
+
if (!userLimit.allowed) {
|
|
812
|
+
return Response.json({ error: 'Rate limit exceeded' }, { status: 429 })
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// Step 2: Check AI cost circuit breaker
|
|
816
|
+
const aiBreaker = await checkCircuitBreaker({
|
|
817
|
+
key: 'ai:dalle',
|
|
818
|
+
threshold: 10000, // $100/hour limit
|
|
819
|
+
window: 3600,
|
|
820
|
+
cooldown: 300
|
|
821
|
+
})
|
|
822
|
+
|
|
823
|
+
if (!aiBreaker.allowed) {
|
|
824
|
+
return Response.json({ error: 'AI service temporarily unavailable' }, { status: 503 })
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// Step 3: Enqueue background job
|
|
828
|
+
const job = await enqueueJob('generate-image', { userId, prompt })
|
|
829
|
+
|
|
830
|
+
// Step 4: Track analytics
|
|
831
|
+
await trackEvent('image_requested', { userId, prompt })
|
|
832
|
+
await incrementUsage(userId, 'images_generated', 1)
|
|
833
|
+
|
|
834
|
+
// Step 5: Increment AI costs (after successful generation)
|
|
835
|
+
await aiBreaker.increment(40) // $0.40 per image
|
|
836
|
+
|
|
837
|
+
return Response.json({ jobId: job.id, status: 'queued' })
|
|
838
|
+
}
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
**Job Handler:**
|
|
842
|
+
|
|
843
|
+
```typescript
|
|
844
|
+
const generateImageJob = defineJob(
|
|
845
|
+
'generate-image',
|
|
846
|
+
async (payload: { userId: string; prompt: string }) => {
|
|
847
|
+
// Generate image
|
|
848
|
+
const image = await callDalleAPI(payload.prompt)
|
|
849
|
+
|
|
850
|
+
// Upload to CDN
|
|
851
|
+
const result = await uploadFile({
|
|
852
|
+
file: image,
|
|
853
|
+
key: `images/${payload.userId}/${Date.now()}.png`,
|
|
854
|
+
contentType: 'image/png',
|
|
855
|
+
resize: { width: 1024, height: 1024 }
|
|
856
|
+
})
|
|
857
|
+
|
|
858
|
+
// Track success
|
|
859
|
+
await trackEvent('image_generated', {
|
|
860
|
+
userId: payload.userId,
|
|
861
|
+
url: result.url
|
|
862
|
+
})
|
|
863
|
+
|
|
864
|
+
return { url: result.url }
|
|
865
|
+
}
|
|
866
|
+
)
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
This workflow demonstrates:
|
|
870
|
+
- ✅ Rate limiting per user
|
|
871
|
+
- ✅ Circuit breaker for cost control
|
|
872
|
+
- ✅ Background job processing
|
|
873
|
+
- ✅ CDN file storage
|
|
874
|
+
- ✅ Analytics tracking
|
|
392
875
|
```
|
|
393
876
|
|
|
394
877
|
## API Reference
|