miragedev-sdk 0.3.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 +414 -10
- package/dist/ai/index.d.cts +2 -2
- package/dist/ai/index.d.ts +2 -2
- package/dist/{ai-DTlWq4Xf.d.cts → ai-B4sdf-PH.d.cts} +1 -1
- package/dist/{ai-DTlWq4Xf.d.ts → ai-B4sdf-PH.d.ts} +1 -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
|
|
|
@@ -389,6 +411,388 @@ OPENAI_API_KEY=sk-...
|
|
|
389
411
|
**Supported Providers:**
|
|
390
412
|
- ✅ OpenAI (GPT-4, GPT-3.5, embeddings)
|
|
391
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
|
|
392
796
|
```
|
|
393
797
|
|
|
394
798
|
## API Reference
|
package/dist/ai/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as AIConfig, C as ChatOptions,
|
|
2
|
-
export {
|
|
1
|
+
import { A as AIConfig, C as ChatOptions, a as ChatResponse, S as StreamChunk, E as EmbeddingOptions, b as EmbeddingResponse } from '../ai-B4sdf-PH.cjs';
|
|
2
|
+
export { f as AIFunction, e as AIMessage, c as AIProvider, d as AIRole } from '../ai-B4sdf-PH.cjs';
|
|
3
3
|
|
|
4
4
|
declare function setAIConfig(config: Partial<AIConfig>): void;
|
|
5
5
|
declare function getAIConfig(): AIConfig;
|
package/dist/ai/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as AIConfig, C as ChatOptions,
|
|
2
|
-
export {
|
|
1
|
+
import { A as AIConfig, C as ChatOptions, a as ChatResponse, S as StreamChunk, E as EmbeddingOptions, b as EmbeddingResponse } from '../ai-B4sdf-PH.js';
|
|
2
|
+
export { f as AIFunction, e as AIMessage, c as AIProvider, d as AIRole } from '../ai-B4sdf-PH.js';
|
|
3
3
|
|
|
4
4
|
declare function setAIConfig(config: Partial<AIConfig>): void;
|
|
5
5
|
declare function getAIConfig(): AIConfig;
|
|
@@ -69,4 +69,4 @@ type EmbeddingResponse = {
|
|
|
69
69
|
};
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
-
export type { AIConfig as A, ChatOptions as C, EmbeddingOptions as E, StreamChunk as S,
|
|
72
|
+
export type { AIConfig as A, ChatOptions as C, EmbeddingOptions as E, StreamChunk as S, ChatResponse as a, EmbeddingResponse as b, AIProvider as c, AIRole as d, AIMessage as e, AIFunction as f };
|
|
@@ -69,4 +69,4 @@ type EmbeddingResponse = {
|
|
|
69
69
|
};
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
-
export type { AIConfig as A, ChatOptions as C, EmbeddingOptions as E, StreamChunk as S,
|
|
72
|
+
export type { AIConfig as A, ChatOptions as C, EmbeddingOptions as E, StreamChunk as S, ChatResponse as a, EmbeddingResponse as b, AIProvider as c, AIRole as d, AIMessage as e, AIFunction as f };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
require('../chunk-75ZPJI57.cjs');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
|
|
6
|
+
function useAnalytics() {
|
|
7
|
+
const track = react.useCallback(
|
|
8
|
+
async (eventName, properties) => {
|
|
9
|
+
try {
|
|
10
|
+
await fetch("/api/analytics/track", {
|
|
11
|
+
method: "POST",
|
|
12
|
+
headers: { "Content-Type": "application/json" },
|
|
13
|
+
body: JSON.stringify({ eventName, properties })
|
|
14
|
+
});
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error("Analytics tracking error:", error);
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
[]
|
|
20
|
+
);
|
|
21
|
+
const identify = react.useCallback(
|
|
22
|
+
async (userId, properties) => {
|
|
23
|
+
try {
|
|
24
|
+
await fetch("/api/analytics/identify", {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: { "Content-Type": "application/json" },
|
|
27
|
+
body: JSON.stringify({ userId, properties })
|
|
28
|
+
});
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error("Analytics identify error:", error);
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
[]
|
|
34
|
+
);
|
|
35
|
+
return { track, identify };
|
|
36
|
+
}
|
|
37
|
+
function useUsageLimits(metric) {
|
|
38
|
+
const [usage, setUsage] = react.useState(0);
|
|
39
|
+
const [limit, setLimit] = react.useState(void 0);
|
|
40
|
+
const [isLoading, setIsLoading] = react.useState(true);
|
|
41
|
+
const [error, setError] = react.useState(null);
|
|
42
|
+
react.useEffect(() => {
|
|
43
|
+
const fetchUsage = async () => {
|
|
44
|
+
try {
|
|
45
|
+
setIsLoading(true);
|
|
46
|
+
const response = await fetch(
|
|
47
|
+
`/api/analytics/usage?metric=${encodeURIComponent(metric)}`
|
|
48
|
+
);
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
throw new Error(`Failed to fetch usage: ${response.statusText}`);
|
|
51
|
+
}
|
|
52
|
+
const data = await response.json();
|
|
53
|
+
setUsage(data.usage || 0);
|
|
54
|
+
setLimit(data.limit);
|
|
55
|
+
setError(null);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
setError(err instanceof Error ? err : new Error("Unknown error"));
|
|
58
|
+
} finally {
|
|
59
|
+
setIsLoading(false);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
fetchUsage();
|
|
63
|
+
}, [metric]);
|
|
64
|
+
const percentage = limit ? Math.min(Math.round(usage / limit * 100), 100) : 0;
|
|
65
|
+
const isLimitReached = limit ? usage >= limit : false;
|
|
66
|
+
const remaining = limit ? Math.max(limit - usage, 0) : 0;
|
|
67
|
+
return {
|
|
68
|
+
usage,
|
|
69
|
+
limit,
|
|
70
|
+
percentage,
|
|
71
|
+
isLimitReached,
|
|
72
|
+
remaining,
|
|
73
|
+
isLoading,
|
|
74
|
+
error
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function usePageTracking(additionalProperties) {
|
|
78
|
+
const { track } = useAnalytics();
|
|
79
|
+
react.useEffect(() => {
|
|
80
|
+
const trackPageView = async () => {
|
|
81
|
+
track("page_view", {
|
|
82
|
+
path: window.location.pathname,
|
|
83
|
+
search: window.location.search,
|
|
84
|
+
...additionalProperties
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
trackPageView();
|
|
88
|
+
}, [track, additionalProperties]);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
exports.useAnalytics = useAnalytics;
|
|
92
|
+
exports.usePageTracking = usePageTracking;
|
|
93
|
+
exports.useUsageLimits = useUsageLimits;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { E as EventProperties, U as UserProperties } from '../analytics-KVIUba3B.cjs';
|
|
2
|
+
|
|
3
|
+
declare function useAnalytics(): {
|
|
4
|
+
track: (eventName: string, properties?: EventProperties) => Promise<void>;
|
|
5
|
+
identify: (userId: string, properties?: UserProperties) => Promise<void>;
|
|
6
|
+
};
|
|
7
|
+
interface UsageLimitsData {
|
|
8
|
+
usage: number;
|
|
9
|
+
limit?: number;
|
|
10
|
+
percentage: number;
|
|
11
|
+
isLimitReached: boolean;
|
|
12
|
+
remaining: number;
|
|
13
|
+
isLoading: boolean;
|
|
14
|
+
error: Error | null;
|
|
15
|
+
}
|
|
16
|
+
declare function useUsageLimits(metric: string): UsageLimitsData;
|
|
17
|
+
declare function usePageTracking(additionalProperties?: EventProperties): void;
|
|
18
|
+
|
|
19
|
+
export { type UsageLimitsData, useAnalytics, usePageTracking, useUsageLimits };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { E as EventProperties, U as UserProperties } from '../analytics-KVIUba3B.js';
|
|
2
|
+
|
|
3
|
+
declare function useAnalytics(): {
|
|
4
|
+
track: (eventName: string, properties?: EventProperties) => Promise<void>;
|
|
5
|
+
identify: (userId: string, properties?: UserProperties) => Promise<void>;
|
|
6
|
+
};
|
|
7
|
+
interface UsageLimitsData {
|
|
8
|
+
usage: number;
|
|
9
|
+
limit?: number;
|
|
10
|
+
percentage: number;
|
|
11
|
+
isLimitReached: boolean;
|
|
12
|
+
remaining: number;
|
|
13
|
+
isLoading: boolean;
|
|
14
|
+
error: Error | null;
|
|
15
|
+
}
|
|
16
|
+
declare function useUsageLimits(metric: string): UsageLimitsData;
|
|
17
|
+
declare function usePageTracking(additionalProperties?: EventProperties): void;
|
|
18
|
+
|
|
19
|
+
export { type UsageLimitsData, useAnalytics, usePageTracking, useUsageLimits };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import '../chunk-MLKGABMK.js';
|
|
2
|
+
import { useCallback, useState, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
function useAnalytics() {
|
|
5
|
+
const track = useCallback(
|
|
6
|
+
async (eventName, properties) => {
|
|
7
|
+
try {
|
|
8
|
+
await fetch("/api/analytics/track", {
|
|
9
|
+
method: "POST",
|
|
10
|
+
headers: { "Content-Type": "application/json" },
|
|
11
|
+
body: JSON.stringify({ eventName, properties })
|
|
12
|
+
});
|
|
13
|
+
} catch (error) {
|
|
14
|
+
console.error("Analytics tracking error:", error);
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
[]
|
|
18
|
+
);
|
|
19
|
+
const identify = useCallback(
|
|
20
|
+
async (userId, properties) => {
|
|
21
|
+
try {
|
|
22
|
+
await fetch("/api/analytics/identify", {
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: { "Content-Type": "application/json" },
|
|
25
|
+
body: JSON.stringify({ userId, properties })
|
|
26
|
+
});
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error("Analytics identify error:", error);
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
[]
|
|
32
|
+
);
|
|
33
|
+
return { track, identify };
|
|
34
|
+
}
|
|
35
|
+
function useUsageLimits(metric) {
|
|
36
|
+
const [usage, setUsage] = useState(0);
|
|
37
|
+
const [limit, setLimit] = useState(void 0);
|
|
38
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
39
|
+
const [error, setError] = useState(null);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const fetchUsage = async () => {
|
|
42
|
+
try {
|
|
43
|
+
setIsLoading(true);
|
|
44
|
+
const response = await fetch(
|
|
45
|
+
`/api/analytics/usage?metric=${encodeURIComponent(metric)}`
|
|
46
|
+
);
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
throw new Error(`Failed to fetch usage: ${response.statusText}`);
|
|
49
|
+
}
|
|
50
|
+
const data = await response.json();
|
|
51
|
+
setUsage(data.usage || 0);
|
|
52
|
+
setLimit(data.limit);
|
|
53
|
+
setError(null);
|
|
54
|
+
} catch (err) {
|
|
55
|
+
setError(err instanceof Error ? err : new Error("Unknown error"));
|
|
56
|
+
} finally {
|
|
57
|
+
setIsLoading(false);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
fetchUsage();
|
|
61
|
+
}, [metric]);
|
|
62
|
+
const percentage = limit ? Math.min(Math.round(usage / limit * 100), 100) : 0;
|
|
63
|
+
const isLimitReached = limit ? usage >= limit : false;
|
|
64
|
+
const remaining = limit ? Math.max(limit - usage, 0) : 0;
|
|
65
|
+
return {
|
|
66
|
+
usage,
|
|
67
|
+
limit,
|
|
68
|
+
percentage,
|
|
69
|
+
isLimitReached,
|
|
70
|
+
remaining,
|
|
71
|
+
isLoading,
|
|
72
|
+
error
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function usePageTracking(additionalProperties) {
|
|
76
|
+
const { track } = useAnalytics();
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
const trackPageView = async () => {
|
|
79
|
+
track("page_view", {
|
|
80
|
+
path: window.location.pathname,
|
|
81
|
+
search: window.location.search,
|
|
82
|
+
...additionalProperties
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
trackPageView();
|
|
86
|
+
}, [track, additionalProperties]);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { useAnalytics, usePageTracking, useUsageLimits };
|