CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-unimodules--core

Deprecated compatibility wrapper that provides backward compatibility for applications migrating from legacy Unimodules infrastructure to Expo Modules API

Pending
Overview
Eval results
Files

error-handling.mddocs/

Error Handling

Standardized error classes for consistent error handling across Expo modules with error codes and platform-specific messaging.

Capabilities

CodedError Class

General-purpose error class that provides consistent error handling with error codes for easy categorization and handling.

/**
 * General error class for all errors in Expo modules
 * Guarantees a code field for error differentiation
 */
class CodedError extends Error {
  /**
   * Error code for categorizing error types
   */
  code: string;
  
  /**
   * Additional error information
   */
  info?: any;
  
  /**
   * Create a coded error
   * @param code - Error code for identification
   * @param message - Human-readable error message
   */
  constructor(code: string, message: string);
}

Usage Examples:

import { CodedError } from "@unimodules/core";

// Create custom errors
function validateInput(data: unknown) {
  if (!data) {
    throw new CodedError('INVALID_INPUT', 'Input data is required');
  }
  
  if (typeof data !== 'object') {
    throw new CodedError('INVALID_TYPE', 'Input must be an object');
  }
}

// Handle coded errors
try {
  await someNativeOperation();
} catch (error) {
  if (error instanceof CodedError) {
    switch (error.code) {
      case 'PERMISSION_DENIED':
        showPermissionDialog();
        break;
      case 'NETWORK_ERROR':
        showRetryButton();
        break;
      case 'INVALID_ARGUMENT':
        showValidationError(error.message);
        break;
      default:
        showGenericError(error.message);
    }
  } else {
    // Handle non-coded errors
    showGenericError('An unexpected error occurred');
  }
}

// Add additional error information
function createDetailedError(code: string, message: string, details: any) {
  const error = new CodedError(code, message);
  error.info = details;
  return error;
}

const error = createDetailedError(
  'API_ERROR',
  'Failed to fetch user data',
  {
    endpoint: '/api/users/123',
    statusCode: 404,
    timestamp: new Date().toISOString()
  }
);

UnavailabilityError Class

Specialized error class for features that are unavailable, unsupported, or not implemented on the current platform.

/**
 * Error for unavailable, unsupported, or unimplemented properties
 * Automatically includes platform information
 */
class UnavailabilityError extends CodedError {
  /**
   * Create an unavailability error
   * @param moduleName - Name of the module
   * @param propertyName - Name of the unavailable property/method
   */
  constructor(moduleName: string, propertyName: string);
}

Usage Examples:

import { UnavailabilityError, Platform } from "@unimodules/core";

// Throw for unsupported features
class CameraModule {
  static async takePictureAsync() {
    if (Platform.OS === 'web') {
      throw new UnavailabilityError('Camera', 'takePictureAsync');
    }
    // Native implementation
    return NativeModulesProxy.Camera.takePictureAsync();
  }
  
  static async recordVideoAsync() {
    if (!Platform.select({ ios: true, android: true, default: false })) {
      throw new UnavailabilityError('Camera', 'recordVideoAsync');
    }
    return NativeModulesProxy.Camera.recordVideoAsync();
  }
}

// Handle unavailability errors
try {
  await CameraModule.takePictureAsync();
} catch (error) {
  if (error instanceof UnavailabilityError) {
    console.warn(`Feature not available: ${error.message}`);
    // Show alternative UI or disable feature
    showFeatureUnavailableMessage();
  }
}

// Conditional feature checking
function createFeatureProxy(moduleName: string) {
  return new Proxy({}, {
    get(target, propertyName: string) {
      const module = NativeModulesProxy[moduleName];
      if (!module || !(propertyName in module)) {
        throw new UnavailabilityError(moduleName, propertyName);
      }
      return module[propertyName];
    }
  });
}

Error Code Standards

Common error codes used throughout the Expo ecosystem:

/**
 * Standard error codes used across Expo modules
 */
type StandardErrorCodes = 
  | 'ERR_UNAVAILABLE'          // Feature not available on platform
  | 'ERR_DEPRECATED_API'       // API has been deprecated/removed
  | 'ERR_INVALID_ARGUMENT'     // Invalid argument provided
  | 'ERR_PERMISSION_DENIED'    // Permission required but not granted
  | 'ERR_NETWORK_ERROR'        // Network operation failed
  | 'ERR_FILE_NOT_FOUND'       // File system operation failed
  | 'ERR_OPERATION_CANCELLED'  // User cancelled operation
  | 'ERR_TIMEOUT'              // Operation timed out
  | 'ERR_INITIALIZATION'       // Module failed to initialize
  | 'ERR_PLATFORM_SPECIFIC';   // Platform-specific error

