npm package: @shikitoka/umbrella-sdk
Description: Official TypeScript SDK for Umbrella Platform - Seamless authentication, payments, and subscription management
Author: Umbrella Platform
Latest version: 1.1.0

# @shiki/sdk

TypeScript SDK for integrating with Umbrella Platform.

## Installation

```bash
npm install @shiki/sdk
# or
bun add @shiki/sdk
```

## Quick Start

```typescript
import { UmbrellaSDK } from '@shiki/sdk';

// Initialize SDK
const sdk = new UmbrellaSDK({
  apiKey: 'uk_your_api_key',
  projectId: 'proj_your_project_id',
  apiUrl: 'https://api.umbrella.com',
});

// Use SDK methods
const user = await sdk.auth.login('user@example.com', 'password');
```

## Features

- ✅ **Type-Safe**: Full TypeScript support with Hono RPC
- ✅ **Auth**: Login, register, logout, get current user
- ✅ **Payments**: Create checkout, manage subscriptions
- ✅ **Error Handling**: Typed errors with details
- ✅ **Framework Agnostic**: Works with any JavaScript framework

## Usage

### Authentication

```typescript
// Login
const { user, sessionId } = await sdk.auth.login('user@example.com', 'password123');

// Register
const { user, sessionId } = await sdk.auth.register(
  'user@example.com',
  'password123',
  'John Doe'
);

// Get current user
const user = await sdk.auth.getCurrentUser();

// Logout
await sdk.auth.logout();
```

### SSO Token Handling & Auth Redirect

The SDK provides seamless SSO (Single Sign-On) integration and automatic auth redirect:

#### Simple Auth Initialization (Recommended)

```typescript
// Use initAuth() for automatic SSO handling and redirect
import { sdk } from './lib/sdk';

// On app initialization
await sdk.auth.initAuth({
  platformUrl: 'https://platform.umbrella.com',
  autoRedirect: true, // Auto redirect to login if no auth
  onSuccess: (user) => {
    console.log('User authenticated:', user.email);
    // Update app state with user
  },
  onError: (error) => {
    console.error('SSO error:', error.message);
  },
  onNoAuth: () => {
    console.log('No authentication found');
  },
});
```

#### Manual SSO Token Handling

```typescript
// Automatically handle SSO token from URL on page load
const success = await sdk.auth.handleSSOToken({
  onSuccess: (user) => {
    console.log('User authenticated:', user);
  },
  onError: (error) => {
    console.error('SSO failed:', error.message);
  },
});

// Manual token validation
const result = await sdk.auth.validateSSOToken(token);
if (result.valid) {
  console.log('User:', result.user);
  console.log('Session ID:', result.sessionId);
}
```

#### Auth Redirect Methods

```typescript
// Check if user is authenticated, redirect to login if not
const isAuthenticated = sdk.auth.requireAuth({
  platformUrl: 'https://platform.umbrella.com',
  redirectPath: '/dashboard', // Optional: custom redirect after login
});

// Generate login URL with redirect parameter
const loginUrl = sdk.auth.getLoginUrl(
  'https://platform.umbrella.com',
  'https://myapp.com/dashboard'
);
// Result: https://platform.umbrella.com/login?redirect=https%3A%2F%2Fmyapp.com%2Fdashboard

// Get stored session
const session = sdk.auth.getStoredSession();
if (session) {
  console.log('User:', session.user);
}

// Clear stored session
sdk.auth.clearStoredSession();
```

#### Protected Routes Example

```typescript
// Protect a route/page
function ProtectedPage() {
  useEffect(() => {
    // Redirect to login if not authenticated
    sdk.auth.requireAuth({
      platformUrl: 'https://platform.umbrella.com',
    });
  }, []);

  return <div>Protected Content</div>;
}
```

#### Complete Auth Flow (Manual)

