miragedev-sdk
v0.5.2
Published
AI-first SDK for building SAAS applications with Next.js
Maintainers
Readme
MirageDev SDK
AI-first SDK for building SAAS applications with Next.js. Build production-ready SAAS apps in minutes, not weeks.
✨ Features
Core Modules
- 🔐 Authentication: Supabase (email, magic link, OAuth)
- 📧 Email: Resend (transactional emails, templates)
- 🤖 AI: OpenAI, Anthropic, Replicate (LLMs, image generation)
- 💳 Payments: Stripe (subscriptions, one-time payments)
- 📦 Storage: Cloudflare R2 (file upload, CDN integration)
- 📊 Analytics: PostHog (event tracking, usage limits)
- 🚦 Limits: Upstash Redis (rate limiting, circuit breaker)
- ⚙️ Jobs: Trigger.dev (background jobs, scheduling)
Developer Experience
- 🎯 Type-safe: Full TypeScript support
- 🔧 Zero-config: Sensible defaults, minimal setup
- 🧩 Modular: Use only what you need
- 📚 Well-documented: Comprehensive guides and examples
- 🚀 Production-ready: Battle-tested patterns
- 📱 PWA: Progressive Web App with offline support
- 🎯 Mobile-First: Optimized hooks for mobile experiences
- 🤖 AI-Friendly: Comprehensive JSDoc for AI code generation
Quick Start (2 minutes)
Option 1: Create New Project (Recommended)
npx miragedev-sdk create my-saas
cd my-saas
npm run devThat's it! You now have a full-featured SAAS with:
- ✅ Next.js 14+ with App Router
- ✅ Authentication (login page included)
- ✅ Protected dashboard
- ✅ Stripe billing setup
- ✅ Email templates
- ✅ Pricing page
- ✅ PWA ready
Option 2: Add to Existing Project
# In your existing Next.js project
npm install miragedev-sdk
# Peer dependencies (install only what you need)
npm install @supabase/supabase-js # For authentication
npm install resend # For email
npm install openai @anthropic-ai/sdk replicate # For AI
npm install stripe # For payments
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner sharp # For storage
npm install posthog-node # For analytics
npm install @upstash/redis # For limits
npm install @trigger.dev/sdk # For jobs
npx miragedev-sdk initWhat gets created:
lib/miragedev.config.ts- SDK configuration.env.local.example- Environment variables templatelib/miragedev.init.ts- Initialization fileAGENTS.md- Instructions for AI assistants (Copilot, Cursor, etc.)
Configure Environment
Copy .env.local.example to .env.local and fill in your API keys:
cp .env.local.example .env.local# Auth
AUTH_SECRET=your-secret-key-here # Generate with: openssl rand -base64 32
# Billing (Stripe)
STRIPE_SECRET_KEY=sk_test_xxx # From https://dashboard.stripe.com/test/apikeys
STRIPE_WEBHOOK_SECRET=whsec_xxx # From https://dashboard.stripe.com/test/webhooks
# Email (Resend)
RESEND_API_KEY=re_xxx # From https://resend.com/api-keys
[email protected]Start Building
// Server Component - Protected Page
import { requireAuth } from 'miragedev-sdk/auth'
export default async function DashboardPage() {
const session = await requireAuth() // Throws if not authenticated
return <div>Welcome {session.user.name}!</div>
}// Client Component - Subscription Button
'use client'
import { useSubscription } from 'miragedev-sdk/billing/client'
export function UpgradeButton() {
const { subscription, isLoading } = useSubscription()
if (subscription?.status === 'active') {
return <button>Manage Subscription</button>
}
return <button>Upgrade to Pro</button>
}Configuration
Auth Module
import { requireAuth, getSession } from 'miragedev-sdk/auth'
import { authMiddleware } from 'miragedev-sdk/auth/middleware'
import { useSession, signIn, signOut } from 'miragedev-sdk/auth/client'
import { enableBiometric, signInWithBiometric } from 'miragedev-sdk/auth/biometric'Server-Side:
requireAuth()- Require authentication, throws error if not authenticatedgetSession()- Get current session or null
Client-Side:
useSession()- React hook for session statesignIn(provider?)- Sign in with optional providersignOut()- Sign out current user
Middleware:
// middleware.ts
export default authMiddleware({
publicRoutes: ['/', '/pricing'],
redirectTo: '/login'
})Biometric:
await enableBiometric() // Enable for current user
const session = await signInWithBiometric() // Sign in with biometricBilling Module
import {
createCheckout,
createPortal,
getSubscription,
cancelSubscription
} from 'miragedev-sdk/billing'
import { useSubscription, openBillingPortal } from 'miragedev-sdk/billing/client'
import { createMobileCheckout, createPaymentIntent } from 'miragedev-sdk/billing/mobile'
import { handleWebhook } from 'miragedev-sdk/billing/webhook'Checkout:
const { url } = await createCheckout({
priceId: 'price_xxx',
userId: session.user.id,
successUrl: 'https://yourapp.com/success',
cancelUrl: 'https://yourapp.com/cancel',
})
redirect(url)Webhooks:
// app/api/webhooks/stripe/route.ts
export async function POST(req: Request) {
return handleWebhook(req, {
onSubscriptionCreated: async (data) => {
await db.user.update({
where: { id: data.userId },
data: { isPro: true }
})
},
onSubscriptionCanceled: async (data) => {
// Handle cancellation
}
})
}Mobile:
// For mobile PWA checkout
const { clientSecret } = await createMobileCheckout({
priceId: 'price_xxx',
userId: user.id,
successUrl: 'myapp://success',
cancelUrl: 'myapp://cancel',
})Email Module
import { sendEmail, sendTemplateEmail } from 'miragedev-sdk/email'Basic Email:
await sendEmail({
to: '[email protected]',
subject: 'Welcome!',
html: '<p>Hello World</p>',
})Template Email:
await sendTemplateEmail({
to: '[email protected]',
template: 'welcome',
data: {
userName: 'John',
actionUrl: 'https://app.com/onboarding'
}
})Available Templates:
welcome- Welcome new usersreset-password- Password resetsubscription-created- Subscription confirmationsubscription-canceled- Cancellation noticeinvoice- Invoice notification
PWA Module
import { configurePWA } from 'miragedev-sdk/pwa'export const pwaConfig = configurePWA({
name: 'My SaaS App',
shortName: 'MySaaS',
theme: '#000000',
backgroundColor: '#ffffff',
offline: {
enabled: true,
pages: ['/', '/dashboard']
}
})
// Use pwaConfig.manifest in Next.js metadata
// Write pwaConfig.serviceWorker to public/sw.jsMobile Module
import {
useNetworkStatus,
useInstallPrompt,
useNotifications,
usePullToRefresh
} from 'miragedev-sdk/mobile'function MyComponent() {
const isOnline = useNetworkStatus()
const { canInstall, promptInstall } = useInstallPrompt()
const { permission, requestPermission } = useNotifications()
const { isRefreshing } = usePullToRefresh(async () => {
await fetchData()
})
// Your component logic
}AI Module
import { chat, chatStream, useChat, setAIConfig } from 'miragedev-sdk/ai'Setup:
// lib/miragedev.init.ts or app setup
import { initMirageDev } from 'miragedev-sdk'
initMirageDev({
ai: {
provider: 'openai',
apiKey: process.env.OPENAI_API_KEY,
defaultModel: 'gpt-4-turbo-preview',
temperature: 0.7,
}
})Server-Side Chat:
import { chat } from 'miragedev-sdk/ai'
export async function POST(req: Request) {
const { message } = await req.json()
const response = await chat({
messages: [
{ role: 'system', content: 'You are a helpful assistant' },
{ role: 'user', content: message }
],
temperature: 0.7,
})
return Response.json({ reply: response.content })
}Streaming (Edge Runtime):
import { chatStream, createEdgeStreamResponse } from 'miragedev-sdk/ai'
export const runtime = 'edge'
export async function POST(req: Request) {
const { messages } = await req.json()
return createEdgeStreamResponse(async (write) => {
await chatStream({ messages }, (chunk) => {
write(chunk.delta)
})
})
}Client-Side Hook:
'use client'
import { useChat } from 'miragedev-sdk/ai'
export function ChatComponent() {
const { messages, sendMessage, isLoading, streamingMessage } = useChat({
stream: true,
initialMessages: [
{ role: 'system', content: 'You are a helpful assistant' }
],
onError: (error) => console.error(error)
})
return (
<div>
{messages.map((msg, i) => (
<div key={i}>
<strong>{msg.role}:</strong> {msg.content}
</div>
))}
{streamingMessage && (
<div>
<strong>assistant:</strong> {streamingMessage}
</div>
)}
<button
onClick={() => sendMessage('Hello!')}
disabled={isLoading}
>
Send Message
</button>
</div>
)
}Embeddings:
import { createEmbeddings } from 'miragedev-sdk/ai'
const response = await createEmbeddings({
input: 'Machine learning is fascinating',
})
console.log(response.embeddings[0]) // [0.123, -0.456, ...]Image Generation:
import { generateImage } from 'miragedev-sdk/ai'
const result = await generateImage({
prompt: 'A serene mountain landscape at sunset',
model: 'dall-e-3',
size: '1024x1024',
quality: 'standard'
})
console.log(result.images[0].url) // https://oaidalleapiprodscus.blob...Client-Side Hook:
'use client'
import { useImageGeneration } from 'miragedev-sdk/ai'
export function ImageGenerator() {
const { generate, isLoading, result } = useImageGeneration()
return (
<div>
<button onClick={() => generate('A sunset')}>
{isLoading ? 'Generating...' : 'Generate'}
</button>
{result && <img src={result.images[0].url} alt="Generated" />}
</div>
)
}With Rate Limiting:
import { generateImage } from 'miragedev-sdk/ai'
import { checkRateLimit } from 'miragedev-sdk/limits'
const limit = await checkRateLimit({
key: `images:${userId}`,
limit: 10,
window: 3600
})
if (!limit.allowed) {
throw new Error('Rate limit exceeded')
}
const result = await generateImage({ prompt: 'A sunset' })Save to Permanent Storage:
import { generateImage } from 'miragedev-sdk/ai'
import { uploadFile } from 'miragedev-sdk/storage'
const result = await generateImage({ prompt: 'A sunset' })
// Download and upload to R2
const response = await fetch(result.images[0].url)
const buffer = await response.arrayBuffer()
await uploadFile({
file: Buffer.from(buffer),
key: `images/${userId}/${Date.now()}.png`,
contentType: 'image/png'
})Cost Warning: DALL-E 3 costs $0.04-$0.12 per image. Always implement rate limiting.
Environment Variables:
OPENAI_API_KEY=sk-...Supported Models:
- ✅ DALL-E 2 (256x256, 512x512, 1024x1024)
- ✅ DALL-E 3 (1024x1024, 1792x1024, 1024x1792, HD quality)
Supported Providers:
- ✅ OpenAI (GPT-4, GPT-3.5, embeddings, DALL-E)
- 🔜 Anthropic (Claude) - Coming soon
📦 Storage Module
Upload and manage files with CDN integration.
Features:
- File upload with automatic optimization (resize, compress)
- CDN integration (Cloudflare R2)
- Pre-signed upload URLs for client-side uploads
- Public URL generation
- File metadata tracking
Quick Start:
import { uploadFile, getPublicUrl } from 'miragedev-sdk/storage'
// Upload a file
const result = await uploadFile({
file: imageBuffer,
key: 'avatars/user-123.jpg',
contentType: 'image/jpeg',
resize: { width: 800, height: 800, fit: 'cover' }
})
// Get public CDN URL
const url = await getPublicUrl(result.key)
console.log(url) // https://cdn.example.com/avatars/user-123.jpgConfiguration:
import { initMirageDev } from 'miragedev-sdk'
initMirageDev({
storage: {
provider: 'cloudflare-r2',
cloudflareR2: {
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
accessKeyId: process.env.CLOUDFLARE_ACCESS_KEY_ID!,
secretAccessKey: process.env.CLOUDFLARE_SECRET_ACCESS_KEY!,
bucketName: process.env.CLOUDFLARE_BUCKET_NAME!,
cdnUrl: 'https://cdn.example.com', // Optional
}
}
})Environment Variables:
CLOUDFLARE_ACCOUNT_IDCLOUDFLARE_ACCESS_KEY_IDCLOUDFLARE_SECRET_ACCESS_KEYCLOUDFLARE_BUCKET_NAME
📊 Analytics Module
Track events, identify users, and monitor usage limits.
Features:
- Event tracking with properties
- User identification and properties
- Usage tracking with Redis storage
- Automatic integration with Limits module
Quick Start:
import { trackEvent, identifyUser, incrementUsage, getUsage } from 'miragedev-sdk/analytics'
// Track events
await trackEvent('image_generated', {
userId: 'user-123',
model: 'dalle-3',
credits: 10
})
// Identify users
await identifyUser('user-123', {
email: '[email protected]',
plan: 'pro',
signupDate: '2026-01-01'
})
// Track usage limits
await incrementUsage('user-123', 'api_calls', 1)
const usage = await getUsage('user-123', 'api_calls')
console.log(`API calls: ${usage}`)React Hooks:
'use client'
import { useAnalytics, useUsageLimits, usePageTracking } from 'miragedev-sdk/analytics/client'
function Dashboard() {
const { trackEvent } = useAnalytics()
const { usage, increment, reset } = useUsageLimits('user-123', 'api_calls')
usePageTracking() // Auto-track page views
return <div>API Calls: {usage}</div>
}Configuration:
initMirageDev({
analytics: {
provider: 'posthog',
posthog: {
apiKey: process.env.POSTHOG_API_KEY!,
host: process.env.POSTHOG_HOST || 'https://app.posthog.com',
}
},
limits: { // Required for usage tracking
provider: 'upstash',
upstash: {
url: process.env.UPSTASH_REDIS_URL!,
token: process.env.UPSTASH_REDIS_TOKEN!,
}
}
})Environment Variables:
POSTHOG_API_KEYPOSTHOG_HOST(optional)
🚦 Limits Module
Rate limiting and circuit breaker for cost control.
Features:
- Sliding window rate limiting
- Multi-tier rate limits
- Circuit breaker pattern
- Cost protection for AI APIs
- Redis-backed counters
Quick Start:
import { checkRateLimit, checkCircuitBreaker, checkMultipleRateLimits } from 'miragedev-sdk/limits'
// Rate limiting
const result = await checkRateLimit({
key: 'api:user:123',
limit: 100,
window: 3600 // 1 hour
})
if (!result.allowed) {
throw new Error(`Rate limit exceeded. Try again in ${result.resetIn}s`)
}
// Multi-tier rate limits
const [ipLimit, userLimit] = await checkMultipleRateLimits([
{ key: 'api:ip:1.2.3.4', limit: 1000, window: 3600 },
{ key: 'api:user:123', limit: 100, window: 3600 },
])
if (!ipLimit.allowed || !userLimit.allowed) {
throw new Error('Rate limit exceeded')
}
// Circuit breaker for AI costs
const breaker = await checkCircuitBreaker({
key: 'ai:openai',
threshold: 1000, // $10 limit
window: 3600,
cooldown: 300
})
if (!breaker.allowed) {
throw new Error('AI spending limit reached')
}
// After successful AI call
await breaker.increment(5) // $0.05 spentConfiguration:
initMirageDev({
limits: {
provider: 'upstash',
upstash: {
url: process.env.UPSTASH_REDIS_URL!,
token: process.env.UPSTASH_REDIS_TOKEN!,
}
}
})Environment Variables:
UPSTASH_REDIS_URLUPSTASH_REDIS_TOKEN
⚙️ Jobs Module
Background jobs and scheduled tasks.
Features:
- Background job processing
- Cron scheduling
- Job status tracking
- Type-safe job definitions
- Automatic retries
Quick Start:
import { enqueueJob, getJobStatus, scheduleJob } from 'miragedev-sdk/jobs'
import { defineJob, registerJobs } from 'miragedev-sdk/jobs/define'
// Define job handlers
const generateImageJob = defineJob(
'generate-image',
async (payload: { userId: string; prompt: string }, context) => {
const image = await generateImage(payload.prompt)
await uploadFile({ file: image, key: `images/${context.job.id}.png` })
await trackEvent('image_generated', { userId: payload.userId })
return { success: true }
}
)
// Register jobs (in your server startup)
registerJobs([generateImageJob])
// Enqueue jobs
const job = await enqueueJob('generate-image', {
userId: 'user-123',
prompt: 'A beautiful sunset'
})
// Check job status
const status = await getJobStatus(job.id)
console.log(status) // 'queued', 'running', 'success', 'failed'
// Schedule jobs
await scheduleJob('generate-image', '0 0 * * *', {
userId: 'system',
prompt: 'Daily digest image'
})React Hooks:
'use client'
import { useJobStatus, useUserJobs } from 'miragedev-sdk/jobs/client'
function JobTracker({ jobId }: { jobId: string }) {
const { status, result, error, isLoading } = useJobStatus(jobId)
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return <div>Job Status: {status}</div>
}
function UserJobs({ userId }: { userId: string }) {
const { jobs, isLoading } = useUserJobs(userId, { limit: 10 })
return (
<ul>
{jobs.map(job => (
<li key={job.id}>{job.name}: {job.status}</li>
))}
</ul>
)
}Configuration:
initMirageDev({
jobs: {
provider: 'trigger',
trigger: {
apiKey: process.env.TRIGGER_API_KEY!,
apiUrl: process.env.TRIGGER_API_URL || 'https://api.trigger.dev',
}
}
})Environment Variables:
TRIGGER_API_KEYTRIGGER_API_URL(optional)
🔗 Module Integration
Combine modules for powerful SaaS workflows.
Example: AI Image Generation SaaS
import { checkRateLimit, checkCircuitBreaker } from 'miragedev-sdk/limits'
import { enqueueJob } from 'miragedev-sdk/jobs'
import { uploadFile } from 'miragedev-sdk/storage'
import { trackEvent, incrementUsage } from 'miragedev-sdk/analytics'
export async function POST(req: Request) {
const { userId, prompt } = await req.json()
// Step 1: Check rate limits
const userLimit = await checkRateLimit({
key: `generate:${userId}`,
limit: 10,
window: 3600 // 10 images per hour
})
if (!userLimit.allowed) {
return Response.json({ error: 'Rate limit exceeded' }, { status: 429 })
}
// Step 2: Check AI cost circuit breaker
const aiBreaker = await checkCircuitBreaker({
key: 'ai:dalle',
threshold: 10000, // $100/hour limit
window: 3600,
cooldown: 300
})
if (!aiBreaker.allowed) {
return Response.json({ error: 'AI service temporarily unavailable' }, { status: 503 })
}
// Step 3: Enqueue background job
const job = await enqueueJob('generate-image', { userId, prompt })
// Step 4: Track analytics
await trackEvent('image_requested', { userId, prompt })
await incrementUsage(userId, 'images_generated', 1)
// Step 5: Increment AI costs (after successful generation)
await aiBreaker.increment(40) // $0.40 per image
return Response.json({ jobId: job.id, status: 'queued' })
}Job Handler:
const generateImageJob = defineJob(
'generate-image',
async (payload: { userId: string; prompt: string }) => {
// Generate image
const image = await callDalleAPI(payload.prompt)
// Upload to CDN
const result = await uploadFile({
file: image,
key: `images/${payload.userId}/${Date.now()}.png`,
contentType: 'image/png',
resize: { width: 1024, height: 1024 }
})
// Track success
await trackEvent('image_generated', {
userId: payload.userId,
url: result.url
})
return { url: result.url }
}
)This workflow demonstrates:
- ✅ Rate limiting per user
- ✅ Circuit breaker for cost control
- ✅ Background job processing
- ✅ CDN file storage
- ✅ Analytics tracking
## API Reference
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.
## Examples
Check out the `examples/` directory for complete working applications:
- `examples/basic-saas` - Minimal SAAS with auth + billing
- `examples/with-biometric` - Biometric authentication example
- `examples/stripe-advanced` - Advanced billing with multiple tiers
## Troubleshooting
### "SDK not initialized" error
Make sure you call `initMirageDev()` before using any SDK functions. Import the initialization in your root layout.
### Webhooks not working
1. Verify your webhook secret is correct in `.env.local`
2. Make sure your webhook endpoint is publicly accessible
3. Check Stripe dashboard for webhook delivery logs
### TypeScript errors
The SDK is fully typed. If you see type errors, make sure you're using TypeScript 5.0+ and have `strict: true` in your tsconfig.json.
## Publishing the SDK
### For Maintainers: Automated Publishing
This SDK uses **automated versioning and publishing** via GitHub Actions.
#### How It Works
When a PR is merged to `main`:
1. ✅ Tests run automatically
2. ✅ Version is bumped based on commit messages:
- `feat:` → minor version (0.1.0 → 0.2.0)
- `fix:` → patch version (0.1.0 → 0.1.1)
- `feat!:` or `BREAKING CHANGE` → major version (0.1.0 → 1.0.0)
3. ✅ package.json is updated and committed
4. ✅ Git tag is created automatically
5. ✅ Package is published to NPM
6. ✅ GitHub Release is created
#### Setup (One Time)
1. Add `NPM_TOKEN` to GitHub repository secrets:
- Go to npmjs.com → Access Tokens → Generate New Token (Automation)
- Copy token
- Go to GitHub repo → Settings → Secrets → New secret
- Name: `NPM_TOKEN`, Value: (paste token)
2. That's it! Now every merge to main auto-publishes.
#### Publishing Process
**Simple workflow:**
```bash
# 1. Create feature branch
git checkout -b feat/new-feature
# 2. Make changes with conventional commits
git commit -m "feat: add new awesome feature"
# 3. Push and create PR
git push origin feat/new-feature
# 4. Merge PR to main
# → GitHub Actions automatically:
# - Runs tests
# - Bumps version (0.1.0 → 0.2.0)
# - Publishes to NPM
# - Creates releaseNo manual npm version or npm publish needed!
Manual Publishing (Emergency Only)
If you need to publish manually:
npm version patch # or minor/major
npm run prepublishOnly # Runs type-check, tests, build
npm publish --access public
git push && git push --tagsAfter Publishing
Users can install the latest version:
npm install miragedev-sdk@latestLicense
MIT © MirageDev
Support
Built with ❤️ for the Next.js community
