Dependencies
Service Dependencies - CleanTextLab
Last Updated: January 5, 2026 Purpose: Document all external dependencies and internal service relationships Audience: Developers, DevOps, and AI assistants
Table of Contents
- External Services
- NPM Package Dependencies
- Internal Service Graph
- API Dependency Chain
- Critical Path Analysis
- Fallback Strategies
External Services
1. Vercel (Hosting & Edge Functions)
Purpose: Application hosting, serverless functions, CDN Critical: ✅ Yes - App won't run without it Failover: None (Vercel or nothing)
Dependencies:
- Production deployment:
cleantextlab.com - Preview deployments: Auto-generated URLs
- Edge Functions:
/api/*routes - Analytics: Vercel Analytics (optional)
Configuration:
vercel.json- Build settings- Environment variables in Vercel dashboard
Cost: Free tier → Pro ($20/mo for team)
2. Clerk (Authentication)
Purpose: User authentication, OAuth, session management Critical: ⚠️ Partial - Tools work without auth, but Pro features require it
Features Used:
- OAuth providers (Google, GitHub)
- Session management
- User metadata storage (Stripe plan)
- Public metadata for entitlements
API Endpoints:
/api/auth/*- Clerk webhook handlers- Middleware:
middleware.tsfor protected routes
Configuration:
// src/middleware.ts
export default clerkMiddleware((auth, req) => {
const isPublicRoute = publicRoutes.includes(req.nextUrl.pathname);
if (!isPublicRoute) {
auth().protect();
}
});
Environment Variables:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_...
CLERK_SECRET_KEY=sk_...
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
Cost: Free (10,000 MAU) → Pro ($25/mo)
Fallback: App continues to work for anonymous users, Pro features disabled
3. Upstash Redis (Cloud Storage)
Purpose: Cloud history sync, API key storage, rate limiting Critical: ⚠️ Partial - Only for Pro users with cloud sync
Features Used:
- History sync (E2E encrypted)
- API key + entitlement storage
- Rate limit counters
- Share links
Implementation (REST KV):
// src/lib/server/storage.ts
const endpoint = `${KV_REST_API_URL}/${cmd}/${encodedArgs}`;
await fetch(endpoint, { method: "POST", headers: { Authorization: `Bearer ${token}` } });
Key Prefixes (examples):
apikey:{key} → ApiKeyRecord
ent:email:{email} → EntitlementRecord
ent:key:{key} → EntitlementRecord
hist:user:{userId} → HistoryItem[]
share:{id} → ShareRecord
Environment Variables:
UPSTASH_REDIS_REST_URL=https://...
UPSTASH_REDIS_REST_TOKEN=...
KV_REST_API_URL=https://... (alias)
KV_REST_API_TOKEN=... (alias)
Cost: Free (10k commands/day) → Pro ($10/mo)
Fallback: localStorage only (no cloud sync)
4. Stripe (Payments)
Purpose: Subscription billing, payment processing Critical: ⚠️ Partial - Only for Pro plan upgrades
Features Used:
- Subscription management
- Webhook handling
- Customer portal
- Usage-based billing (API calls)
Webhooks:
// src/app/api/stripe/webhook/route.ts
export async function POST(req: Request) {
const event = await stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET!
);
switch (event.type) {
case "customer.subscription.created":
// Update Clerk metadata
await clerkClient.users.updateUserMetadata(userId, {
publicMetadata: { stripePlan: "pro" }
});
break;
// ... other events
}
}
Environment Variables:
STRIPE_SECRET_KEY=sk_live_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
Cost: 2.9% + $0.30 per transaction
Fallback: Users can't upgrade, existing Pro users unaffected
5. Google Analytics 4
Purpose: Usage analytics, conversion tracking Critical: ❌ No - Optional analytics
Implementation:
// src/components/AnalyticsScripts.tsx
// Injected only after explicit consent.
<AnalyticsScripts gaId={process.env.NEXT_PUBLIC_GA_ID} />
Events Tracked:
- Tool usage
- Keyboard shortcuts
- API calls
- Conversion events (sign-up, upgrade)
Environment Variables:
NEXT_PUBLIC_GA_ID=G-...
Cost: Free
Fallback: No analytics (graceful degradation, opt-in default)
6. Microsoft Clarity
Purpose: Session analytics and conversion funnel tagging Critical: ❌ No - Optional analytics
Configuration:
NEXT_PUBLIC_CLARITY_ID=...
Cost: Free
Fallback: No session analytics (consent-gated)
7. Resend (Email Delivery)
Purpose: Transactional emails for contact, bug reports, and email capture Critical: ❌ No - App logs to console without Resend configured
Environment Variables:
RESEND_API_KEY=...
FROM_EMAIL=...
Cost: Free tier + usage
Fallback: Email delivery skipped, request still succeeds
8. Sentry (Optional)
Purpose: Error monitoring in production Critical: ❌ No - Optional
Configuration: sentry.client.config.ts, sentry.server.config.ts
Fallback: Errors are only logged to console
9. Google AI / OpenAI (AI SDK)
Purpose: Smart input detection and AI-assisted flows Critical: ❌ No - System falls back to regex-based detection
Endpoints:
generativelanguage.googleapis.com(Google AI)
Fallback: Local heuristics and deterministic routing
NPM Package Dependencies
Production Dependencies
Core Framework
{
"next": "^15.5.9",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"typescript": "^5"
}
Critical: ✅ Yes - App won't compile without these
UI & Styling
{
"tailwindcss": "^4",
"lucide-react": "^0.559.0",
"cmdk": "^1.1.1"
}
Critical: ✅ Yes - UI won't render correctly
Icons Used:
Undo2,Redo2(undo/redo)Copy,Check(copy states)Download,Share2,History(actions)Settings,Moon,Sun(preferences)Zap,Code,FileText,Lock(feature icons)
Authentication
{
"@clerk/nextjs": "^6.36.2"
}
Critical: ⚠️ Partial - Required for Pro features
Storage & KV (REST)
{
"none": "Uses Upstash REST API via fetch"
}
Critical: ⚠️ Partial - Required for cloud sync + API key storage
Payments
{
"stripe": "^16.0.0"
}
Critical: ⚠️ Partial - Required for subscription management
Text & Data Processing
{
"sql-formatter": "^15.6.11",
"cronstrue": "^3.9.0",
"cron-parser": "^5.4.0",
"diff": "^8.0.2",
"fast-xml-parser": "^5.3.3",
"ipaddr.js": "^2.3.0",
"ip-address": "^10.1.0",
"papaparse": "^5.5.3",
"xlsx": "^0.18.5",
"uuid": "^13.0.0",
"reading-time": "^1.5.0",
"pako": "^2.1.0"
}
Critical: ⚠️ Per-tool - Each tool relies on specific library
Fallback: If library fails, show error message for that specific tool
Markdown & Docs
{
"react-markdown": "^10.1.0",
"remark-gfm": "^4.0.1",
"rehype-highlight": "^7.0.2"
}
AI & MCP
{
"ai": "^5.0.113",
"@ai-sdk/google": "^2.0.46",
"@ai-sdk/openai": "^2.0.86",
"@ai-sdk/react": "^2.0.115",
"@modelcontextprotocol/sdk": "^1.25.1",
"js-tiktoken": "^1.0.21"
}
Observability
{
"@sentry/nextjs": "^10.29.0"
}
Development Dependencies
{
"@playwright/test": "^1.57.0",
"playwright-chromium": "^1.57.0",
"eslint": "^9",
"@types/node": "^20.19.26",
"@types/react": "^19",
"@types/react-dom": "^19",
"tsx": "^4.21.0"
}
Critical: ❌ No - Only for development
Internal Service Graph
┌─────────────────────────────────────────────────────────────────┐
│ USER REQUEST │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌────────────────────────┐
│ Next.js Middleware │
│ (Auth Check) │
└────────┬───────────────┘
│
┌────────────┴───────────┐
│ │
▼ ▼
┌────────────────┐ ┌────────────────┐
│ Public Routes │ │ Protected Routes│
│ (Tools, Docs) │ │ (/dashboard) │
└────────┬───────┘ └────────┬────────┘
│ │
│ ▼
│ ┌─────────────────┐
│ │ Clerk Auth │
│ │ (Session) │
│ └────────┬────────┘
│ │
│ ▼
│ ┌─────────────────┐
│ │ Entitlements │
│ │ (isPro check) │
│ └────────┬────────┘
│ │
└───────────┬───────────┘
│
▼
┌───────────────────────┐
│ Tool Component │
│ (Client-side) │
└───────┬───────────────┘
│
┌───────────┼───────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│useUndo │ │useHistory│ │useShare │
│Redo │ │ │ │ │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ localStorage │ │
│ │ (Free users) │ │
│ └────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ Upstash Redis │ │
│ │ (Pro users) │ │
│ └────────────────┘ │
│ │
└────────────┬───────────┘
│
▼
┌────────────────┐
│ GA4 Analytics │
│ (Optional) │
└────────────────┘
API Dependency Chain
Public API (/api/v1/run)
User Request
│
▼
API Route Handler
│
├─→ Rate Limit Check (Upstash Redis)
│ └─→ FAIL: Return 429 Too Many Requests
│
├─→ API Key Check (if x-api-key header present)
│ └─→ Upstash Redis lookup
│ └─→ Invalid: Return 401 Unauthorized
│
├─→ Input Validation
│ └─→ Invalid: Return 400 Bad Request
│
├─→ Workflow Engine
│ │
│ ├─→ Load default executors (in-memory map)
│ │
│ ├─→ Execute steps sequentially
│ │ └─→ Each step processes output from previous step
│ │
│ └─→ Error Handling
│ └─→ Return 500 on server error
│
└─→ Return JSON Response
Protected API (/api/user/history)
User Request
│
▼
API Route Handler
│
├─→ Clerk Auth Check
│ └─→ No session: Return 401 Unauthorized
│
├─→ Entitlements Check
│ └─→ Not Pro: Return 403 Forbidden
│
├─→ Upstash Redis Lookup
│ └─→ GET user:{userId}:history:{toolSlug}
│
└─→ Return Encrypted History
Critical Path Analysis
Path 1: Anonymous User Using Tool (Client-Side Only)
User → Tool Component → Processing Logic → localStorage → Display Result
Dependencies:
- None (fully client-side)
Single Point of Failure:
- None (works offline)
Recovery Time:
- Instant (no external services)
Path 2: Pro User with Cloud Sync
User → Clerk Auth → Tool Component → Processing → Upstash Redis → Display Result
Dependencies:
- Clerk (authentication)
- Upstash Redis (cloud storage)
Single Points of Failure:
- Clerk outage → User can't authenticate → Fallback to localStorage
- Upstash outage → Cloud sync fails → Fallback to localStorage
Recovery Time:
- Graceful degradation: Immediate (local-only mode)
- Full recovery: When service restores
Path 3: API Request (External Integration)
API Client → Vercel Edge → Rate Limit (Redis) → API Key Check (Redis) → Workflow Engine → Return JSON
Dependencies:
- Vercel (hosting)
- Upstash Redis (rate limiting + API keys)
- Tool processor libraries
Single Points of Failure:
- Vercel outage → API completely down → No fallback
- Upstash outage → Rate limiting disabled (security risk) → Emergency rate limit via edge config
- Library failure → Specific tool fails → Return error for that step only
Recovery Time:
- Vercel: 99.99% uptime SLA (< 1 min recovery)
- Upstash: 99.9% uptime SLA (< 5 min recovery)
- Library: Immediate (error returned)
Fallback Strategies
1. Authentication Failure (Clerk Down)
Impact: Pro users can't sign in
Fallback:
// Graceful degradation in middleware.ts
export default clerkMiddleware((auth, req) => {
try {
const isPublicRoute = publicRoutes.includes(req.nextUrl.pathname);
if (!isPublicRoute) {
auth().protect();
}
} catch (error) {
// Clerk error - allow access to public routes only
console.error("[Auth] Clerk unavailable:", error);
if (!publicRoutes.includes(req.nextUrl.pathname)) {
return NextResponse.redirect(new URL("/", req.url));
}
}
});
User Experience:
- Can't access
/dashboardor Pro features - All tools still work client-side
- No data loss (localStorage continues working)
2. Cloud Storage Failure (Upstash Down)
Impact: Cloud sync and API key lookups fail
Fallback:
// src/lib/upstash.ts
export async function saveToCloud(userId: string, data: any) {
try {
await redis.set(`user:${userId}:history`, data);
} catch (error) {
console.error("[Upstash] Cloud save failed:", error);
// Fallback to localStorage
localStorage.setItem(`clt-cloud-queue-${userId}`, JSON.stringify(data));
// Retry later
setTimeout(() => retryCloudSave(userId), 60000); // Retry in 1 min
}
}
User Experience:
- History saves to localStorage only
- API requests without API key still work (public rate limit)
- Background retry when service restores
3. Payment Processing Failure (Stripe Down)
Impact: Users can't upgrade to Pro
Fallback:
// src/app/api/stripe/checkout/route.ts
export async function POST(req: Request) {
try {
const session = await stripe.checkout.sessions.create({...});
return NextResponse.json({ url: session.url });
} catch (error) {
console.error("[Stripe] Checkout failed:", error);
// Log to monitoring
await logError("stripe-checkout-failed", error);
// Show user-friendly error
return NextResponse.json(
{ error: "Payment system temporarily unavailable. Please try again in a few minutes." },
{ status: 503 }
);
}
}
User Experience:
- Upgrade button shows "Temporarily unavailable"
- Existing Pro users unaffected
- Manual upgrade option via email support
4. Library Failure (e.g., libphonenumber-js crash)
Impact: Specific tool (Phone Number Formatter) fails
Fallback:
// src/components/tools/PhoneNumberFormatterTool.tsx
const output = useMemo(() => {
try {
const parsed = parsePhoneNumber(input);
return parsed.format("E.164");
} catch (error) {
console.error("[Phone Formatter] Library error:", error);
// Basic fallback: regex-based formatting
return input.replace(/[^\d+]/g, "");
}
}, [input]);
User Experience:
- Tool shows degraded results (basic formatting instead of full library)
- Error message: "Advanced formatting unavailable. Using basic mode."
- Other tools unaffected
Dependency Update Strategy
Critical Updates (Security Patches)
- Monitor GitHub Dependabot alerts
- Update immediately:
npm update [package] - Run tests:
npm test && npm run build - Deploy to preview:
git push origin preview - Test in preview environment
- Merge to main if passing
Non-Critical Updates
- Weekly check:
npm outdated - Batch updates:
npm update --save - Review changelog for breaking changes
- Update code if needed
- Full test suite:
npm run test:e2e - Deploy
Major Version Updates
- Read migration guide
- Create feature branch:
git checkout -b upgrade/[package]-v[X] - Update package:
npm install [package]@latest - Fix breaking changes
- Test thoroughly
- Code review
- Merge to main
Service Health Monitoring
External Services
| Service | Status Page | Alert Method |
|---|---|---|
| Vercel | status.vercel.com | Email + Slack |
| Clerk | status.clerk.com | |
| Upstash | status.upstash.com | |
| Stripe | status.stripe.com |
Internal Monitoring
// src/app/api/health/route.ts
export async function GET() {
const health = {
status: "ok",
timestamp: Date.now(),
services: {
clerk: await checkClerk(),
upstash: await checkUpstash(),
stripe: await checkStripe(),
}
};
const allHealthy = Object.values(health.services).every(s => s.status === "ok");
return NextResponse.json(
health,
{ status: allHealthy ? 200 : 503 }
);
}
async function checkUpstash() {
try {
await redis.ping();
return { status: "ok" };
} catch (error) {
return { status: "error", message: error.message };
}
}
Cost Breakdown
| Service | Free Tier | Current Usage | Projected @ 10K Users | Projected @ 100K Users |
|---|---|---|---|---|
| Vercel | 100GB bandwidth | ~5GB/mo | ~50GB/mo ($20/mo) | ~500GB/mo ($100/mo) |
| Clerk | 10K MAU | ~100 MAU | ~10K MAU (free) | ~100K MAU ($250/mo) |
| Upstash | 10K commands/day | ~500/day | ~5K/day (free) | ~50K/day ($50/mo) |
| Stripe | 2.9% + $0.30 | $0 | ~$100 MRR ($3/mo fees) | ~$1K MRR ($30/mo fees) |
| GA4 | Unlimited | Free | Free | Free |
Total Monthly Cost:
- Current: $0
- @ 10K users: ~$23/mo
- @ 100K users: ~$430/mo
Document Owner: Tyson K. Last Updated: January 5, 2026 Next Review: March 1, 2026