```typescript
// In your project's main entry point (e.g., app.tsx, main.ts)
import { sdk } from './lib/sdk';

async function initAuth() {
  // Try to handle SSO token from URL
  const ssoSuccess = await sdk.auth.handleSSOToken({
    onSuccess: (user) => {
      console.log('Logged in via SSO:', user.email);
    },
    onError: (error) => {
      if (error.type === 'expired') {
        console.log('SSO token expired, redirecting...');
        const loginUrl = sdk.auth.getLoginUrl(
          'https://platform.umbrella.com',
          window.location.href
        );
        window.location.href = loginUrl;
      }
    },
  });

  // If no SSO token, check for stored session
  if (!ssoSuccess) {
    const session = sdk.auth.getStoredSession();
    if (session) {
      console.log('Restored session:', session.user.email);
    } else {
      // No auth, redirect to platform login
      sdk.auth.requireAuth({
        platformUrl: 'https://platform.umbrella.com',
      });
    }
  }
}

initAuth();
```

### User Profile

```typescript
// Get user profile
const profile = await sdk.user.getProfile('user_123');
console.log(profile.name, profile.email, profile.avatar);

// Update user profile
const updated = await sdk.user.updateProfile('user_123', {
  name: 'John Doe',
  avatar: 'https://example.com/avatar.jpg',
});

// Get user preferences
const preferences = await sdk.user.getPreferences('user_123');
console.log(preferences.theme, preferences.language);

// Update user preferences
await sdk.user.updatePreferences('user_123', {
  theme: 'dark',
  language: 'en',
  notifications: {
    email: true,
    push: false,
  },
});

// Get current user from session
const currentUser = sdk.user.getCurrentUserProfile();
if (currentUser) {
  console.log('Current user:', currentUser.email);
}
```

### Payments & Subscriptions

#### Basic Payment Operations

```typescript
// Create checkout
const { checkoutUrl } = await sdk.payments.createCheckout({
  userId: 'user_123',
  variantId: 'variant_456',
  productName: 'Pro Plan',
  successUrl: 'https://myapp.com/success',
  cancelUrl: 'https://myapp.com/cancel',
});

// Redirect user to checkout
window.location.href = checkoutUrl;

// Get user subscriptions
const subscriptions = await sdk.payments.getSubscriptions('user_123');

// Check if user has access (simple)
const hasAccess = await sdk.payments.hasAccess('user_123');

// Cancel subscription
await sdk.payments.cancelSubscription('sub_123');
```

#### Subscription Checking with Caching (Recommended)

```typescript
// Check subscription access with automatic caching (5 min TTL)
const result = await sdk.payments.checkAccess('user_123');

if (result.hasAccess) {
  console.log('User has active subscription:', result.subscription);
  // Allow access to premium features
} else {
  console.log('No active subscription:', result.message);
  // Show subscription prompt
}

// Custom cache TTL
const result = await sdk.payments.checkAccess('user_123', {
  useCache: true,
  cacheTTL: 10 * 60 * 1000, // 10 minutes
});

// Bypass cache for fresh data
const result = await sdk.payments.checkAccess('user_123', {
  useCache: false,
});

// Clear subscription cache
sdk.payments.clearSubscriptionCache('user_123'); // Clear specific user
sdk.payments.clearSubscriptionCache(); // Clear all
```

#### Require Subscription (Auto Redirect)

```typescript
// Automatically check and redirect if no subscription
const hasAccess = await sdk.payments.requireSubscription('user_123', {
  platformUrl: 'https://platform.umbrella.com',
  projectSlug: 'my-project', // Optional: direct link to project
  onNoAccess: () => {
    console.log('User needs to subscribe');
  },
});

if (hasAccess) {
  // User has subscription, continue
  console.log('Access granted');
}
```

#### Manual Subscription Redirect

```typescript
// Redirect to subscription page
sdk.payments.redirectToSubscription({
  platformUrl: 'https://platform.umbrella.com',
  projectSlug: 'my-project', // Optional
  returnUrl: 'https://myapp.com/dashboard', // Optional
});
```

#### Custom Subscription Prompt

