npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

miragedev-sdk

v0.5.2

Published

AI-first SDK for building SAAS applications with Next.js

Readme

MirageDev SDK

AI-first SDK for building SAAS applications with Next.js. Build production-ready SAAS apps in minutes, not weeks.

npm version License: MIT

✨ 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 dev

That'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 init

What gets created:

  • lib/miragedev.config.ts - SDK configuration
  • .env.local.example - Environment variables template
  • lib/miragedev.init.ts - Initialization file
  • AGENTS.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 authenticated
  • getSession() - Get current session or null

Client-Side:

  • useSession() - React hook for session state
  • signIn(provider?) - Sign in with optional provider
  • signOut() - 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 biometric

Billing 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 users
  • reset-password - Password reset
  • subscription-created - Subscription confirmation
  • subscription-canceled - Cancellation notice
  • invoice - 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.js

Mobile 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.jpg

Configuration:

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_ID
  • CLOUDFLARE_ACCESS_KEY_ID
  • CLOUDFLARE_SECRET_ACCESS_KEY
  • CLOUDFLARE_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_KEY
  • POSTHOG_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 spent

Configuration:

initMirageDev({
  limits: {
    provider: 'upstash',
    upstash: {
      url: process.env.UPSTASH_REDIS_URL!,
      token: process.env.UPSTASH_REDIS_TOKEN!,
    }
  }
})

Environment Variables:

  • UPSTASH_REDIS_URL
  • UPSTASH_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_KEY
  • TRIGGER_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 release

No 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 --tags

After Publishing

Users can install the latest version:

npm install miragedev-sdk@latest

License

MIT © MirageDev

Support


Built with ❤️ for the Next.js community