Usage Examples:

import { CodedError } from "@unimodules/core";

// Use standard error codes
function validatePermission(permission: string) {
  if (!permission) {
    throw new CodedError('ERR_INVALID_ARGUMENT', 'Permission name is required');
  }
}

async function requestWithTimeout<T>(operation: Promise<T>, timeout: number): Promise<T> {
  const timeoutPromise = new Promise<never>((_, reject) => {
    setTimeout(() => {
      reject(new CodedError('ERR_TIMEOUT', `Operation timed out after ${timeout}ms`));
    }, timeout);
  });
  
  return Promise.race([operation, timeoutPromise]);
}

// Handle standard error codes
function handleStandardError(error: CodedError) {
  switch (error.code) {
    case 'ERR_PERMISSION_DENIED':
      return 'Permission required. Please grant access in settings.';
    case 'ERR_NETWORK_ERROR':
      return 'Network error. Please check your connection.';
    case 'ERR_TIMEOUT':
      return 'Operation timed out. Please try again.';
    case 'ERR_UNAVAILABLE':
      return 'This feature is not available on your device.';
    default:
      return `Error: ${error.message}`;
  }
}

Advanced Usage

Error Factory Pattern

import { CodedError, UnavailabilityError } from "@unimodules/core";

class ErrorFactory {
  static createPermissionError(permission: string): CodedError {
    return new CodedError(
      'ERR_PERMISSION_DENIED',
      `${permission} permission is required but not granted`
    );
  }
  
  static createNetworkError(details?: any): CodedError {
    const error = new CodedError('ERR_NETWORK_ERROR', 'Network request failed');
    if (details) {
      error.info = details;
    }
    return error;
  }
  
  static createValidationError(field: string, value: any): CodedError {
    const error = new CodedError(
      'ERR_INVALID_ARGUMENT',
      `Invalid value for field '${field}'`
    );
    error.info = { field, value };
    return error;
  }
  
  static createUnavailableError(module: string, method: string): UnavailabilityError {
    return new UnavailabilityError(module, method);
  }
}

// Usage
throw ErrorFactory.createPermissionError('CAMERA');
throw ErrorFactory.createNetworkError({ statusCode: 500, endpoint: '/api/data' });

Error Recovery Patterns

import { CodedError, UnavailabilityError } from "@unimodules/core";

class ErrorRecovery {
  static async withRetry<T>(
    operation: () => Promise<T>,
    maxRetries: number = 3,
    delay: number = 1000
  ): Promise<T> {
    let lastError: Error;
    
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        return await operation();
      } catch (error) {
        lastError = error;
        
        // Don't retry certain error types
        if (error instanceof UnavailabilityError ||
            (error instanceof CodedError && error.code === 'ERR_PERMISSION_DENIED')) {
          throw error;
        }
        
        if (attempt < maxRetries) {
          await new Promise(resolve => setTimeout(resolve, delay * attempt));
        }
      }
    }
    
    throw new CodedError('ERR_MAX_RETRIES_EXCEEDED', 
      `Operation failed after ${maxRetries} attempts: ${lastError.message}`);
  }
  
  static async withFallback<T>(
    primary: () => Promise<T>,
    fallback: () => Promise<T>
  ): Promise<T> {
    try {
      return await primary();
    } catch (error) {
      if (error instanceof UnavailabilityError) {
        console.warn(`Primary method unavailable, using fallback: ${error.message}`);
        return await fallback();
      }
      throw error;
    }
  }
}

// Usage
const result = await ErrorRecovery.withRetry(async () => {
  return await fetch('/api/data');
});

const data = await ErrorRecovery.withFallback(
  () => NativeModulesProxy.Storage.getSecureItem('key'),
  () => AsyncStorage.getItem('key')
);

Types

interface ErrorInfo {
  [key: string]: any;
}

interface CodedErrorConstructor {
  new (code: string, message: string): CodedError;
  prototype: CodedError;
}

interface UnavailabilityErrorConstructor {
  new (moduleName: string, propertyName: string): UnavailabilityError;
  prototype: UnavailabilityError;
}

Install with Tessl CLI

npx tessl i tessl/npm-unimodules--core

docs

error-handling.md

event-management.md

index.md

native-modules.md

native-views.md

permissions.md

platform-utilities.md

utilities.md

tile.json