Examples

This section provides comprehensive examples of using the Asterisms JS SDK in different scenarios and frameworks.

Quick Start Examples

Basic SDK Setup

// Basic setup with minimal configuration
import { createAsterismsSDK } from '@asterisms/sdk';
import { SRP6AuthProvider } from '@asterisms/auth-provider-srp6';

const sdk = createAsterismsSDK({
  bundleId: 'com.yourcompany.yourapp',
  rootDomain: 'yourapp.com',
  navigationAdapter: {
    navigate: (url) => (window.location.href = url),
    getCurrentPath: () => window.location.pathname
  }
});

// Register authentication provider
sdk.auth.registerProvider(
  'srp6',
  new SRP6AuthProvider({
    serviceUrl: 'https://auth.yourapp.com'
  })
);

console.log('SDK initialized:', sdk.isReady());

Authentication Flow

async function loginExample() {
  try {
    // Login user
    const result = await sdk.auth.login({
      provider: 'srp6',
      credentials: {
        username: 'user@example.com',
        password: 'userPassword'
      }
    });

    console.log('Login successful:', result.user);

    // Get current user
    const user = sdk.auth.getCurrentUser();
    console.log('Current user:', user);

    // Check authentication status
    const isAuthenticated = sdk.auth.isAuthenticated();
    console.log('Is authenticated:', isAuthenticated);
  } catch (error) {
    console.error('Login failed:', error.message);
  }
}

async function logoutExample() {
  try {
    await sdk.auth.logout();
    console.log('Logout successful');
  } catch (error) {
    console.error('Logout failed:', error.message);
  }
}

Framework Integration Examples

React Integration

1. Auth Provider Component

// components/AuthProvider.tsx
import React, { createContext, useContext, useEffect, useState } from 'react';
import { sdk } from '../lib/sdk';

interface AuthState {
  isAuthenticated: boolean;
  user: any | null;
  loading: boolean;
}

const AuthContext = createContext<AuthState>({
  isAuthenticated: false,
  user: null,
  loading: true
});

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [authState, setAuthState] = useState<AuthState>({
    isAuthenticated: false,
    user: null,
    loading: true
  });

  useEffect(() => {
    // Subscribe to authentication state changes
    const unsubscribe = sdk.auth.onAuthStateChanged((state) => {
      setAuthState({
        isAuthenticated: state.isAuthenticated,
        user: state.user,
        loading: false
      });
    });

    // Initial auth check
    setAuthState({
      isAuthenticated: sdk.auth.isAuthenticated(),
      user: sdk.auth.getCurrentUser(),
      loading: false
    });

    return unsubscribe;
  }, []);

  return (
    <AuthContext.Provider value={authState}>
      {children}
    </AuthContext.Provider>
  );
}

export const useAuth = () => useContext(AuthContext);

2. Login Component

// components/LoginForm.tsx
import React, { useState } from 'react';
import { sdk } from '../lib/sdk';
import { useAuth } from './AuthProvider';

export function LoginForm() {
  const [credentials, setCredentials] = useState({ username: '', password: '' });
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');
  const { isAuthenticated } = useAuth();

  if (isAuthenticated) {
    return <div>Already logged in!</div>;
  }

  const handleLogin = async (e: React.FormEvent) => {
    e.preventDefault();
    setLoading(true);
    setError('');

    try {
      await sdk.auth.login({
        provider: 'srp6',
        credentials
      });
    } catch (err: any) {
      setError(err.message || 'Login failed');
    } finally {
      setLoading(false);
    }
  };

  return (
    <form onSubmit={handleLogin}>
      <div>
        <label htmlFor="username">Username:</label>
        <input
          id="username"
          type="email"
          value={credentials.username}
          onChange={(e) => setCredentials({
            ...credentials,
            username: e.target.value
          })}
          required
        />
      </div>

      <div>
        <label htmlFor="password">Password:</label>
        <input
          id="password"
          type="password"
          value={credentials.password}
          onChange={(e) => setCredentials({
            ...credentials,
            password: e.target.value
          })}
          required
        />
      </div>

      {error && <div className="error">{error}</div>}

      <button type="submit" disabled={loading}>
        {loading ? 'Logging in...' : 'Login'}
      </button>
    </form>
  );
}