```typescript
// Get HTML for subscription prompt modal
const modalHTML = sdk.payments.getSubscriptionPromptHTML({
  title: 'Premium Feature',
  message: 'This feature requires a Pro subscription. Upgrade now!',
  buttonText: 'Upgrade to Pro',
});

// Render in your app
document.body.insertAdjacentHTML('beforeend', modalHTML);
```

#### Complete Subscription Flow Example

```typescript
// In your premium feature component
import { sdk } from './lib/sdk';

async function accessPremiumFeature(userId: string) {
  // Check subscription with caching
  const result = await sdk.payments.checkAccess(userId);

  if (!result.hasAccess) {
    // Show subscription prompt
    const modalHTML = sdk.payments.getSubscriptionPromptHTML({
      title: 'Subscription Required',
      message: 'You need an active subscription to access this feature.',
    });
    document.body.insertAdjacentHTML('beforeend', modalHTML);
    
    // Or auto redirect
    // sdk.payments.redirectToSubscription({
    //   projectSlug: 'my-project',
    // });
    
    return;
  }

  // User has subscription, proceed
  console.log('Access granted, subscription:', result.subscription);
  // ... premium feature logic
}
```

#### Protected Feature Example

```typescript
// Protect a premium feature
function PremiumFeature({ userId }) {
  const [hasAccess, setHasAccess] = useState(false);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function checkSubscription() {
      const result = await sdk.payments.checkAccess(userId);
      setHasAccess(result.hasAccess);
      setLoading(false);

      if (!result.hasAccess) {
        // Auto redirect after 3 seconds
        setTimeout(() => {
          sdk.payments.redirectToSubscription({
            projectSlug: 'my-project',
          });
        }, 3000);
      }
    }

    checkSubscription();
  }, [userId]);

  if (loading) return <div>Checking subscription...</div>;
  
  if (!hasAccess) {
    return (
      <div>
        <h2>Subscription Required</h2>
        <p>Redirecting to subscription page...</p>
      </div>
    );
  }

  return <div>Premium Feature Content</div>;
}
```

## Framework Examples

### Next.js

```typescript
// lib/sdk.ts
import { UmbrellaSDK } from '@shiki/sdk';

export const sdk = new UmbrellaSDK({
  apiKey: process.env.UMBRELLA_API_KEY!,
  projectId: process.env.UMBRELLA_PROJECT_ID!,
  apiUrl: process.env.UMBRELLA_API_URL!,
});

// app/api/auth/login/route.ts
import { sdk } from '@/lib/sdk';

export async function POST(request: Request) {
  const { email, password } = await request.json();

  try {
    const result = await sdk.auth.login(email, password);
    return Response.json(result);
  } catch (error) {
    return Response.json({ error: error.message }, { status: 401 });
  }
}

// app/layout.tsx - Handle SSO on app initialization
'use client';

import { useEffect, useState } from 'react';
import { sdk } from '@/lib/sdk';

export default function RootLayout({ children }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    // Initialize auth with automatic SSO handling and redirect
    sdk.auth.initAuth({
      platformUrl: process.env.NEXT_PUBLIC_PLATFORM_URL,
      autoRedirect: true, // Auto redirect to login if no auth
      onSuccess: (userData) => {
        console.log('User authenticated:', userData.email);
        setUser(userData);
      },
      onError: (error) => {
        console.error('Auth error:', error.message);
      },
    });
  }, []);

  return <html>{children}</html>;
}

// Or use requireAuth for protected pages
// app/dashboard/page.tsx
'use client';

import { useEffect } from 'react';
import { sdk } from '@/lib/sdk';

export default function DashboardPage() {
  useEffect(() => {
    // Redirect to login if not authenticated
    sdk.auth.requireAuth({
      platformUrl: process.env.NEXT_PUBLIC_PLATFORM_URL,
    });
  }, []);

  return <div>Dashboard Content</div>;
}
```

### Vue 3