3. File Upload Component

// components/FileUpload.tsx
import React, { useState, useCallback } from 'react';
import { sdk } from '../lib/sdk';

interface UploadProgress {
  [fileId: string]: {
    percentage: number;
    status: 'uploading' | 'completed' | 'error';
  };
}

export function FileUpload() {
  const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
  const [uploadProgress, setUploadProgress] = useState<UploadProgress>({});

  const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files) {
      setSelectedFiles(Array.from(e.target.files));
    }
  };

  const uploadFile = useCallback(async (file: File) => {
    try {
      const upload = sdk.storage.uploadWithProgress(file, {
        folder: '/uploads',
        metadata: {
          originalName: file.name,
          uploadedAt: new Date().toISOString()
        }
      });

      // Track progress
      upload.onProgress((progress) => {
        setUploadProgress(prev => ({
          ...prev,
          [progress.fileId]: {
            percentage: progress.percentage,
            status: 'uploading'
          }
        }));
      });

      const result = await upload.promise;

      setUploadProgress(prev => ({
        ...prev,
        [result.fileId]: {
          percentage: 100,
          status: 'completed'
        }
      }));

      console.log('Upload completed:', result);

    } catch (error) {
      console.error('Upload failed:', error);
      setUploadProgress(prev => ({
        ...prev,
        [file.name]: {
          percentage: 0,
          status: 'error'
        }
      }));
    }
  }, []);

  const handleUpload = async () => {
    for (const file of selectedFiles) {
      await uploadFile(file);
    }
  };

  return (
    <div>
      <input
        type="file"
        multiple
        onChange={handleFileSelect}
      />

      <button onClick={handleUpload} disabled={selectedFiles.length === 0}>
        Upload Files
      </button>

      {selectedFiles.map((file, index) => {
        const progress = uploadProgress[file.name];
        return (
          <div key={index} className="file-item">
            <span>{file.name}</span>
            {progress && (
              <div className="progress">
                <div
                  className="progress-bar"
                  style={{ width: `${progress.percentage}%` }}
                />
                <span>{progress.status}: {progress.percentage}%</span>
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
}

SvelteKit Integration

1. Auth Store

// src/lib/stores/auth.ts
import { writable } from 'svelte/store';
import { sdk } from '$lib/sdk';

interface AuthState {
  isAuthenticated: boolean;
  user: any | null;
  loading: boolean;
}

function createAuthStore() {
  const { subscribe, set, update } = writable<AuthState>({
    isAuthenticated: false,
    user: null,
    loading: true
  });

  // Subscribe to SDK auth changes
  sdk.auth.onAuthStateChanged((state) => {
    set({
      isAuthenticated: state.isAuthenticated,
      user: state.user,
      loading: false
    });
  });

  return {
    subscribe,
    login: async (credentials: any) => {
      try {
        await sdk.auth.login({
          provider: 'srp6',
          credentials
        });
      } catch (error) {
        console.error('Login failed:', error);
        throw error;
      }
    },
    logout: async () => {
      try {
        await sdk.auth.logout();
      } catch (error) {
        console.error('Logout failed:', error);
        throw error;
      }
    }
  };
}

export const auth = createAuthStore();

2. Login Page

<!-- src/routes/login/+page.svelte -->
<script lang="ts">
  import { auth } from '$lib/stores/auth';
  import { goto } from '$app/navigation';
  import { onMount } from 'svelte';

  let username = '';
  let password = '';
  let loading = false;
  let error = '';

  $: if ($auth.isAuthenticated) {
    goto('/dashboard');
  }

  async function handleLogin() {
    loading = true;
    error = '';

    try {
      await auth.login({ username, password });
    } catch (err: any) {
      error = err.message || 'Login failed';
    } finally {
      loading = false;
    }
  }
</script>

<div class="login-container">
  <form on:submit|preventDefault={handleLogin}>
    <h1>Login</h1>

    <div class="field">
      <label for="username">Username:</label>
      <input
        id="username"
        type="email"
        bind:value={username}
        required
      />
    </div>

    <div class="field">
      <label for="password">Password:</label>
      <input
        id="password"
        type="password"
        bind:value={password}
        required
      />
    </div>

    {#if error}
      <div class="error">{error}</div>
    {/if}

    <button type="submit" disabled={loading}>
      {loading ? 'Logging in...' : 'Login'}
    </button>
  </form>
</div>

<style>
  .login-container {
    max-width: 400px;
    margin: 2rem auto;
    padding: 2rem;
    border: 1px solid #ccc;
    border-radius: 8px;
  }

  .field {
    margin-bottom: 1rem;
  }

  label {
    display: block;
    margin-bottom: 0.5rem;
  }

  input {
    width: 100%;
    padding: 0.5rem;
    border: 1px solid #ccc;
    border-radius: 4px;
  }

  .error {
    color: red;
    margin-bottom: 1rem;
  }

  button {
    width: 100%;
    padding: 0.75rem;
    background: #007cba;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }

  button:disabled {
    opacity: 0.6;
    cursor: not-allowed;
  }
</style>

3. Protected Route Hook

// src/hooks.server.ts
import { sdk } from '$lib/sdk';
import { redirect } from '@sveltejs/kit';

export async function handle({ event, resolve }) {
  // Get token from cookies or headers
  const token = event.cookies.get('auth-token') || event.request.headers.get('authorization');

  if (token) {
    // Set token in SDK
    sdk.auth.setToken(token);
  }

  // Check if route requires authentication
  if (event.url.pathname.startsWith('/protected')) {
    if (!sdk.auth.isAuthenticated()) {
      throw redirect(302, '/login');
    }
  }

  return resolve(event);
}

Vue.js Integration

1. Auth Composable

// composables/useAuth.ts
import { ref, computed, onMounted, onUnmounted } from 'vue';
import { sdk } from '../lib/sdk';

export function useAuth() {
  const authState = ref({
    isAuthenticated: false,
    user: null,
    loading: true
  });

  let unsubscribe: (() => void) | null = null;

  onMounted(() => {
    unsubscribe = sdk.auth.onAuthStateChanged((state) => {
      authState.value = {
        isAuthenticated: state.isAuthenticated,
        user: state.user,
        loading: false
      };
    });

    // Initial state
    authState.value = {
      isAuthenticated: sdk.auth.isAuthenticated(),
      user: sdk.auth.getCurrentUser(),
      loading: false
    };
  });

  onUnmounted(() => {
    if (unsubscribe) {
      unsubscribe();
    }
  });

  const login = async (credentials: any) => {
    try {
      await sdk.auth.login({
        provider: 'srp6',
        credentials
      });
    } catch (error) {
      console.error('Login failed:', error);
      throw error;
    }
  };

  const logout = async () => {
    try {
      await sdk.auth.logout();
    } catch (error) {
      console.error('Logout failed:', error);
      throw error;
    }
  };

  return {
    authState: computed(() => authState.value),
    isAuthenticated: computed(() => authState.value.isAuthenticated),
    user: computed(() => authState.value.user),
    loading: computed(() => authState.value.loading),
    login,
    logout
  };
}

Vanilla JavaScript Integration

<!doctype html>
<html>
  <head>
    <title>Asterisms JS SDK Example</title>
  </head>
  <body>
    <div id="app">
      <div id="login-form" style="display: none;">
        <h2>Login</h2>
        <form id="loginForm">
          <input type="email" id="username" placeholder="Username" required />
          <input type="password" id="password" placeholder="Password" required />
          <button type="submit">Login</button>
        </form>
        <div id="error" style="color: red;"></div>
      </div>

      <div id="dashboard" style="display: none;">
        <h2>Dashboard</h2>
        <p>Welcome, <span id="userName"></span>!</p>
        <button id="logoutBtn">Logout</button>

        <div>
          <h3>Upload File</h3>
          <input type="file" id="fileInput" />
          <button id="uploadBtn">Upload</button>
          <div id="uploadProgress"></div>
        </div>
      </div>
    </div>

    <script type="module">
      import { createAsterismsSDK } from 'https://unpkg.com/@asterisms/sdk';
      import { SRP6AuthProvider } from 'https://unpkg.com/@asterisms/auth-provider-srp6';

      // Initialize SDK
      const sdk = createAsterismsSDK({
        bundleId: 'com.example.app',
        rootDomain: 'example.com',
        navigationAdapter: {
          navigate: (url) => (window.location.href = url),
          getCurrentPath: () => window.location.pathname
        }
      });

      // Register auth provider
      sdk.auth.registerProvider(
        'srp6',
        new SRP6AuthProvider({
          serviceUrl: 'https://auth.example.com'
        })
      );

      // DOM elements
      const loginForm = document.getElementById('login-form');
      const dashboard = document.getElementById('dashboard');
      const loginFormEl = document.getElementById('loginForm');
      const usernameInput = document.getElementById('username');
      const passwordInput = document.getElementById('password');
      const errorDiv = document.getElementById('error');
      const userNameSpan = document.getElementById('userName');
      const logoutBtn = document.getElementById('logoutBtn');
      const fileInput = document.getElementById('fileInput');
      const uploadBtn = document.getElementById('uploadBtn');
      const uploadProgress = document.getElementById('uploadProgress');

      // Auth state handler
      sdk.auth.onAuthStateChanged((state) => {
        if (state.isAuthenticated) {
          loginForm.style.display = 'none';
          dashboard.style.display = 'block';
          userNameSpan.textContent = state.user.displayName || state.user.email;
        } else {
          loginForm.style.display = 'block';
          dashboard.style.display = 'none';
        }
      });

      // Login form handler
      loginFormEl.addEventListener('submit', async (e) => {
        e.preventDefault();
        errorDiv.textContent = '';

        try {
          await sdk.auth.login({
            provider: 'srp6',
            credentials: {
              username: usernameInput.value,
              password: passwordInput.value
            }
          });
        } catch (error) {
          errorDiv.textContent = error.message;
        }
      });

      // Logout handler
      logoutBtn.addEventListener('click', async () => {
        try {
          await sdk.auth.logout();
        } catch (error) {
          console.error('Logout failed:', error);
        }
      });

      // Upload handler
      uploadBtn.addEventListener('click', async () => {
        const file = fileInput.files[0];
        if (!file) return;

        try {
          const upload = sdk.storage.uploadWithProgress(file);

          upload.onProgress((progress) => {
            uploadProgress.innerHTML = `
            <div>Uploading: ${progress.percentage}%</div>
            <div style="width: 100%; background: #f0f0f0;">
              <div style="width: ${progress.percentage}%; background: #007cba; height: 20px;"></div>
            </div>
          `;
          });

          const result = await upload.promise;
          uploadProgress.innerHTML = `<div style="color: green;">Upload completed!</div>`;
          console.log('Upload result:', result);
        } catch (error) {
          uploadProgress.innerHTML = `<div style="color: red;">Upload failed: ${error.message}</div>`;
        }
      });

      // Initial auth check
      if (sdk.auth.isAuthenticated()) {
        const user = sdk.auth.getCurrentUser();
        userNameSpan.textContent = user.displayName || user.email;
        dashboard.style.display = 'block';
      } else {
        loginForm.style.display = 'block';
      }
    </script>
  </body>
</html>

Common Use Cases

1. File Management with Drive

async function fileManagementExample() {
  // Create a folder
  const folder = await sdk.drive.createFolder({
    name: 'My Documents',
    parent: 'root'
  });

  // Upload files to the folder
  const files = document.getElementById('fileInput').files;
  for (const file of files) {
    const result = await sdk.storage.upload(file, {
      folder: folder.id,
      metadata: {
        category: 'document',
        tags: ['important']
      }
    });
    console.log('File uploaded:', result);
  }

  // List folder contents
  const contents = await sdk.drive.listFolder(folder.id);
  console.log('Folder contents:', contents);

  // Share the folder
  await sdk.drive.share(folder.id, {
    users: ['colleague@example.com'],
    permission: 'write',
    message: 'Shared documents folder'
  });
}

2. Real-time Notifications

function notificationExample() {
  // Subscribe to notifications
  sdk.notification.onNotificationReceived((notification) => {
    // Show notification in UI
    showNotification(notification);

    // Show browser notification if permission granted
    if (Notification.permission === 'granted') {
      new Notification(notification.title, {
        body: notification.message,
        icon: notification.icon
      });
    }
  });

  // Request permission for browser notifications
  if (Notification.permission === 'default') {
    Notification.requestPermission().then((permission) => {
      if (permission === 'granted') {
        console.log('Notification permission granted');
      }
    });
  }

  // Mark notifications as read
  async function markAsRead(notificationIds) {
    await sdk.notification.markAsRead(notificationIds);
  }
}

function showNotification(notification) {
  const notificationEl = document.createElement('div');
  notificationEl.className = 'notification';
  notificationEl.innerHTML = `
    <h4>${notification.title}</h4>
    <p>${notification.message}</p>
    <button onclick="markAsRead(['${notification.id}'])">Mark as Read</button>
  `;
  document.getElementById('notifications').appendChild(notificationEl);
}

3. User Profile Management

async function profileManagementExample() {
  // Get current profile
  const profile = await sdk.profile.getProfile();
  console.log('Current profile:', profile);

  // Update profile
  await sdk.profile.updateProfile({
    displayName: 'John Doe',
    bio: 'Software Developer',
    website: 'https://johndoe.dev'
  });

  // Upload avatar
  const avatarFile = document.getElementById('avatarInput').files[0];
  if (avatarFile) {
    await sdk.profile.uploadAvatar(avatarFile);
  }

  // Update preferences
  await sdk.profile.updatePreferences({
    theme: 'dark',
    language: 'en',
    notifications: {
      email: true,
      push: true,
      inApp: true
    }
  });
}

4. Error Handling Patterns

import {
  AsterismsError,
  AsterismsBackendError,
  InvalidTokenError,
  HttpFetchError
} from '@asterisms/sdk';

async function robustErrorHandling() {
  try {
    const result = await sdk.storage.upload(file);
    return result;
  } catch (error) {
    if (error instanceof InvalidTokenError) {
      // Handle authentication errors
      console.log('Authentication required');
      // Redirect to login
      window.location.href = '/login';
    } else if (error instanceof AsterismsBackendError) {
      // Handle backend errors
      switch (error.status) {
        case 413:
          showError('File too large');
          break;
        case 429:
          showError('Too many requests, please try again later');
          break;
        case 503:
          showError('Service temporarily unavailable');
          break;
        default:
          showError(`Server error: ${error.message}`);
      }
    } else if (error instanceof HttpFetchError) {
      // Handle network errors
      if (error.isNetworkError) {
        showError('Network connection error');
      } else {
        showError('Request failed');
      }
    } else if (error instanceof AsterismsError) {
      // Handle other SDK errors
      showError(`SDK Error: ${error.message}`);
    } else {
      // Handle unexpected errors
      console.error('Unexpected error:', error);
      showError('An unexpected error occurred');
    }
  }
}

function showError(message) {
  // Show error in UI
  const errorEl = document.createElement('div');
  errorEl.className = 'error';
  errorEl.textContent = message;
  document.body.appendChild(errorEl);

  // Auto-remove after 5 seconds
  setTimeout(() => {
    document.body.removeChild(errorEl);
  }, 5000);
}

Testing Examples

See the Testing Guide for comprehensive testing examples.

Next Steps