```typescript
// src/lib/sdk.ts
import { UmbrellaSDK } from '@shiki/sdk';

export const sdk = new UmbrellaSDK({
  apiKey: import.meta.env.VITE_UMBRELLA_API_KEY,
  projectId: import.meta.env.VITE_UMBRELLA_PROJECT_ID,
  apiUrl: import.meta.env.VITE_UMBRELLA_API_URL,
});

// src/composables/useAuth.ts
import { ref, onMounted } from 'vue';
import { sdk } from '@/lib/sdk';

export function useAuth() {
  const user = ref(null);
  const loading = ref(false);

  async function login(email: string, password: string) {
    loading.value = true;
    try {
      const result = await sdk.auth.login(email, password);
      user.value = result.user;
      return result;
    } finally {
      loading.value = false;
    }
  }

  // Initialize auth on mount
  onMounted(async () => {
    await sdk.auth.initAuth({
      platformUrl: import.meta.env.VITE_PLATFORM_URL,
      autoRedirect: true,
      onSuccess: (userData) => {
        user.value = userData;
      },
      onError: (error) => {
        console.error('Auth error:', error.message);
      },
    });
  });

  return { user, loading, login };
}

// Or use requireAuth for protected routes
// src/views/DashboardView.vue
import { onMounted } from 'vue';
import { sdk } from '@/lib/sdk';

export default {
  setup() {
    onMounted(() => {
      sdk.auth.requireAuth({
        platformUrl: import.meta.env.VITE_PLATFORM_URL,
      });
    });
  },
}
```

### Svelte/SvelteKit

```typescript
// src/lib/sdk.ts
import { UmbrellaSDK } from '@shiki/sdk';

export const sdk = new UmbrellaSDK({
  apiKey: import.meta.env.VITE_UMBRELLA_API_KEY,
  projectId: import.meta.env.VITE_UMBRELLA_PROJECT_ID,
  apiUrl: import.meta.env.VITE_UMBRELLA_API_URL,
});

// src/routes/api/auth/login/+server.ts
import { sdk } from '$lib/sdk';
import { json } from '@sveltejs/kit';

export async function POST({ request }) {
  const { email, password } = await request.json();

  try {
    const result = await sdk.auth.login(email, password);
    return json(result);
  } catch (error) {
    return json({ error: error.message }, { status: 401 });
  }
}

// src/routes/+layout.svelte - Handle SSO on app initialization
<script lang="ts">
  import { onMount } from 'svelte';
  import { sdk } from '$lib/sdk';
  import { writable } from 'svelte/store';

  export const user = writable(null);

  onMount(async () => {
    // Initialize auth with automatic handling
    await sdk.auth.initAuth({
      platformUrl: import.meta.env.VITE_PLATFORM_URL,
      autoRedirect: true,
      onSuccess: (userData) => {
        user.set(userData);
      },
      onError: (error) => {
        console.error('Auth error:', error.message);
      },
    });
  });
</script>

<slot />

<!-- Or use requireAuth for protected routes -->
<!-- src/routes/dashboard/+page.svelte -->
<script lang="ts">
  import { onMount } from 'svelte';
  import { sdk } from '$lib/sdk';

  onMount(() => {
    sdk.auth.requireAuth({
      platformUrl: import.meta.env.VITE_PLATFORM_URL,
    });
  });
</script>

<div>Dashboard Content</div>
```

## Error Handling

```typescript
try {
  await sdk.auth.login(email, password);
} catch (error) {
  console.error('Error code:', error.code);
  console.error('Error message:', error.message);
  console.error('Status code:', error.statusCode);
  console.error('Details:', error.details);
}
```

## Configuration

```typescript
const sdk = new UmbrellaSDK({
  apiKey: 'uk_your_api_key', // Required: Your project API key
  projectId: 'proj_your_project_id', // Required: Your project ID
  apiUrl: 'https://api.umbrella.com', // Required: API URL
  timeout: 5000, // Optional: Request timeout in ms
});
```

## Type Safety

The SDK is fully typed with TypeScript:

```typescript
import type { User, Subscription, CheckoutResponse } from '@shiki/sdk';

const user: User = await sdk.auth.getCurrentUser();
const subscriptions: Subscription[] = await sdk.payments.getSubscriptions(userId);
const checkout: CheckoutResponse = await sdk.payments.createCheckout(params);
```

## License

MIT