@shipstatic/ship 0.1.19 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.js +7 -7
- package/dist/browser.js.map +1 -1
- package/dist/cli.cjs +10 -9
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/env.ts","../src/core/config.ts","../src/types.ts","../src/core/constants.ts","../src/api/http.ts","../src/index.ts","../src/core/platform-config.ts","../src/lib/prepare-input.ts","../src/lib/md5.ts","../src/lib/junk.ts","../src/lib/node-files.ts","../src/lib/path.ts","../src/lib/browser-files.ts","../src/resources.ts"],"sourcesContent":["/**\n * @file Environment detection utilities for the Ship SDK.\n * Helps in determining whether the SDK is running in a Node.js, browser, or unknown environment.\n */\n\n/**\n * Represents the detected or simulated JavaScript execution environment.\n */\nexport type ExecutionEnvironment = 'browser' | 'node' | 'unknown';\n\n/** @internal Environment override for testing. */\nlet _testEnvironment: ExecutionEnvironment | null = null;\n\n/**\n * **FOR TESTING PURPOSES ONLY.**\n *\n * Allows tests to override the detected environment, forcing the SDK to behave\n * as if it's running in the specified environment.\n *\n * @param env - The environment to simulate ('node', 'browser', 'unknown'),\n * or `null` to clear the override and revert to actual environment detection.\n * @internal\n */\nexport function __setTestEnvironment(env: ExecutionEnvironment | null): void {\n _testEnvironment = env;\n}\n\n/**\n * Detects the actual JavaScript execution environment (Node.js, browser, or unknown)\n * by checking for characteristic global objects.\n * @returns The detected environment as {@link ExecutionEnvironment}.\n * @internal\n */\nfunction detectEnvironment(): ExecutionEnvironment {\n // Check for Node.js environment\n if (typeof process !== 'undefined' && process.versions && process.versions.node) {\n return 'node';\n }\n\n // Check for Browser environment (including Web Workers)\n if (typeof window !== 'undefined' || typeof self !== 'undefined') {\n return 'browser';\n }\n\n return 'unknown';\n}\n\n/**\n * Gets the current effective execution environment.\n *\n * This function first checks if a test environment override is active via {@link __setTestEnvironment}.\n * If not, it detects the actual environment (Node.js, browser, or unknown).\n *\n * @returns The current execution environment: 'browser', 'node', or 'unknown'.\n * @public\n */\nexport function getENV(): ExecutionEnvironment {\n // Return test override if set\n if (_testEnvironment) {\n return _testEnvironment;\n }\n \n // Detect actual environment\n return detectEnvironment();\n}\n","/**\n * @file Manages loading and validation of client configuration.\n * This module uses `cosmiconfig` to find and load configuration from various\n * file sources (e.g., `.shiprc`, `package.json`) and environment variables.\n * Configuration values are validated using Zod schemas.\n */\n\nimport { z } from 'zod';\nimport { ShipClientOptions, DeploymentOptions, ShipError } from '../types.js';\nimport { getENV } from '../lib/env.js';\nimport { DEFAULT_API } from './constants.js';\n\n\n\n/** @internal Name of the module, used by cosmiconfig for config file searching. */\nconst MODULE_NAME = 'ship';\n\n/**\n * Zod schema for validating ship configuration.\n * @internal\n */\nconst ConfigSchema = z.object({\n apiUrl: z.string().url().optional(),\n apiKey: z.string().optional()\n}).strict();\n\n/**\n * Validates configuration using Zod schema.\n * @param config - Configuration object to validate\n * @returns Validated configuration or throws error\n * @internal\n */\nfunction validateConfig(config: any): Partial<ShipClientOptions> {\n try {\n return ConfigSchema.parse(config);\n } catch (error) {\n if (error instanceof z.ZodError) {\n const firstError = error.issues[0];\n const path = firstError.path.length > 0 ? ` at ${firstError.path.join('.')}` : '';\n throw ShipError.config(`Configuration validation failed${path}: ${firstError.message}`);\n }\n throw ShipError.config('Configuration validation failed');\n }\n}\n\n/**\n * Loads client configuration from files.\n * Searches for .shiprc and package.json with ship key.\n * First searches from the current directory, then from the home directory.\n * @param configFile - Optional specific config file path to load\n * @returns Configuration object or empty if not found/invalid\n * @internal\n */\nasync function loadConfigFromFile(configFile?: string): Promise<Partial<ShipClientOptions>> {\n try {\n // Only use cosmiconfig in Node.js environments\n if (getENV() !== 'node') {\n return {};\n }\n \n // Dynamically import cosmiconfig and os only in Node.js environments\n const { cosmiconfigSync } = await import('cosmiconfig');\n const os = await import('os');\n \n const explorer = cosmiconfigSync(MODULE_NAME, {\n searchPlaces: [\n `.${MODULE_NAME}rc`,\n 'package.json',\n `${os.homedir()}/.${MODULE_NAME}rc`, // Always include home directory as fallback\n ],\n stopDir: os.homedir(), // Stop searching at home directory\n });\n \n let result;\n \n // If a specific config file is provided, load it directly\n if (configFile) {\n result = explorer.load(configFile);\n } else {\n // cosmiconfig automatically searches up the directory tree\n // from current directory to stopDir (home directory)\n result = explorer.search();\n }\n \n if (result && result.config) {\n return validateConfig(result.config);\n }\n } catch (error) {\n if (error instanceof ShipError) throw error; // Re-throw all ShipError instances\n // Silently fail for file loading issues - this is optional config\n }\n return {};\n}\n\n/**\n * Simplified configuration loading prioritizing environment variables.\n * Only loads file config if environment variables are not set.\n * Only available in Node.js environments.\n *\n * @param configFile - Optional specific config file path to load\n * @returns Configuration object with loaded values\n * @throws {ShipInvalidConfigError} If the configuration is invalid.\n */\nexport async function loadConfig(configFile?: string): Promise<Partial<ShipClientOptions>> {\n if (getENV() !== 'node') return {};\n\n // Start with environment variables (highest priority)\n const envConfig = {\n apiUrl: process.env.SHIP_API_URL,\n apiKey: process.env.SHIP_API_KEY,\n };\n\n // Always try to load file config for fallback values\n const fileConfig = await loadConfigFromFile(configFile);\n\n // Merge with environment variables taking precedence\n const mergedConfig = {\n apiUrl: envConfig.apiUrl ?? fileConfig.apiUrl,\n apiKey: envConfig.apiKey ?? fileConfig.apiKey,\n };\n\n // Validate final config\n return validateConfig(mergedConfig);\n}\n\n/**\n * Simplified configuration resolution with clear precedence.\n * Precedence: user options > environment variables > config files > defaults.\n * \n * @param userOptions - Options provided directly by the user\n * @param loadedConfig - Configuration loaded from environment/files\n * @returns Resolved configuration with api and apiKey\n */\nexport function resolveConfig(\n userOptions: ShipClientOptions = {}, \n loadedConfig: Partial<ShipClientOptions> = {}\n): { apiUrl: string; apiKey?: string } {\n // Build final config with clear precedence\n // Only use userOptions.apiKey if it's explicitly provided (not undefined)\n const finalConfig = {\n apiUrl: userOptions.apiUrl || loadedConfig.apiUrl || DEFAULT_API,\n apiKey: userOptions.apiKey || loadedConfig.apiKey,\n };\n\n // Return with optional apiKey\n return finalConfig.apiKey !== undefined \n ? { apiUrl: finalConfig.apiUrl, apiKey: finalConfig.apiKey }\n : { apiUrl: finalConfig.apiUrl };\n}\n\n\n// =============================================================================\n// CONFIGURATION MERGING\n// =============================================================================\n\n/**\n * Merge deployment options with client defaults.\n * Uses a declarative approach with object spreading for simplicity.\n */\nexport function mergeDeployOptions(\n userOptions: DeploymentOptions = {},\n clientDefaults: ShipClientOptions\n): DeploymentOptions {\n return {\n // Start with client defaults\n onProgress: clientDefaults.onProgress,\n onProgressStats: clientDefaults.onProgressStats,\n maxConcurrency: clientDefaults.maxConcurrentDeploys,\n timeout: clientDefaults.timeout,\n apiKey: clientDefaults.apiKey,\n apiUrl: clientDefaults.apiUrl,\n // Overwrite with any user-provided options\n ...userOptions,\n };\n}\n","/**\n * @file SDK-specific type definitions\n * Consolidates all Ship SDK types into a single file for clarity.\n * Core types come from @shipstatic/types, while SDK-specific types are defined here.\n */\n\n// Re-export all types from @shipstatic/types for convenience\nexport * from '@shipstatic/types';\n\n// =============================================================================\n// ENVIRONMENT-SPECIFIC TYPES\n// =============================================================================\n\n/**\n * Consolidated input type for all environments\n */\nexport type DeployInput = FileList | File[] | HTMLInputElement | string[];\n\n// =============================================================================\n// DEPLOYMENT OPTIONS\n// =============================================================================\n\n/**\n * Universal deploy options for both Node.js and Browser environments\n */\nexport interface DeploymentOptions {\n /** The API URL to use for this specific deploy. Overrides client's default. */\n apiUrl?: string;\n /** An AbortSignal to allow cancellation of the deploy operation. */\n signal?: AbortSignal;\n /** An optional subdomain to suggest for the deployment. Availability is subject to the API. */\n subdomain?: string;\n /** Callback invoked if the deploy is cancelled via the AbortSignal. */\n onCancel?: () => void;\n /** Maximum number of concurrent operations. */\n maxConcurrency?: number;\n /** Timeout in milliseconds for the deploy request. */\n timeout?: number;\n /** The API key to use for this specific deploy. Overrides client's default. */\n apiKey?: string;\n /** Whether to auto-detect and optimize file paths by flattening common directories. Defaults to true. */\n pathDetect?: boolean;\n /** Whether to auto-detect SPAs and generate ship.json configuration. Defaults to true. */\n spaDetect?: boolean;\n /** Callback for overall deploy progress (0-100). */\n onProgress?: (progress: number) => void;\n /** Callback for detailed progress statistics. */\n onProgressStats?: (progressStats: ProgressStats) => void;\n}\n\n/**\n * Options for configuring an deploy operation via `apiClient.deployFiles`.\n * Derived from DeploymentOptions but excludes client-side only options.\n */\nexport type ApiDeployOptions = Omit<DeploymentOptions, 'pathDetect'>;\n\n// =============================================================================\n// PROGRESS TRACKING\n// =============================================================================\n\n/**\n * Detailed statistics about the progress of an deploy operation.\n */\nexport interface ProgressStats {\n /** The number of bytes loaded so far. */\n loaded: number;\n /** The total number of bytes to be loaded. May be 0 if unknown initially. */\n total: number;\n /** The progress as a fraction (loaded/total). Value is between 0 and 1. */\n progress: number;\n /** Optional identifier for the file this progress pertains to, if applicable. */\n file?: string;\n}\n\n// =============================================================================\n// CLIENT CONFIGURATION\n// =============================================================================\n\n/**\n * Options for configuring a `Ship` instance.\n * Sets default API host, key, progress callbacks, concurrency, and timeouts for the client.\n */\nexport interface ShipClientOptions {\n /** Default API URL for the client instance. */\n apiUrl?: string | undefined;\n /** Default API key for the client instance. */\n apiKey?: string | undefined;\n /** Path to custom config file. */\n configFile?: string | undefined;\n /**\n * Default callback for overall deploy progress for deploys made with this client.\n * @param progress - A number between 0 and 100.\n */\n onProgress?: ((progress: number) => void) | undefined;\n /**\n * Default callback for detailed progress statistics for deploys made with this client.\n * @param progressStats - Progress statistics object.\n */\n onProgressStats?: ((progressStats: ProgressStats) => void) | undefined;\n /**\n * Default for maximum concurrent deploys.\n * Used if an deploy operation doesn't specify its own `maxConcurrency`.\n * Defaults to 4 if not set here or in the specific deploy call.\n */\n maxConcurrentDeploys?: number | undefined;\n /**\n * Default timeout in milliseconds for API requests made by this client instance.\n * Used if an deploy operation doesn't specify its own timeout.\n */\n timeout?: number | undefined;\n}\n\n// =============================================================================\n// FILE REPRESENTATION\n// =============================================================================\n\n/**\n * Represents a file that has been processed and is ready for deploy.\n * Used internally by the SDK and in advanced/manual deploy scenarios.\n */\nexport interface StaticFile {\n /**\n * The content of the file.\n * In Node.js, this is typically a `Buffer`.\n * In the browser, this is typically a `File` or `Blob` object.\n */\n content: File | Buffer | Blob;\n /**\n * The desired path for the file on the server, relative to the deployment root.\n * Should include the filename, e.g., `images/photo.jpg`.\n */\n path: string;\n /**\n * The original absolute file system path (primarily used in Node.js environments).\n * This helps in debugging or associating the server path back to its source.\n */\n filePath?: string;\n /**\n * The MD5 hash (checksum) of the file's content.\n * This is calculated by the SDK before deploy if not provided.\n */\n md5?: string;\n /** The size of the file in bytes. */\n size: number;\n}\n\n// =============================================================================\n// API RESPONSES\n// =============================================================================\n\n// PingResponse is imported from @shipstatic/types (single source of truth)","/**\n * @file Shared, environment-agnostic constants.\n */\n\n/** Default API URL if not otherwise configured. */\nexport const DEFAULT_API = 'https://api.shipstatic.com';","/**\n * @file Manages HTTP requests to the Ship API using native fetch.\n */\nimport * as _mime from 'mime-types';\nimport type { Deployment, DeploymentListResponse, PingResponse, ConfigResponse, DeploymentRemoveResponse, Alias, AliasListResponse, Account } from '@shipstatic/types';\nimport { StaticFile, ApiDeployOptions, ShipClientOptions } from '../types.js';\nimport { ShipError } from '@shipstatic/types';\nimport { getENV } from '../lib/env.js';\nimport { DEFAULT_API } from '../core/constants.js';\n\n// FormData and File types for Node.js environment\n/**\n * Internal type alias for Node.js FormData implementation used during file deploys.\n * @internal\n */\ntype FormDataNode = any;\n/**\n * Internal type alias for Node.js File implementation used during file deploys.\n * @internal\n */\ntype FileNode = any;\n\n/** Default API host URL if not otherwise configured. */\n/** @internal */\nconst DEPLOY_ENDPOINT = '/deployments';\n/** @internal */\nconst PING_ENDPOINT = '/ping';\n/** @internal */\nconst ALIASES_ENDPOINT = '/aliases';\n/** @internal */\nconst CONFIG_ENDPOINT = '/config';\n/** @internal */\nconst ACCOUNT_ENDPOINT = '/account';\n/** @internal */\nconst SPA_CHECK_ENDPOINT = '/spa-check';\n\n/**\n * Determines the MIME type for a given file (File object or path string) in browser environments.\n * Falls back to 'application/octet-stream' if type cannot be determined.\n * @internal\n * @param file - File object or file path string.\n * @returns The MIME type as a string.\n */\nfunction getBrowserContentType(file: File | string): string {\n if (typeof file === 'string') {\n return _mime.lookup(file) || 'application/octet-stream';\n } else {\n return _mime.lookup(file.name) || file.type || 'application/octet-stream';\n }\n}\n\n/**\n * Collects all items from an AsyncIterable into an array.\n * Useful for converting streaming multipart encoders to Buffer arrays.\n * @internal\n * @template T - The item type yielded by the iterable.\n * @param iterable - The async iterable to collect.\n * @returns A promise resolving to an array of all items.\n */\nasync function collectAsyncIterable<T>(iterable: AsyncIterable<T>): Promise<T[]> {\n const result: T[] = [];\n for await (const x of iterable) {\n result.push(x);\n }\n return result;\n}\n\n/**\n * Handles direct HTTP communication with the Ship API, including deploys and health checks.\n * Responsible for constructing requests, managing authentication, and error translation.\n * @internal\n */\nexport class ApiHttp {\n private readonly apiUrl: string;\n private readonly apiKey: string;\n\n /**\n * Constructs a new ApiHttp instance with the provided client options.\n * @param options - Client options including API host, apiKey, and timeout settings.\n */\n constructor(options: ShipClientOptions) {\n this.apiUrl = options.apiUrl || DEFAULT_API;\n this.apiKey = options.apiKey ?? \"\";\n }\n\n /**\n * Generates common headers for API requests, including the API key if present.\n * @param customHeaders - Optional additional headers to include.\n * @returns Object containing merged headers.\n * @private\n */\n #getAuthHeaders(customHeaders: Record<string, string> = {}): Record<string, string> {\n const headers = { ...customHeaders };\n if (this.apiKey) {\n headers['Authorization'] = `Bearer ${this.apiKey}`;\n }\n return headers;\n }\n\n /**\n * Low-level fetch wrapper with authentication - used only by #request method.\n * This method only handles the raw fetch request with auth headers.\n * @param url - Request URL\n * @param options - Fetch options\n * @param operationName - Name of operation for error messages\n * @returns Promise resolving to Response object\n * @private\n */\n async #fetchWithAuth(url: string, options: RequestInit = {}, operationName: string): Promise<Response> {\n const headers = this.#getAuthHeaders(options.headers as Record<string, string>);\n \n const fetchOptions: RequestInit = {\n ...options,\n headers,\n credentials: getENV() === 'browser' ? 'include' : 'same-origin',\n };\n\n try {\n const response = await fetch(url, fetchOptions);\n return response;\n } catch (error: any) {\n this.#handleFetchError(error, operationName);\n // This line is unreachable because #handleFetchError always throws\n throw error;\n }\n }\n\n /**\n * Unified HTTP request helper that handles the complete request lifecycle.\n * Makes authenticated requests, checks response status, and handles all errors.\n * Automatically determines whether to parse JSON based on response headers.\n * @param url - Request URL\n * @param options - Fetch options \n * @param operationName - Name of operation for error messages\n * @returns Promise resolving to parsed JSON response or undefined for empty responses\n * @throws {ShipError} Various ShipError types based on the failure mode\n * @private\n */\n async #request<T>(url: string, options: RequestInit = {}, operationName: string): Promise<T> {\n try {\n const response = await this.#fetchWithAuth(url, options, operationName);\n \n if (!response.ok) {\n await this.#handleResponseError(response, operationName);\n }\n \n // Check if response has content to parse\n const contentLength = response.headers.get('Content-Length');\n if (contentLength === '0' || response.status === 204) {\n return undefined as T; // Return undefined for empty responses\n }\n \n return await response.json() as T;\n } catch (error: any) {\n if (error instanceof ShipError) {\n throw error;\n }\n this.#handleFetchError(error, operationName);\n // This line is unreachable because #handleFetchError always throws\n throw error;\n }\n }\n \n /**\n * Sends a ping request to the Ship API server to verify connectivity and authentication.\n * @returns Promise resolving to `true` if the ping is successful, `false` otherwise.\n * @throws {ShipApiError} If the API returns an error response (4xx, 5xx).\n * @throws {ShipNetworkError} If a network error occurs (e.g., DNS failure, connection refused).\n */\n public async ping(): Promise<boolean> {\n const data = await this.#request<PingResponse>(`${this.apiUrl}${PING_ENDPOINT}`, { method: 'GET' }, 'Ping');\n return data?.success || false;\n }\n\n /**\n * Get full ping response from the API server\n * @returns Promise resolving to the full PingResponse object.\n */\n public async getPingResponse(): Promise<PingResponse> {\n return await this.#request<PingResponse>(`${this.apiUrl}${PING_ENDPOINT}`, { method: 'GET' }, 'Ping');\n }\n\n /**\n * Fetches platform configuration from the API.\n * @returns Promise resolving to the config response.\n * @throws {ShipError} If the config request fails.\n */\n public async getConfig(): Promise<ConfigResponse> {\n return await this.#request<ConfigResponse>(`${this.apiUrl}${CONFIG_ENDPOINT}`, { method: 'GET' }, 'Config');\n }\n\n /**\n * Deploys an array of StaticFile objects to the Ship API.\n * Constructs and sends a multipart/form-data POST request, handling both browser and Node.js environments.\n * Validates files and manages deploy progress and error translation.\n * @param files - Array of StaticFile objects to deploy (must include MD5 checksums).\n * @param options - Optional per-deploy configuration (overrides instance defaults).\n * @returns Promise resolving to a full Deployment object on success.\n * @throws {ShipFileError} If a file is missing an MD5 checksum or content type is unsupported.\n * @throws {ShipClientError} If no files are provided or if environment is unknown.\n * @throws {ShipNetworkError} If a network error occurs during deploy.\n * @throws {ShipApiError} If the API returns an error response.\n * @throws {ShipCancelledError} If the deploy is cancelled via an AbortSignal.\n */\n public async deploy(\n files: StaticFile[],\n options: ApiDeployOptions = {}\n ): Promise<Deployment> {\n this.#validateFiles(files);\n\n const {\n apiUrl = this.apiUrl,\n signal\n } = options;\n\n const { requestBody, requestHeaders } = await this.#prepareRequestPayload(files);\n \n const fetchOptions: RequestInit = {\n method: 'POST',\n body: requestBody,\n headers: requestHeaders,\n signal: signal || null\n };\n\n // Use unified request method to eliminate duplication\n return await this.#request<Deployment>(`${apiUrl}${DEPLOY_ENDPOINT}`, fetchOptions, 'Deploy');\n }\n \n /**\n * Validates the files array for deploy requirements.\n * Ensures all files have MD5 checksums and at least one file is present.\n * @param files - Files to validate.\n * @throws {ShipFileError} If a file is missing an MD5 checksum.\n * @throws {ShipClientError} If no files are provided.\n * @private\n */\n #validateFiles(files: StaticFile[]): void {\n if (!files.length) {\n throw ShipError.business('No files to deploy.');\n }\n \n for (const file of files) {\n if (!file.md5) {\n throw ShipError.file(`MD5 checksum missing for file: ${file.path}`, file.path);\n }\n }\n }\n\n\n /**\n * Prepares the request payload (body and headers) for deploy based on execution environment.\n * Selects browser or Node.js multipart construction as needed.\n * @param files - Files to deploy.\n * @returns Promise resolving to request body and headers.\n * @private\n */\n async #prepareRequestPayload(files: StaticFile[]): Promise<{\n requestBody: FormData | Buffer;\n requestHeaders: Record<string, string>;\n }> {\n let requestBody: FormData | Buffer;\n let requestHeaders: Record<string, string> = {};\n \n if (getENV() === 'browser') {\n requestBody = this.#createBrowserBody(files);\n } else if (getENV() === 'node') {\n const { body, headers } = await this.#createNodeBody(files);\n requestBody = body;\n requestHeaders = headers;\n } else {\n throw ShipError.business('Unknown or unsupported execution environment');\n }\n \n return { requestBody, requestHeaders };\n }\n\n /**\n * Creates a FormData object for browser environments, populating it with files and checksums.\n * @param files - Array of StaticFile objects to include in the FormData.\n * @returns FormData object ready for transmission.\n * @throws {ShipFileError} If file content is of an unsupported type for browser FormData.\n * @private\n */\n #createBrowserBody(files: StaticFile[]): FormData {\n const formData = new FormData();\n const checksums: string[] = [];\n \n for (let i = 0; i < files.length; i++) {\n const file = files[i];\n let fileContent: File | Blob;\n if (file.content instanceof File || file.content instanceof Blob) {\n fileContent = file.content;\n } else {\n throw ShipError.file(`Unsupported file.content type for browser FormData: ${file.path}`, file.path);\n }\n const contentType = getBrowserContentType(fileContent instanceof File ? fileContent : file.path);\n const fileWithPath = new File([fileContent], file.path, { type: contentType });\n formData.append('files[]', fileWithPath);\n checksums.push(file.md5!);\n }\n \n // Add checksums as JSON array\n formData.append('checksums', JSON.stringify(checksums));\n return formData;\n }\n\n /**\n * Creates the multipart request body (Buffer) and headers for Node.js environments using formdata-node and form-data-encoder.\n * @param files - Array of StaticFile objects to include in the multipart body.\n * @returns Promise resolving to an object with the body Buffer and headers.\n * @throws {ShipFileError} If file content is of an unsupported type for Node.js FormData.\n * @private\n */\n async #createNodeBody(files: StaticFile[]): Promise<{body: Buffer, headers: Record<string, string>}> {\n const { FormData: FormDataNodeClass, File: FileNodeClass } = await import('formdata-node');\n const { FormDataEncoder: FormDataEncoderClass } = await import('form-data-encoder');\n const pathImport = await import('path');\n const formDataNodeInstance: FormDataNode = new FormDataNodeClass();\n const checksums: string[] = [];\n\n for (let i = 0; i < files.length; i++) {\n const file = files[i];\n const contentType = _mime.lookup(file.path) || 'application/octet-stream';\n let fileNodeInstance: FileNode;\n\n if (Buffer.isBuffer(file.content)) {\n fileNodeInstance = new FileNodeClass([file.content], file.path, { type: contentType });\n } else if (typeof Blob !== \"undefined\" && file.content instanceof Blob) {\n fileNodeInstance = new FileNodeClass([file.content], file.path, { type: contentType });\n } else {\n throw ShipError.file(`Unsupported file.content type for Node.js FormData: ${file.path}`, file.path);\n }\n const preservedPath = file.path.startsWith('/') ? file.path : '/' + file.path;\n formDataNodeInstance.append('files[]', fileNodeInstance, preservedPath);\n checksums.push(file.md5!);\n }\n\n // Add checksums as JSON array\n formDataNodeInstance.append('checksums', JSON.stringify(checksums));\n\n const encoder = new FormDataEncoderClass(formDataNodeInstance);\n const encodedChunks = await collectAsyncIterable(encoder.encode());\n const body = Buffer.concat(encodedChunks.map(chunk => Buffer.from(chunk as Uint8Array)));\n\n const headers = {\n 'Content-Type': encoder.contentType,\n 'Content-Length': Buffer.byteLength(body).toString()\n };\n return { body, headers };\n }\n\n /**\n * Handles fetch response errors and throws appropriate ShipError types.\n * @param response - Fetch Response object with error status\n * @param operationName - Name of the failed operation\n * @throws {ShipApiError} Always throws with API error details\n * @private\n */\n async #handleResponseError(response: Response, operationName: string): Promise<never> {\n let errorData: any = {};\n try {\n const contentType = response.headers.get('content-type');\n if (contentType && contentType.includes('application/json')) {\n errorData = await response.json();\n } else {\n errorData = { message: await response.text() };\n }\n } catch {\n errorData = { message: 'Failed to parse error response' };\n }\n\n // Handle structured error responses (with error field, code, or message)\n if (errorData.error || errorData.code || errorData.message) {\n // Use message if available, otherwise use the error field as the message, or fallback\n const message = errorData.message || errorData.error || `${operationName} failed due to API error`;\n \n // Handle authentication errors specifically\n if (response.status === 401) {\n throw ShipError.authentication(message);\n }\n \n throw ShipError.api(\n message,\n response.status,\n errorData.code,\n errorData\n );\n }\n\n // Fallback for completely unstructured errors\n // Handle authentication errors specifically for unstructured responses too\n if (response.status === 401) {\n throw ShipError.authentication(`Authentication failed for ${operationName}`);\n }\n \n throw ShipError.api(\n `${operationName} failed due to API error`,\n response.status,\n undefined,\n errorData\n );\n }\n\n /**\n * Translates fetch errors into appropriate ShipError types and always throws.\n * Intended for use in catch blocks of API requests; never returns.\n * @param error - The error object caught from fetch.\n * @param operationName - Name of the failed operation (e.g., 'Deploy', 'Ping').\n * @throws {ShipCancelledError} If the request was cancelled.\n * @throws {ShipNetworkError} If a network error occurred (DNS failure, connection refused).\n * @throws {ShipClientError} For other unexpected errors.\n * @private\n */\n #handleFetchError(error: any, operationName: string): never {\n if (error.name === 'AbortError') {\n throw ShipError.cancelled(`${operationName} operation was cancelled.`);\n }\n if (error instanceof TypeError && error.message.includes('fetch')) {\n throw ShipError.network(`${operationName} failed due to network error: ${error.message}`, error);\n }\n if (error instanceof ShipError) {\n throw error;\n }\n throw ShipError.business(`An unexpected error occurred during ${operationName}: ${error.message || 'Unknown error'}`);\n }\n\n /**\n * Lists all deployments for the authenticated account\n * @returns Promise resolving to deployment list response\n */\n public async listDeployments(): Promise<DeploymentListResponse> {\n return await this.#request<DeploymentListResponse>(`${this.apiUrl}${DEPLOY_ENDPOINT}`, { method: 'GET' }, 'List Deployments');\n }\n\n /**\n * Gets a specific deployment by ID\n * @param id - Deployment ID to retrieve\n * @returns Promise resolving to deployment details\n */\n public async getDeployment(id: string): Promise<Deployment> {\n return await this.#request<Deployment>(`${this.apiUrl}${DEPLOY_ENDPOINT}/${id}`, { method: 'GET' }, 'Get Deployment');\n }\n\n /**\n * Removes a deployment by ID\n * @param id - Deployment ID to remove\n * @returns Promise resolving when removal is complete\n */\n public async removeDeployment(id: string): Promise<void> {\n await this.#request<DeploymentRemoveResponse>(`${this.apiUrl}${DEPLOY_ENDPOINT}/${id}`, { method: 'DELETE' }, 'Remove Deployment');\n }\n\n /**\n * Sets an alias (create or update)\n * @param name - Alias name\n * @param deployment - Deployment name to point to\n * @returns Promise resolving to the created/updated alias with operation context\n */\n public async setAlias(name: string, deployment: string): Promise<import('@shipstatic/types').Alias> {\n try {\n const response = await this.#fetchWithAuth(`${this.apiUrl}${ALIASES_ENDPOINT}/${encodeURIComponent(name)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ deployment: deployment })\n }, 'Set Alias');\n \n if (!response.ok) {\n await this.#handleResponseError(response, 'Set Alias');\n }\n \n const alias = await response.json() as import('@shipstatic/types').Alias;\n \n // 201 = created, 200 = updated\n return {\n ...alias,\n isCreate: response.status === 201\n };\n } catch (error: any) {\n this.#handleFetchError(error, 'Set Alias');\n }\n }\n\n /**\n * Gets a specific alias by name\n * @param name - Alias name to retrieve\n * @returns Promise resolving to alias details\n */\n public async getAlias(name: string): Promise<Alias> {\n return await this.#request<Alias>(`${this.apiUrl}${ALIASES_ENDPOINT}/${encodeURIComponent(name)}`, { method: 'GET' }, 'Get Alias');\n }\n\n /**\n * Lists all aliases for the authenticated account\n * @returns Promise resolving to alias list response\n */\n public async listAliases(): Promise<AliasListResponse> {\n return await this.#request<AliasListResponse>(`${this.apiUrl}${ALIASES_ENDPOINT}`, { method: 'GET' }, 'List Aliases');\n }\n\n /**\n * Removes an alias by name\n * @param name - Alias name to remove\n * @returns Promise resolving to removal confirmation\n */\n public async removeAlias(name: string): Promise<void> {\n await this.#request<void>(`${this.apiUrl}${ALIASES_ENDPOINT}/${encodeURIComponent(name)}`, { method: 'DELETE' }, 'Remove Alias');\n }\n\n /**\n * Gets account details for the authenticated user\n * @returns Promise resolving to account details\n */\n public async getAccount(): Promise<Account> {\n return await this.#request<Account>(`${this.apiUrl}${ACCOUNT_ENDPOINT}`, { method: 'GET' }, 'Get Account');\n }\n\n /**\n * Creates a new API key for the authenticated user\n * @returns Promise resolving to the new API key\n */\n public async createApiKey(): Promise<{ apiKey: string }> {\n return await this.#request<{ apiKey: string }>(`${this.apiUrl}/key`, { method: 'POST' }, 'Create API Key');\n }\n\n /**\n * Checks if file paths represent a SPA structure using AI analysis\n * @param filePaths - Array of file paths to analyze\n * @returns Promise resolving to boolean indicating if it's a SPA\n */\n public async checkSPA(filePaths: string[]): Promise<boolean> {\n const response = await this.#request<{ isSPA: boolean }>(\n `${this.apiUrl}${SPA_CHECK_ENDPOINT}`, \n { \n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ files: filePaths })\n }, \n 'SPA Check'\n );\n return response.isSPA;\n }\n}\n","/**\n * @file Main entry point for the Ship SDK.\n * This module provides a simplified class-based SDK interface similar to Vercel's approach.\n */\n\n// Core imports\nimport { getENV } from './lib/env.js';\nimport { loadConfig, resolveConfig } from './core/config.js';\nimport { ApiHttp } from './api/http.js';\nimport { ShipError } from '@shipstatic/types';\nimport { setConfig } from './core/platform-config.js';\nimport type { ShipClientOptions } from './types.js';\n\n// Resource imports\nimport { createDeploymentResource, createAliasResource, createAccountResource, createKeysResource } from './resources.js';\nimport type { DeploymentResource, AliasResource, AccountResource, KeysResource, DeployInput } from './resources.js';\nimport type { DeploymentOptions } from './types.js';\nimport type { Deployment } from '@shipstatic/types';\n\n\n\n// Re-export types from deploy types\nexport type { DeployInput, DeploymentOptions } from './types.js';\n\n\n/**\n * Ship SDK Client - Universal class-based interface for both Node.js and browser environments.\n * \n * ```\n * const ship = new Ship({ apiKey: \"your-api-key\" });\n * ```\n * \n * Automatically detects the environment and handles Node.js and browser deploys directly.\n * In Node.js environments, loads configuration from files and environment variables.\n * In browser environments, uses only the provided options.\n */\nexport class Ship {\n private http: ApiHttp;\n private environment: 'node' | 'browser';\n private readonly clientOptions: ShipClientOptions;\n private initPromise: Promise<void> | null = null;\n \n // Resource instances (initialized during creation)\n private _deployments: DeploymentResource;\n private _aliases: AliasResource;\n private _account: AccountResource;\n private _keys: KeysResource;\n\n constructor(options: ShipClientOptions = {}) {\n this.clientOptions = options;\n this.environment = getENV() as 'node' | 'browser';\n \n if (this.environment !== 'node' && this.environment !== 'browser') {\n throw ShipError.business('Unsupported execution environment.');\n }\n \n // Initialize HTTP client with constructor options for immediate use\n const config = resolveConfig(options, {});\n this.http = new ApiHttp({ ...options, ...config });\n \n // Initialize resources with lazy loading support\n const initCallback = this.getInitCallback();\n const getApi = () => this.http; // Dynamic getter for current HTTP client\n this._deployments = createDeploymentResource(getApi, this.clientOptions, initCallback);\n this._aliases = createAliasResource(getApi, initCallback);\n this._account = createAccountResource(getApi, initCallback);\n this._keys = createKeysResource(getApi, initCallback);\n }\n\n /**\n * Ensure full initialization is complete - called lazily by resources\n */\n private async ensureInitialized(): Promise<void> {\n if (!this.initPromise) {\n this.initPromise = this.initializeConfig();\n }\n return this.initPromise;\n }\n\n /**\n * Helper method to create initialization callback for resources\n */\n private getInitCallback() {\n return () => this.ensureInitialized();\n }\n\n /**\n * Initialize config from file/env and platform config from API\n */\n private async initializeConfig(): Promise<void> {\n try {\n // Load config from file/env\n const loadedConfig = await loadConfig(this.clientOptions.configFile);\n // Re-resolve and re-create the http client with the full config\n const finalConfig = resolveConfig(this.clientOptions, loadedConfig);\n this.http = new ApiHttp({ ...this.clientOptions, ...finalConfig });\n \n const platformConfig = await this.http.getConfig();\n setConfig(platformConfig);\n } catch (error) {\n // Reset initialization promise so it can be retried\n this.initPromise = null;\n throw error;\n }\n }\n\n /**\n * Ping the API server to check connectivity\n */\n async ping(): Promise<boolean> {\n // Ensure initialization before any HTTP operations\n await this.ensureInitialized();\n return this.http.ping();\n }\n\n /**\n * Deploy project (convenience shortcut to ship.deployments.create())\n */\n async deploy(input: DeployInput, options?: DeploymentOptions): Promise<Deployment> {\n return this.deployments.create(input, options);\n }\n\n /**\n * Get current account information (convenience shortcut to ship.account.get())\n */\n async whoami() {\n return this.account.get();\n }\n\n /**\n * Get deployments resource (environment-specific)\n */\n get deployments(): DeploymentResource {\n return this._deployments;\n }\n \n /**\n * Get aliases resource\n */\n get aliases(): AliasResource {\n return this._aliases;\n }\n \n /**\n * Get account resource\n */\n get account(): AccountResource {\n return this._account;\n }\n \n /**\n * Get keys resource\n */\n get keys(): KeysResource {\n return this._keys;\n }\n\n\n}\n\n// Default export (for import Ship from 'ship')\nexport default Ship;\n\n// Export all public types\nexport type { StaticFile, ShipClientOptions, ApiDeployOptions, ProgressStats } from './types.js';\nexport type { PingResponse } from '@shipstatic/types';\n\n// Export resource types\nexport type { DeploymentResource, AliasResource, AccountResource, KeysResource } from './resources.js';\n\n// Export main error class and error type enum\nexport { ShipError, ShipErrorType } from '@shipstatic/types';\n\n// Advanced/utility exports (for tests and power users; subject to change)\nexport { processFilesForNode } from './lib/node-files.js';\nexport { processFilesForBrowser } from './lib/browser-files.js';\n\n// Test utilities\n/**\n * @internal\n * Test utility to set the execution environment (e.g., 'node', 'browser').\n * This should not be used in production code.\n */\nexport { __setTestEnvironment } from './lib/env.js';\n","/**\n * @file Platform configuration management for the Ship SDK.\n * Implements fail-fast dynamic configuration with mandatory API fetch.\n */\n\nimport type { ConfigResponse } from '@shipstatic/types';\nimport { ShipError } from '@shipstatic/types';\n\n// Dynamic config - must be fetched from API before operations\nlet _config: ConfigResponse | null = null;\n\n/**\n * Set the current config (called after fetching from API)\n */\nexport function setConfig(config: ConfigResponse): void {\n _config = config;\n}\n\n/**\n * Get current config - throws if not initialized (fail-fast approach)\n * @throws {ShipError.config} If configuration hasn't been fetched from API\n */\nexport function getCurrentConfig(): ConfigResponse {\n if (_config === null) {\n throw ShipError.config(\n 'Platform configuration not initialized. The SDK must fetch configuration from the API before performing operations.'\n );\n }\n return _config;\n}\n\n/**\n * Check if config has been initialized from API\n */\nexport function isConfigInitialized(): boolean {\n return _config !== null;\n}\n\n/**\n * Reset config state (primarily for testing)\n * @internal\n */\nexport function resetConfig(): void {\n _config = null;\n}","/**\n * @file Input conversion utilities for deployment\n * Converts various input types to StaticFile[] for unified processing\n */\n\nimport type { StaticFile, DeploymentOptions, DeployInput } from '../types.js';\nimport { ShipError, DEPLOYMENT_CONFIG_FILENAME } from '@shipstatic/types';\nimport { getENV } from './env.js';\nimport { processFilesForNode } from './node-files.js';\nimport { processFilesForBrowser } from './browser-files.js';\nimport { getCurrentConfig } from '../core/platform-config.js';\nimport { calculateMD5 } from './md5.js';\n\n\n/**\n * Fail-fast file validation for both Node.js and browser environments\n * Validates immediately without collecting all files first for better performance\n * @param files - Array of files to validate (can be File[] or file metadata)\n * @param options - Validation options\n * @throws {ShipError} If validation fails\n * @internal\n */\nfunction validateFiles(files: Array<{ name: string; size: number }>, options: { skipEmptyCheck?: boolean } = {}): void {\n const config = getCurrentConfig();\n \n // Check for empty file array - fail fast\n if (!options.skipEmptyCheck && files.length === 0) {\n throw ShipError.business('No files to deploy.');\n }\n \n // Check file count limit - fail fast\n if (files.length > config.maxFilesCount) {\n throw ShipError.business(`Too many files to deploy. Maximum allowed is ${config.maxFilesCount}.`);\n }\n \n // Validate individual files and calculate total size - fail on first violation\n let totalSize = 0;\n for (const file of files) {\n // Individual file size validation - fail immediately\n if (file.size > config.maxFileSize) {\n throw ShipError.business(`File ${file.name} is too large. Maximum allowed size is ${config.maxFileSize / (1024 * 1024)}MB.`);\n }\n \n // Accumulate total size and check incrementally for early failure\n totalSize += file.size;\n if (totalSize > config.maxTotalSize) {\n throw ShipError.business(`Total deploy size is too large. Maximum allowed is ${config.maxTotalSize / (1024 * 1024)}MB.`);\n }\n }\n}\n\n/**\n * Early validation for file count and basic input\n * Used before file processing to fail fast on obvious issues\n * @param input - Input to validate\n * @param environment - Current environment (node/browser)\n * @throws {ShipError} If validation fails\n * @internal\n */\nfunction validateInputEarly(input: any, environment: string): void {\n if (environment === 'node') {\n if (!Array.isArray(input)) {\n throw ShipError.business('Invalid input type for Node.js environment. Expected string[] file paths.');\n }\n if (input.length === 0) {\n throw ShipError.business('No files to deploy.');\n }\n if (!input.every(item => typeof item === 'string')) {\n throw ShipError.business('Invalid input type for Node.js environment. Expected string[] file paths.');\n }\n } else if (environment === 'browser') {\n if (input instanceof HTMLInputElement && !input.files) {\n throw ShipError.business('No files selected in HTMLInputElement');\n }\n }\n}\n\n/**\n * Shared post-processing logic for StaticFile arrays\n * @param files - Array of StaticFile objects to process\n * @returns Processed StaticFile array\n * @internal\n */\nfunction postProcessFiles(files: StaticFile[]): StaticFile[] {\n // Validate processed files\n validateFiles(files, { skipEmptyCheck: true });\n \n // Normalize paths to forward slashes\n files.forEach(f => {\n if (f.path) f.path = f.path.replace(/\\\\/g, '/');\n });\n \n return files;\n}\n\n/**\n * Converts Node.js string[] paths to StaticFile[]\n */\nexport async function convertNodeInput(\n input: string[], \n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n // Early validation - fail fast before processing\n validateInputEarly(input, 'node');\n\n // Pass options directly to node processor - no conflicting logic here\n const staticFiles: StaticFile[] = await processFilesForNode(input, options);\n \n // Apply shared validation and post-processing\n return postProcessFiles(staticFiles);\n}\n\n/**\n * Converts browser FileList/File[]/HTMLInputElement to StaticFile[]\n */\nexport async function convertBrowserInput(\n input: FileList | File[] | HTMLInputElement,\n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n // Early validation - fail fast before processing\n validateInputEarly(input, 'browser');\n\n let fileArray: File[];\n \n if (input instanceof HTMLInputElement) {\n fileArray = Array.from(input.files!);\n } else if (\n typeof input === 'object' &&\n input !== null &&\n typeof (input as any).length === 'number' &&\n typeof (input as any).item === 'function'\n ) {\n fileArray = Array.from(input as FileList);\n } else if (Array.isArray(input)) {\n if (input.length > 0 && typeof input[0] === 'string') {\n throw ShipError.business('Invalid input type for browser environment. Expected File[], FileList, or HTMLInputElement.');\n }\n fileArray = input as File[];\n } else {\n throw ShipError.business('Invalid input type for browser environment. Expected File[], FileList, or HTMLInputElement.');\n }\n\n // Filter out empty files first\n fileArray = fileArray.filter(file => {\n if (file.size === 0) {\n console.warn(`Skipping empty file: ${file.name}`);\n return false;\n }\n return true;\n });\n\n // Early validation using shared logic - fail fast before heavy processing\n validateFiles(fileArray);\n\n // Pass options directly to browser processor - no conflicting logic here\n const staticFiles: StaticFile[] = await processFilesForBrowser(fileArray as File[], options);\n \n // Apply shared validation and post-processing\n return postProcessFiles(staticFiles);\n}\n\n/**\n * Unified input conversion function with automatic SPA detection\n * Converts any DeployInput to StaticFile[] and auto-generates ship.json for SPAs\n */\nexport async function convertDeployInput(\n input: DeployInput,\n options: DeploymentOptions = {},\n apiClient?: any\n): Promise<StaticFile[]> {\n const environment = getENV();\n \n // Early validation at the unified level - fail immediately on environment issues\n if (environment !== 'node' && environment !== 'browser') {\n throw ShipError.business('Unsupported execution environment.');\n }\n \n // Convert input to StaticFile[] based on environment\n let files: StaticFile[];\n if (environment === 'node') {\n files = await convertNodeInput(input as string[], options);\n } else {\n files = await convertBrowserInput(input as FileList | File[] | HTMLInputElement, options);\n }\n \n // Auto-detect and configure SPA projects\n if (apiClient) {\n files = await detectAndConfigureSPA(files, apiClient, options);\n }\n \n return files;\n}\n\n/**\n * Creates ship.json configuration for SPA projects\n * @private\n */\nasync function createSPAConfig(): Promise<StaticFile> {\n const config = {\n \"rewrites\": [{\n \"source\": \"/(.*)\",\n \"destination\": \"/index.html\"\n }]\n };\n \n const content = Buffer.from(JSON.stringify(config, null, 2), 'utf-8');\n const { md5 } = await calculateMD5(content);\n \n return {\n path: DEPLOYMENT_CONFIG_FILENAME,\n content,\n size: content.length,\n md5\n };\n}\n\n/**\n * Detects SPA projects and auto-generates configuration\n * @private\n */\nasync function detectAndConfigureSPA(files: StaticFile[], apiClient: any, options: DeploymentOptions): Promise<StaticFile[]> {\n // Skip if disabled or config already exists\n if (options.spaDetect === false || files.some(f => f.path === DEPLOYMENT_CONFIG_FILENAME)) {\n return files;\n }\n \n try {\n const filePaths = files.map(f => f.path);\n const isSPA = await apiClient.checkSPA(filePaths);\n \n if (isSPA) {\n const spaConfig = await createSPAConfig();\n console.log(`SPA detected - generated ${DEPLOYMENT_CONFIG_FILENAME}`);\n return [...files, spaConfig];\n }\n } catch (error) {\n console.warn('SPA detection failed, continuing without auto-config');\n }\n \n return files;\n}","/**\n * @file Simplified MD5 calculation utility with separate environment handlers.\n */\nimport { getENV } from './env';\nimport { ShipError } from '@shipstatic/types';\n\nexport interface MD5Result {\n md5: string;\n}\n\n/**\n * Browser-specific MD5 calculation for Blob/File objects\n */\nasync function calculateMD5Browser(blob: Blob): Promise<MD5Result> {\n const SparkMD5 = (await import('spark-md5')).default;\n \n return new Promise((resolve, reject) => {\n const chunkSize = 2097152; // 2MB chunks\n const chunks = Math.ceil(blob.size / chunkSize);\n let currentChunk = 0;\n const spark = new SparkMD5.ArrayBuffer();\n const fileReader = new FileReader();\n\n const loadNext = () => {\n const start = currentChunk * chunkSize;\n const end = Math.min(start + chunkSize, blob.size);\n fileReader.readAsArrayBuffer(blob.slice(start, end));\n };\n\n fileReader.onload = (e) => {\n const result = e.target?.result as ArrayBuffer;\n if (!result) {\n reject(ShipError.business('Failed to read file chunk'));\n return;\n }\n \n spark.append(result);\n currentChunk++;\n \n if (currentChunk < chunks) {\n loadNext();\n } else {\n resolve({ md5: spark.end() });\n }\n };\n\n fileReader.onerror = () => {\n reject(ShipError.business('Failed to calculate MD5: FileReader error'));\n };\n\n loadNext();\n });\n}\n\n/**\n * Node.js-specific MD5 calculation for Buffer or file path\n */\nasync function calculateMD5Node(input: Buffer | string): Promise<MD5Result> {\n const crypto = await import('crypto');\n \n if (Buffer.isBuffer(input)) {\n const hash = crypto.createHash('md5');\n hash.update(input);\n return { md5: hash.digest('hex') };\n }\n \n // Handle file path\n const fs = await import('fs');\n return new Promise((resolve, reject) => {\n const hash = crypto.createHash('md5');\n const stream = fs.createReadStream(input);\n \n stream.on('error', err => \n reject(ShipError.business(`Failed to read file for MD5: ${err.message}`))\n );\n stream.on('data', chunk => hash.update(chunk));\n stream.on('end', () => resolve({ md5: hash.digest('hex') }));\n });\n}\n\n/**\n * Unified MD5 calculation that delegates to environment-specific handlers\n */\nexport async function calculateMD5(input: Blob | Buffer | string): Promise<MD5Result> {\n const env = getENV();\n \n if (env === 'browser') {\n if (!(input instanceof Blob)) {\n throw ShipError.business('Invalid input for browser MD5 calculation: Expected Blob or File.');\n }\n return calculateMD5Browser(input);\n }\n \n if (env === 'node') {\n if (!(Buffer.isBuffer(input) || typeof input === 'string')) {\n throw ShipError.business('Invalid input for Node.js MD5 calculation: Expected Buffer or file path string.');\n }\n return calculateMD5Node(input);\n }\n \n throw ShipError.business('Unknown or unsupported execution environment for MD5 calculation.');\n}\n","/**\n * @file Utility for filtering out junk files and directories from file paths\n * \n * This module provides functionality to filter out common system junk files and directories\n * from a list of file paths. It uses the 'junk' package to identify junk filenames and\n * a custom list to filter out common junk directories.\n */\nimport { isJunk } from 'junk';\n\n/**\n * List of directory names considered as junk\n * \n * Files within these directories (at any level in the path hierarchy) will be excluded.\n * The comparison is case-insensitive for cross-platform compatibility.\n * \n * @internal\n */\nexport const JUNK_DIRECTORIES = [\n '__MACOSX',\n '.Trashes',\n '.fseventsd',\n '.Spotlight-V100',\n] as const;\n\n/**\n * Filters an array of file paths, removing those considered junk\n * \n * A path is filtered out if either:\n * 1. The basename is identified as junk by the 'junk' package (e.g., .DS_Store, Thumbs.db)\n * 2. Any directory segment in the path matches an entry in JUNK_DIRECTORIES (case-insensitive)\n *\n * All path separators are normalized to forward slashes for consistent cross-platform behavior.\n * \n * @param filePaths - An array of file path strings to filter\n * @returns A new array containing only non-junk file paths\n */\nexport function filterJunk(filePaths: string[]): string[] {\n if (!filePaths || filePaths.length === 0) {\n return [];\n }\n\n return filePaths.filter(filePath => {\n if (!filePath) {\n return false; // Exclude null or undefined paths\n }\n\n // Normalize path separators to forward slashes and split into segments\n const parts = filePath.replace(/\\\\/g, '/').split('/').filter(Boolean);\n if (parts.length === 0) return true;\n \n // Check if the basename is a junk file (using junk package)\n const basename = parts[parts.length - 1];\n if (isJunk(basename)) {\n return false;\n }\n\n // Check if any directory segment is in our junk directories list\n const directorySegments = parts.slice(0, -1);\n for (const segment of directorySegments) {\n if (JUNK_DIRECTORIES.some(junkDir => \n segment.toLowerCase() === junkDir.toLowerCase())) {\n return false;\n }\n }\n\n return true;\n });\n}\n","/**\n * @file Node.js-specific file utilities for the Ship SDK.\n * Provides helpers for recursively discovering, filtering, and preparing files for deploy in Node.js.\n */\nimport { getENV } from './env.js';\nimport { StaticFile, DeploymentOptions } from '../types.js';\nimport { calculateMD5 } from './md5.js';\nimport { filterJunk } from './junk.js';\nimport { ShipError } from '@shipstatic/types';\nimport { getCurrentConfig } from '../core/platform-config.js';\nimport { findCommonParent } from './path.js';\n\nimport * as fs from 'fs';\nimport * as path from 'path';\n\n\n/**\n * Simple recursive function to walk directory and return all file paths.\n * More declarative and focused than the previous implementation.\n * @param dirPath - Directory path to traverse\n * @returns Array of absolute file paths in the directory\n */\nfunction findAllFilePaths(dirPath: string): string[] {\n const results: string[] = [];\n \n try {\n const entries = fs.readdirSync(dirPath);\n \n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry);\n const stats = fs.statSync(fullPath);\n \n if (stats.isDirectory()) {\n const subFiles = findAllFilePaths(fullPath);\n results.push(...subFiles);\n } else if (stats.isFile()) {\n results.push(fullPath);\n }\n }\n } catch (error) {\n console.error(`Error reading directory ${dirPath}:`, error);\n }\n \n return results;\n}\n\n/**\n * Clean, declarative function to get files from a source path.\n * Follows the suggested architectural pattern from the feedback.\n * @param sourcePath - File or directory path to process\n * @param options - Options for processing (basePath, preserveDirs)\n * @returns Promise resolving to array of StaticFile objects\n */\nexport async function getFilesFromPath(\n sourcePath: string, \n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n const absolutePath = path.resolve(sourcePath);\n \n // 1. Discover all files\n const allPaths = (() => {\n const stats = fs.statSync(absolutePath);\n if (stats.isFile()) {\n return [absolutePath];\n } else if (stats.isDirectory()) {\n return findAllFilePaths(absolutePath);\n } else {\n return [];\n }\n })();\n \n // 2. Filter out junk\n const validPaths = allPaths.filter(p => {\n const basename = path.basename(p);\n return filterJunk([basename]).length > 0; // Keep files that pass junk filter\n });\n \n // 3. Determine base for relative paths with automatic path optimization\n const stats = fs.statSync(absolutePath);\n let commonParent: string;\n \n // Default to path detection (flattening) unless explicitly disabled\n if (options.pathDetect === false) {\n // Path detection disabled: preserve directory structure\n commonParent = stats.isDirectory() ? absolutePath : path.dirname(absolutePath);\n } else {\n // Path detection enabled: optimize by finding common parent\n const fileDirs = validPaths.map(filePath => path.dirname(filePath));\n commonParent = findCommonParent(fileDirs);\n }\n \n // 4. Process into StaticFile objects\n const results: StaticFile[] = [];\n let totalSize = 0;\n \n for (const filePath of validPaths) {\n try {\n // Validate file\n const stats = fs.statSync(filePath);\n \n if (stats.size === 0) {\n console.warn(`Skipping empty file: ${filePath}`);\n continue;\n }\n \n const platformLimits = getCurrentConfig();\n if (stats.size > platformLimits.maxFileSize) {\n throw ShipError.business(`File ${filePath} is too large. Maximum allowed size is ${platformLimits.maxFileSize / (1024 * 1024)}MB.`);\n }\n \n totalSize += stats.size;\n if (totalSize > platformLimits.maxTotalSize) {\n throw ShipError.business(`Total deploy size is too large. Maximum allowed is ${platformLimits.maxTotalSize / (1024 * 1024)}MB.`);\n }\n \n // Read content and calculate metadata\n const content = fs.readFileSync(filePath);\n const { md5 } = await calculateMD5(content);\n let relativePath = path.relative(commonParent, filePath).replace(/\\\\/g, '/');\n \n // Security validation: Ensure no dangerous characters in paths\n if (relativePath.includes('\\0') || relativePath.includes('/../') || relativePath.startsWith('../') || relativePath.endsWith('/..')) {\n // Only flatten if there are actual dangerous path traversals\n // Allow legitimate .. that might appear in the middle of a path component (like in filenames)\n relativePath = path.basename(filePath);\n }\n \n // Ensure path is not empty (this can happen if file equals commonParent)\n if (!relativePath) {\n relativePath = path.basename(filePath);\n }\n \n results.push({\n path: relativePath,\n content: content,\n size: content.length,\n md5,\n });\n } catch (error) {\n if (error instanceof ShipError && error.isClientError && error.isClientError()) {\n throw error;\n }\n console.error(`Could not process file ${filePath}:`, error);\n }\n }\n \n // Validate total file count\n const platformLimits = getCurrentConfig();\n if (results.length > platformLimits.maxFilesCount) {\n throw ShipError.business(`Too many files to deploy. Maximum allowed is ${platformLimits.maxFilesCount} files.`);\n }\n \n return results;\n}\n\n\n/**\n * Processes Node.js file and directory paths into an array of StaticFile objects ready for deploy.\n * Now uses the simplified, declarative approach suggested in the feedback.\n * \n * @param paths - File or directory paths to scan and process.\n * @param options - Processing options (basePath, preserveDirs).\n * @returns Promise resolving to an array of StaticFile objects.\n * @throws {ShipClientError} If called outside Node.js or if fs/path modules fail.\n */\nexport async function processFilesForNode(\n paths: string[],\n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n if (getENV() !== 'node') {\n throw ShipError.business('processFilesForNode can only be called in a Node.js environment.');\n }\n\n // Handle multiple paths\n if (paths.length > 1) {\n const allResults: StaticFile[] = [];\n for (const singlePath of paths) {\n const results = await getFilesFromPath(singlePath, options);\n allResults.push(...results);\n }\n return allResults;\n }\n\n // Single path - use the getFilesFromPath function\n return await getFilesFromPath(paths[0], options);\n}\n","/**\n * @file Path helper utilities that work in both browser and Node.js environments.\n * Provides environment-agnostic path manipulation functions.\n */\n\n/**\n * Finds the common parent directory from an array of directory paths.\n * Simple, unified implementation for flattenDirs functionality.\n * \n * @param dirPaths - Array of directory paths (not file paths - directories containing the files)\n * @returns The common parent directory path, or empty string if none found\n */\nexport function findCommonParent(dirPaths: string[]): string {\n if (!dirPaths || dirPaths.length === 0) return '';\n \n // Normalize all paths to use forward slashes\n const normalizedPaths = dirPaths\n .filter(p => p && typeof p === 'string')\n .map(p => p.replace(/\\\\/g, '/'));\n \n if (normalizedPaths.length === 0) return '';\n if (normalizedPaths.length === 1) return normalizedPaths[0];\n \n // Split into segments and find common prefix\n const pathSegments = normalizedPaths.map(p => p.split('/').filter(Boolean));\n const commonSegments = [];\n const minLength = Math.min(...pathSegments.map(p => p.length));\n \n for (let i = 0; i < minLength; i++) {\n const segment = pathSegments[0][i];\n if (pathSegments.every(segments => segments[i] === segment)) {\n commonSegments.push(segment);\n } else {\n break;\n }\n }\n \n return commonSegments.join('/');\n}\n\n\n\n/**\n * Converts backslashes to forward slashes for cross-platform compatibility.\n * Does not remove leading slashes (preserves absolute paths).\n * @param path - The path to normalize\n * @returns Path with forward slashes\n */\nexport function normalizeSlashes(path: string): string {\n return path.replace(/\\\\/g, '/');\n}\n\n/**\n * Normalizes a path for web usage by converting backslashes to forward slashes\n * and removing leading slashes.\n * @param path - The path to normalize\n * @returns Normalized path suitable for web deployment\n */\nexport function normalizeWebPath(path: string): string {\n return path.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n}\n\n","/**\n * @file Browser-specific file utilities for the Ship SDK.\n * Provides helpers for processing browser files into deploy-ready objects and extracting common directory info.\n */\nimport { getENV } from './env.js';\nimport { StaticFile, DeploymentOptions } from '../types.js';\nimport { calculateMD5 } from './md5.js';\nimport { ShipError } from '@shipstatic/types';\nimport { findCommonParent, normalizeWebPath } from './path.js';\nimport { filterJunk } from './junk.js';\n\n\n/**\n * Internal structure representing a browser file to be processed for deploy.\n * @internal\n */\ninterface BrowserFileProcessItem {\n file: File;\n relativePath: string;\n}\n\n/**\n * Processes browser files (FileList or File[]) into an array of StaticFile objects ready for deploy.\n * Calculates MD5, filters junk files, and applies automatic path optimization.\n *\n * @param browserFiles - FileList or File[] to process for deploy.\n * @param options - Processing options including pathDetect for automatic path optimization.\n * @returns Promise resolving to an array of StaticFile objects.\n * @throws {ShipClientError} If called outside a browser or with invalid input.\n */\nexport async function processFilesForBrowser(\n browserFiles: FileList | File[],\n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n if (getENV() !== 'browser') {\n throw ShipError.business('processFilesForBrowser can only be called in a browser environment.');\n }\n\n const filesArray = Array.isArray(browserFiles) ? browserFiles : Array.from(browserFiles);\n \n // Determine common parent for path optimization - enabled by default\n let commonParent = '';\n if (options.pathDetect !== false) {\n // Path detection enabled: optimize by finding common parent\n const fileDirs = filesArray\n .map(file => (file as any).webkitRelativePath || file.name)\n .filter(path => path)\n .map(filePath => filePath.includes('/') ? filePath.substring(0, filePath.lastIndexOf('/')) : '');\n \n commonParent = findCommonParent(fileDirs);\n }\n\n // Prepare file information with automatic path optimization\n const initialFileInfos: BrowserFileProcessItem[] = [];\n for (const file of filesArray) {\n let relativePath = (file as any).webkitRelativePath || file.name;\n \n // Apply path optimization if enabled\n if (commonParent && options.pathDetect !== false) {\n relativePath = normalizeWebPath(relativePath);\n const basePathWithSlash = commonParent.endsWith('/') ? commonParent : `${commonParent}/`;\n if (relativePath.startsWith(basePathWithSlash)) {\n relativePath = relativePath.substring(basePathWithSlash.length);\n } else if (relativePath === commonParent) {\n relativePath = '';\n }\n }\n \n // Always normalize to web paths (forward slashes, no leading slash)\n relativePath = normalizeWebPath(relativePath);\n \n // Security validation: Ensure no dangerous characters in paths\n if (relativePath.includes('..') || relativePath.includes('\\0')) {\n throw ShipError.business(`Security error: Unsafe file path \"${relativePath}\" for file: ${file.name}`);\n }\n \n // Ensure path is not empty\n if (!relativePath) {\n relativePath = file.name;\n }\n \n initialFileInfos.push({ file, relativePath });\n }\n\n // Filter out junk files\n const allRelativePaths = initialFileInfos.map(info => info.relativePath);\n const nonJunkRelativePathsArray = filterJunk(allRelativePaths);\n const nonJunkRelativePathsSet = new Set(nonJunkRelativePathsArray);\n\n // Create StaticFile objects for each valid file\n const result: StaticFile[] = [];\n for (const fileInfo of initialFileInfos) {\n // Skip junk files and empty files\n if (!nonJunkRelativePathsSet.has(fileInfo.relativePath) || fileInfo.file.size === 0) {\n continue;\n }\n \n // Calculate MD5 hash\n const { md5 } = await calculateMD5(fileInfo.file);\n \n // Create and add the StaticFile\n result.push({\n content: fileInfo.file,\n path: fileInfo.relativePath,\n size: fileInfo.file.size,\n md5,\n });\n }\n \n return result;\n}\n\n","/**\n * @file All Ship SDK resources in one place - impossibly simple.\n */\nimport type { Deployment, DeploymentListResponse, Alias, AliasListResponse, Account } from '@shipstatic/types';\nimport type { ApiHttp } from './api/http.js';\nimport type { StaticFile, ShipClientOptions } from './types.js';\nimport type { DeploymentOptions, DeployInput } from './types.js';\nimport { convertDeployInput } from './lib/prepare-input.js';\nimport { mergeDeployOptions } from './core/config.js';\n\n// Re-export DeployInput for external use\nexport type { DeployInput };\n\n// =============================================================================\n// DEPLOYMENT RESOURCE\n// =============================================================================\n\nexport interface DeploymentResource {\n create: (input: DeployInput, options?: DeploymentOptions) => Promise<Deployment>;\n list: () => Promise<DeploymentListResponse>;\n remove: (id: string) => Promise<void>;\n get: (id: string) => Promise<Deployment>;\n}\n\nexport function createDeploymentResource(\n getApi: () => ApiHttp, \n clientDefaults?: ShipClientOptions,\n ensureInit?: () => Promise<void>\n): DeploymentResource {\n return {\n create: async (input: DeployInput, options: DeploymentOptions = {}) => {\n // Ensure full initialization before proceeding\n if (ensureInit) await ensureInit();\n \n // Merge user options with client defaults\n const mergedOptions = clientDefaults \n ? mergeDeployOptions(options, clientDefaults)\n : options;\n \n // Convert input to StaticFile[] with automatic SPA detection\n const staticFiles: StaticFile[] = await convertDeployInput(input, mergedOptions, getApi());\n \n // Deploy using the API - now returns the full Deployment object directly\n return await getApi().deploy(staticFiles, mergedOptions);\n },\n\n list: async () => {\n if (ensureInit) await ensureInit();\n return getApi().listDeployments();\n },\n\n remove: async (id: string) => {\n if (ensureInit) await ensureInit();\n await getApi().removeDeployment(id);\n // Return void for deletion operations\n },\n\n get: async (id: string) => {\n if (ensureInit) await ensureInit();\n return getApi().getDeployment(id);\n }\n };\n}\n\n// =============================================================================\n// ALIAS RESOURCE\n// =============================================================================\n\nexport interface AliasResource {\n set: (aliasName: string, deployment: string) => Promise<Alias>;\n get: (aliasName: string) => Promise<Alias>;\n list: () => Promise<AliasListResponse>;\n remove: (aliasName: string) => Promise<void>;\n}\n\nexport function createAliasResource(getApi: () => ApiHttp, ensureInit?: () => Promise<void>): AliasResource {\n return {\n set: async (aliasName: string, deployment: string) => {\n if (ensureInit) await ensureInit();\n // Set alias and return the created/updated alias directly\n return getApi().setAlias(aliasName, deployment);\n },\n\n get: async (aliasName: string) => {\n if (ensureInit) await ensureInit();\n return getApi().getAlias(aliasName);\n },\n\n list: async () => {\n if (ensureInit) await ensureInit();\n return getApi().listAliases();\n },\n\n remove: async (aliasName: string) => {\n if (ensureInit) await ensureInit();\n await getApi().removeAlias(aliasName);\n // Return void for deletion operations\n }\n };\n}\n\n// =============================================================================\n// ACCOUNT RESOURCE\n// =============================================================================\n\nexport interface AccountResource {\n get: () => Promise<Account>;\n}\n\nexport function createAccountResource(getApi: () => ApiHttp, ensureInit?: () => Promise<void>): AccountResource {\n return {\n get: async () => {\n if (ensureInit) await ensureInit();\n return getApi().getAccount();\n }\n };\n}\n\n// =============================================================================\n// KEYS RESOURCE\n// =============================================================================\n\nexport interface KeysResource {\n create: () => Promise<{ apiKey: string }>;\n}\n\nexport function createKeysResource(getApi: () => ApiHttp, ensureInit?: () => Promise<void>): KeysResource {\n return {\n create: async () => {\n if (ensureInit) await ensureInit();\n return getApi().createApiKey();\n }\n };\n}"],"mappings":"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","names":["_testEnvironment","__setTestEnvironment","env","detectEnvironment","getENV","z","types_exports","__reExport","types_star","DEFAULT_API","MODULE_NAME","ConfigSchema","z","validateConfig","config","error","firstError","path","loadConfigFromFile","configFile","getENV","cosmiconfigSync","os","explorer","result","loadConfig","envConfig","fileConfig","mergedConfig","resolveConfig","userOptions","loadedConfig","finalConfig","DEFAULT_API","mergeDeployOptions","clientDefaults","_mime","ShipError","DEPLOY_ENDPOINT","PING_ENDPOINT","ALIASES_ENDPOINT","CONFIG_ENDPOINT","ACCOUNT_ENDPOINT","SPA_CHECK_ENDPOINT","getBrowserContentType","file","collectAsyncIterable","iterable","result","x","ApiHttp","options","DEFAULT_API","#getAuthHeaders","customHeaders","headers","#fetchWithAuth","url","operationName","fetchOptions","getENV","error","#handleFetchError","#request","response","#handleResponseError","ShipError","files","#validateFiles","apiUrl","signal","requestBody","requestHeaders","#prepareRequestPayload","#createBrowserBody","body","#createNodeBody","formData","checksums","i","fileContent","contentType","fileWithPath","FormDataNodeClass","FileNodeClass","FormDataEncoderClass","pathImport","formDataNodeInstance","fileNodeInstance","preservedPath","encoder","encodedChunks","chunk","errorData","message","id","name","deployment","filePaths","ShipError","ShipError","_config","setConfig","config","getCurrentConfig","ShipError","DEPLOYMENT_CONFIG_FILENAME","ShipError","calculateMD5Browser","blob","SparkMD5","resolve","reject","chunks","currentChunk","spark","fileReader","loadNext","start","end","e","result","calculateMD5Node","input","crypto","hash","fs","stream","err","chunk","calculateMD5","env","getENV","isJunk","JUNK_DIRECTORIES","filterJunk","filePaths","filePath","parts","basename","directorySegments","segment","junkDir","ShipError","findCommonParent","dirPaths","normalizedPaths","p","pathSegments","commonSegments","minLength","i","segment","segments","normalizeWebPath","path","fs","path","findAllFilePaths","dirPath","results","entries","entry","fullPath","stats","subFiles","error","getFilesFromPath","sourcePath","options","absolutePath","validPaths","p","basename","filterJunk","commonParent","fileDirs","filePath","findCommonParent","totalSize","platformLimits","getCurrentConfig","ShipError","content","md5","calculateMD5","relativePath","processFilesForNode","paths","getENV","allResults","singlePath","ShipError","processFilesForBrowser","browserFiles","options","getENV","ShipError","filesArray","commonParent","fileDirs","file","path","filePath","findCommonParent","initialFileInfos","relativePath","normalizeWebPath","basePathWithSlash","allRelativePaths","info","nonJunkRelativePathsArray","filterJunk","nonJunkRelativePathsSet","result","fileInfo","md5","calculateMD5","validateFiles","files","options","config","getCurrentConfig","ShipError","totalSize","file","validateInputEarly","input","environment","item","postProcessFiles","f","convertNodeInput","staticFiles","processFilesForNode","convertBrowserInput","fileArray","processFilesForBrowser","convertDeployInput","apiClient","getENV","detectAndConfigureSPA","createSPAConfig","content","md5","calculateMD5","DEPLOYMENT_CONFIG_FILENAME","filePaths","spaConfig","createDeploymentResource","getApi","clientDefaults","ensureInit","input","options","mergedOptions","mergeDeployOptions","staticFiles","convertDeployInput","id","createAliasResource","aliasName","deployment","createAccountResource","createKeysResource","ShipError","ShipErrorType","Ship","options","getENV","config","resolveConfig","ApiHttp","initCallback","getApi","createDeploymentResource","createAliasResource","createAccountResource","createKeysResource","loadedConfig","loadConfig","finalConfig","platformConfig","setConfig","error","input","src_default"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/env.ts","../src/core/config.ts","../src/types.ts","../src/core/constants.ts","../src/api/http.ts","../src/index.ts","../src/core/platform-config.ts","../src/lib/prepare-input.ts","../src/lib/md5.ts","../src/lib/junk.ts","../src/lib/node-files.ts","../src/lib/path.ts","../src/lib/browser-files.ts","../src/resources.ts"],"sourcesContent":["/**\n * @file Environment detection utilities for the Ship SDK.\n * Helps in determining whether the SDK is running in a Node.js, browser, or unknown environment.\n */\n\n/**\n * Represents the detected or simulated JavaScript execution environment.\n */\nexport type ExecutionEnvironment = 'browser' | 'node' | 'unknown';\n\n/** @internal Environment override for testing. */\nlet _testEnvironment: ExecutionEnvironment | null = null;\n\n/**\n * **FOR TESTING PURPOSES ONLY.**\n *\n * Allows tests to override the detected environment, forcing the SDK to behave\n * as if it's running in the specified environment.\n *\n * @param env - The environment to simulate ('node', 'browser', 'unknown'),\n * or `null` to clear the override and revert to actual environment detection.\n * @internal\n */\nexport function __setTestEnvironment(env: ExecutionEnvironment | null): void {\n _testEnvironment = env;\n}\n\n/**\n * Detects the actual JavaScript execution environment (Node.js, browser, or unknown)\n * by checking for characteristic global objects.\n * @returns The detected environment as {@link ExecutionEnvironment}.\n * @internal\n */\nfunction detectEnvironment(): ExecutionEnvironment {\n // Check for Node.js environment\n if (typeof process !== 'undefined' && process.versions && process.versions.node) {\n return 'node';\n }\n\n // Check for Browser environment (including Web Workers)\n if (typeof window !== 'undefined' || typeof self !== 'undefined') {\n return 'browser';\n }\n\n return 'unknown';\n}\n\n/**\n * Gets the current effective execution environment.\n *\n * This function first checks if a test environment override is active via {@link __setTestEnvironment}.\n * If not, it detects the actual environment (Node.js, browser, or unknown).\n *\n * @returns The current execution environment: 'browser', 'node', or 'unknown'.\n * @public\n */\nexport function getENV(): ExecutionEnvironment {\n // Return test override if set\n if (_testEnvironment) {\n return _testEnvironment;\n }\n \n // Detect actual environment\n return detectEnvironment();\n}\n","/**\n * @file Manages loading and validation of client configuration.\n * This module uses `cosmiconfig` to find and load configuration from various\n * file sources (e.g., `.shiprc`, `package.json`) and environment variables.\n * Configuration values are validated using Zod schemas.\n */\n\nimport { z } from 'zod';\nimport { ShipClientOptions, DeploymentOptions, ShipError } from '../types.js';\nimport { getENV } from '../lib/env.js';\nimport { DEFAULT_API } from './constants.js';\n\n\n\n/** @internal Name of the module, used by cosmiconfig for config file searching. */\nconst MODULE_NAME = 'ship';\n\n/**\n * Zod schema for validating ship configuration.\n * @internal\n */\nconst ConfigSchema = z.object({\n apiUrl: z.string().url().optional(),\n apiKey: z.string().optional()\n}).strict();\n\n/**\n * Validates configuration using Zod schema.\n * @param config - Configuration object to validate\n * @returns Validated configuration or throws error\n * @internal\n */\nfunction validateConfig(config: any): Partial<ShipClientOptions> {\n try {\n return ConfigSchema.parse(config);\n } catch (error) {\n if (error instanceof z.ZodError) {\n const firstError = error.issues[0];\n const path = firstError.path.length > 0 ? ` at ${firstError.path.join('.')}` : '';\n throw ShipError.config(`Configuration validation failed${path}: ${firstError.message}`);\n }\n throw ShipError.config('Configuration validation failed');\n }\n}\n\n/**\n * Loads client configuration from files.\n * Searches for .shiprc and package.json with ship key.\n * First searches from the current directory, then from the home directory.\n * @param configFile - Optional specific config file path to load\n * @returns Configuration object or empty if not found/invalid\n * @internal\n */\nasync function loadConfigFromFile(configFile?: string): Promise<Partial<ShipClientOptions>> {\n try {\n // Only use cosmiconfig in Node.js environments\n if (getENV() !== 'node') {\n return {};\n }\n \n // Dynamically import cosmiconfig and os only in Node.js environments\n const { cosmiconfigSync } = await import('cosmiconfig');\n const os = await import('os');\n \n const explorer = cosmiconfigSync(MODULE_NAME, {\n searchPlaces: [\n `.${MODULE_NAME}rc`,\n 'package.json',\n `${os.homedir()}/.${MODULE_NAME}rc`, // Always include home directory as fallback\n ],\n stopDir: os.homedir(), // Stop searching at home directory\n });\n \n let result;\n \n // If a specific config file is provided, load it directly\n if (configFile) {\n result = explorer.load(configFile);\n } else {\n // cosmiconfig automatically searches up the directory tree\n // from current directory to stopDir (home directory)\n result = explorer.search();\n }\n \n if (result && result.config) {\n return validateConfig(result.config);\n }\n } catch (error) {\n if (error instanceof ShipError) throw error; // Re-throw all ShipError instances\n // Silently fail for file loading issues - this is optional config\n }\n return {};\n}\n\n/**\n * Simplified configuration loading prioritizing environment variables.\n * Only loads file config if environment variables are not set.\n * Only available in Node.js environments.\n *\n * @param configFile - Optional specific config file path to load\n * @returns Configuration object with loaded values\n * @throws {ShipInvalidConfigError} If the configuration is invalid.\n */\nexport async function loadConfig(configFile?: string): Promise<Partial<ShipClientOptions>> {\n if (getENV() !== 'node') return {};\n\n // Start with environment variables (highest priority)\n const envConfig = {\n apiUrl: process.env.SHIP_API_URL,\n apiKey: process.env.SHIP_API_KEY,\n };\n\n // Always try to load file config for fallback values\n const fileConfig = await loadConfigFromFile(configFile);\n\n // Merge with environment variables taking precedence\n const mergedConfig = {\n apiUrl: envConfig.apiUrl ?? fileConfig.apiUrl,\n apiKey: envConfig.apiKey ?? fileConfig.apiKey,\n };\n\n // Validate final config\n return validateConfig(mergedConfig);\n}\n\n/**\n * Simplified configuration resolution with clear precedence.\n * Precedence: user options > environment variables > config files > defaults.\n * \n * @param userOptions - Options provided directly by the user\n * @param loadedConfig - Configuration loaded from environment/files\n * @returns Resolved configuration with api and apiKey\n */\nexport function resolveConfig(\n userOptions: ShipClientOptions = {}, \n loadedConfig: Partial<ShipClientOptions> = {}\n): { apiUrl: string; apiKey?: string } {\n // Build final config with clear precedence\n // Only use userOptions.apiKey if it's explicitly provided (not undefined)\n const finalConfig = {\n apiUrl: userOptions.apiUrl || loadedConfig.apiUrl || DEFAULT_API,\n apiKey: userOptions.apiKey || loadedConfig.apiKey,\n };\n\n // Return with optional apiKey\n return finalConfig.apiKey !== undefined \n ? { apiUrl: finalConfig.apiUrl, apiKey: finalConfig.apiKey }\n : { apiUrl: finalConfig.apiUrl };\n}\n\n\n// =============================================================================\n// CONFIGURATION MERGING\n// =============================================================================\n\n/**\n * Merge deployment options with client defaults.\n * Uses a declarative approach with object spreading for simplicity.\n */\nexport function mergeDeployOptions(\n userOptions: DeploymentOptions = {},\n clientDefaults: ShipClientOptions\n): DeploymentOptions {\n return {\n // Start with client defaults\n onProgress: clientDefaults.onProgress,\n onProgressStats: clientDefaults.onProgressStats,\n maxConcurrency: clientDefaults.maxConcurrentDeploys,\n timeout: clientDefaults.timeout,\n apiKey: clientDefaults.apiKey,\n apiUrl: clientDefaults.apiUrl,\n // Overwrite with any user-provided options\n ...userOptions,\n };\n}\n","/**\n * @file SDK-specific type definitions\n * Consolidates all Ship SDK types into a single file for clarity.\n * Core types come from @shipstatic/types, while SDK-specific types are defined here.\n */\n\n// Re-export all types from @shipstatic/types for convenience\nexport * from '@shipstatic/types';\n\n// =============================================================================\n// ENVIRONMENT-SPECIFIC TYPES\n// =============================================================================\n\n/**\n * Consolidated input type for all environments\n */\nexport type DeployInput = FileList | File[] | HTMLInputElement | string[];\n\n// =============================================================================\n// DEPLOYMENT OPTIONS\n// =============================================================================\n\n/**\n * Universal deploy options for both Node.js and Browser environments\n */\nexport interface DeploymentOptions {\n /** The API URL to use for this specific deploy. Overrides client's default. */\n apiUrl?: string;\n /** An AbortSignal to allow cancellation of the deploy operation. */\n signal?: AbortSignal;\n /** An optional subdomain to suggest for the deployment. Availability is subject to the API. */\n subdomain?: string;\n /** Callback invoked if the deploy is cancelled via the AbortSignal. */\n onCancel?: () => void;\n /** Maximum number of concurrent operations. */\n maxConcurrency?: number;\n /** Timeout in milliseconds for the deploy request. */\n timeout?: number;\n /** The API key to use for this specific deploy. Overrides client's default. */\n apiKey?: string;\n /** Whether to auto-detect and optimize file paths by flattening common directories. Defaults to true. */\n pathDetect?: boolean;\n /** Whether to auto-detect SPAs and generate ship.json configuration. Defaults to true. */\n spaDetect?: boolean;\n /** Callback for overall deploy progress (0-100). */\n onProgress?: (progress: number) => void;\n /** Callback for detailed progress statistics. */\n onProgressStats?: (progressStats: ProgressStats) => void;\n}\n\n/**\n * Options for configuring an deploy operation via `apiClient.deployFiles`.\n * Derived from DeploymentOptions but excludes client-side only options.\n */\nexport type ApiDeployOptions = Omit<DeploymentOptions, 'pathDetect'>;\n\n// =============================================================================\n// PROGRESS TRACKING\n// =============================================================================\n\n/**\n * Detailed statistics about the progress of an deploy operation.\n */\nexport interface ProgressStats {\n /** The number of bytes loaded so far. */\n loaded: number;\n /** The total number of bytes to be loaded. May be 0 if unknown initially. */\n total: number;\n /** The progress as a fraction (loaded/total). Value is between 0 and 1. */\n progress: number;\n /** Optional identifier for the file this progress pertains to, if applicable. */\n file?: string;\n}\n\n// =============================================================================\n// CLIENT CONFIGURATION\n// =============================================================================\n\n/**\n * Options for configuring a `Ship` instance.\n * Sets default API host, key, progress callbacks, concurrency, and timeouts for the client.\n */\nexport interface ShipClientOptions {\n /** Default API URL for the client instance. */\n apiUrl?: string | undefined;\n /** Default API key for the client instance. */\n apiKey?: string | undefined;\n /** Path to custom config file. */\n configFile?: string | undefined;\n /**\n * Default callback for overall deploy progress for deploys made with this client.\n * @param progress - A number between 0 and 100.\n */\n onProgress?: ((progress: number) => void) | undefined;\n /**\n * Default callback for detailed progress statistics for deploys made with this client.\n * @param progressStats - Progress statistics object.\n */\n onProgressStats?: ((progressStats: ProgressStats) => void) | undefined;\n /**\n * Default for maximum concurrent deploys.\n * Used if an deploy operation doesn't specify its own `maxConcurrency`.\n * Defaults to 4 if not set here or in the specific deploy call.\n */\n maxConcurrentDeploys?: number | undefined;\n /**\n * Default timeout in milliseconds for API requests made by this client instance.\n * Used if an deploy operation doesn't specify its own timeout.\n */\n timeout?: number | undefined;\n}\n\n// =============================================================================\n// FILE REPRESENTATION\n// =============================================================================\n\n/**\n * Represents a file that has been processed and is ready for deploy.\n * Used internally by the SDK and in advanced/manual deploy scenarios.\n */\nexport interface StaticFile {\n /**\n * The content of the file.\n * In Node.js, this is typically a `Buffer`.\n * In the browser, this is typically a `File` or `Blob` object.\n */\n content: File | Buffer | Blob;\n /**\n * The desired path for the file on the server, relative to the deployment root.\n * Should include the filename, e.g., `images/photo.jpg`.\n */\n path: string;\n /**\n * The original absolute file system path (primarily used in Node.js environments).\n * This helps in debugging or associating the server path back to its source.\n */\n filePath?: string;\n /**\n * The MD5 hash (checksum) of the file's content.\n * This is calculated by the SDK before deploy if not provided.\n */\n md5?: string;\n /** The size of the file in bytes. */\n size: number;\n}\n\n// =============================================================================\n// API RESPONSES\n// =============================================================================\n\n// PingResponse is imported from @shipstatic/types (single source of truth)","/**\n * @file Shared, environment-agnostic constants.\n */\n\n/** Default API URL if not otherwise configured. */\nexport const DEFAULT_API = 'https://api.shipstatic.com';","/**\n * @file Manages HTTP requests to the Ship API using native fetch.\n */\nimport * as _mime from 'mime-types';\nimport type { Deployment, DeploymentListResponse, PingResponse, ConfigResponse, DeploymentRemoveResponse, Alias, AliasListResponse, Account } from '@shipstatic/types';\nimport { StaticFile, ApiDeployOptions, ShipClientOptions } from '../types.js';\nimport { ShipError } from '@shipstatic/types';\nimport { getENV } from '../lib/env.js';\nimport { DEFAULT_API } from '../core/constants.js';\n\n// FormData and File types for Node.js environment\n/**\n * Internal type alias for Node.js FormData implementation used during file deploys.\n * @internal\n */\ntype FormDataNode = any;\n/**\n * Internal type alias for Node.js File implementation used during file deploys.\n * @internal\n */\ntype FileNode = any;\n\n/** Default API host URL if not otherwise configured. */\n/** @internal */\nconst DEPLOY_ENDPOINT = '/deployments';\n/** @internal */\nconst PING_ENDPOINT = '/ping';\n/** @internal */\nconst ALIASES_ENDPOINT = '/aliases';\n/** @internal */\nconst CONFIG_ENDPOINT = '/config';\n/** @internal */\nconst ACCOUNT_ENDPOINT = '/account';\n/** @internal */\nconst SPA_CHECK_ENDPOINT = '/spa-check';\n\n/**\n * Determines the MIME type for a given file (File object or path string) in browser environments.\n * Falls back to 'application/octet-stream' if type cannot be determined.\n * @internal\n * @param file - File object or file path string.\n * @returns The MIME type as a string.\n */\nfunction getBrowserContentType(file: File | string): string {\n if (typeof file === 'string') {\n return _mime.lookup(file) || 'application/octet-stream';\n } else {\n return _mime.lookup(file.name) || file.type || 'application/octet-stream';\n }\n}\n\n/**\n * Collects all items from an AsyncIterable into an array.\n * Useful for converting streaming multipart encoders to Buffer arrays.\n * @internal\n * @template T - The item type yielded by the iterable.\n * @param iterable - The async iterable to collect.\n * @returns A promise resolving to an array of all items.\n */\nasync function collectAsyncIterable<T>(iterable: AsyncIterable<T>): Promise<T[]> {\n const result: T[] = [];\n for await (const x of iterable) {\n result.push(x);\n }\n return result;\n}\n\n/**\n * Handles direct HTTP communication with the Ship API, including deploys and health checks.\n * Responsible for constructing requests, managing authentication, and error translation.\n * @internal\n */\nexport class ApiHttp {\n private readonly apiUrl: string;\n private readonly apiKey: string;\n\n /**\n * Constructs a new ApiHttp instance with the provided client options.\n * @param options - Client options including API host, apiKey, and timeout settings.\n */\n constructor(options: ShipClientOptions) {\n this.apiUrl = options.apiUrl || DEFAULT_API;\n this.apiKey = options.apiKey ?? \"\";\n }\n\n /**\n * Generates common headers for API requests, including the API key if present.\n * @param customHeaders - Optional additional headers to include.\n * @returns Object containing merged headers.\n * @private\n */\n #getAuthHeaders(customHeaders: Record<string, string> = {}): Record<string, string> {\n const headers = { ...customHeaders };\n if (this.apiKey) {\n headers['Authorization'] = `Bearer ${this.apiKey}`;\n }\n return headers;\n }\n\n /**\n * Low-level fetch wrapper with authentication - used only by #request method.\n * This method only handles the raw fetch request with auth headers.\n * @param url - Request URL\n * @param options - Fetch options\n * @param operationName - Name of operation for error messages\n * @returns Promise resolving to Response object\n * @private\n */\n async #fetchWithAuth(url: string, options: RequestInit = {}, operationName: string): Promise<Response> {\n const headers = this.#getAuthHeaders(options.headers as Record<string, string>);\n \n const fetchOptions: RequestInit = {\n ...options,\n headers,\n credentials: getENV() === 'browser' ? 'include' : 'same-origin',\n };\n\n try {\n const response = await fetch(url, fetchOptions);\n return response;\n } catch (error: any) {\n this.#handleFetchError(error, operationName);\n // This line is unreachable because #handleFetchError always throws\n throw error;\n }\n }\n\n /**\n * Unified HTTP request helper that handles the complete request lifecycle.\n * Makes authenticated requests, checks response status, and handles all errors.\n * Automatically determines whether to parse JSON based on response headers.\n * @param url - Request URL\n * @param options - Fetch options \n * @param operationName - Name of operation for error messages\n * @returns Promise resolving to parsed JSON response or undefined for empty responses\n * @throws {ShipError} Various ShipError types based on the failure mode\n * @private\n */\n async #request<T>(url: string, options: RequestInit = {}, operationName: string): Promise<T> {\n try {\n const response = await this.#fetchWithAuth(url, options, operationName);\n \n if (!response.ok) {\n await this.#handleResponseError(response, operationName);\n }\n \n // Check if response has content to parse\n const contentLength = response.headers.get('Content-Length');\n if (contentLength === '0' || response.status === 204) {\n return undefined as T; // Return undefined for empty responses\n }\n \n return await response.json() as T;\n } catch (error: any) {\n if (error instanceof ShipError) {\n throw error;\n }\n this.#handleFetchError(error, operationName);\n // This line is unreachable because #handleFetchError always throws\n throw error;\n }\n }\n \n /**\n * Sends a ping request to the Ship API server to verify connectivity and authentication.\n * @returns Promise resolving to `true` if the ping is successful, `false` otherwise.\n * @throws {ShipApiError} If the API returns an error response (4xx, 5xx).\n * @throws {ShipNetworkError} If a network error occurs (e.g., DNS failure, connection refused).\n */\n public async ping(): Promise<boolean> {\n const data = await this.#request<PingResponse>(`${this.apiUrl}${PING_ENDPOINT}`, { method: 'GET' }, 'Ping');\n return data?.success || false;\n }\n\n /**\n * Get full ping response from the API server\n * @returns Promise resolving to the full PingResponse object.\n */\n public async getPingResponse(): Promise<PingResponse> {\n return await this.#request<PingResponse>(`${this.apiUrl}${PING_ENDPOINT}`, { method: 'GET' }, 'Ping');\n }\n\n /**\n * Fetches platform configuration from the API.\n * @returns Promise resolving to the config response.\n * @throws {ShipError} If the config request fails.\n */\n public async getConfig(): Promise<ConfigResponse> {\n return await this.#request<ConfigResponse>(`${this.apiUrl}${CONFIG_ENDPOINT}`, { method: 'GET' }, 'Config');\n }\n\n /**\n * Deploys an array of StaticFile objects to the Ship API.\n * Constructs and sends a multipart/form-data POST request, handling both browser and Node.js environments.\n * Validates files and manages deploy progress and error translation.\n * @param files - Array of StaticFile objects to deploy (must include MD5 checksums).\n * @param options - Optional per-deploy configuration (overrides instance defaults).\n * @returns Promise resolving to a full Deployment object on success.\n * @throws {ShipFileError} If a file is missing an MD5 checksum or content type is unsupported.\n * @throws {ShipClientError} If no files are provided or if environment is unknown.\n * @throws {ShipNetworkError} If a network error occurs during deploy.\n * @throws {ShipApiError} If the API returns an error response.\n * @throws {ShipCancelledError} If the deploy is cancelled via an AbortSignal.\n */\n public async deploy(\n files: StaticFile[],\n options: ApiDeployOptions = {}\n ): Promise<Deployment> {\n this.#validateFiles(files);\n\n const {\n apiUrl = this.apiUrl,\n signal\n } = options;\n\n const { requestBody, requestHeaders } = await this.#prepareRequestPayload(files);\n \n const fetchOptions: RequestInit = {\n method: 'POST',\n body: requestBody,\n headers: requestHeaders,\n signal: signal || null\n };\n\n // Use unified request method to eliminate duplication\n return await this.#request<Deployment>(`${apiUrl}${DEPLOY_ENDPOINT}`, fetchOptions, 'Deploy');\n }\n \n /**\n * Validates the files array for deploy requirements.\n * Ensures all files have MD5 checksums and at least one file is present.\n * @param files - Files to validate.\n * @throws {ShipFileError} If a file is missing an MD5 checksum.\n * @throws {ShipClientError} If no files are provided.\n * @private\n */\n #validateFiles(files: StaticFile[]): void {\n if (!files.length) {\n throw ShipError.business('No files to deploy.');\n }\n \n for (const file of files) {\n if (!file.md5) {\n throw ShipError.file(`MD5 checksum missing for file: ${file.path}`, file.path);\n }\n }\n }\n\n\n /**\n * Prepares the request payload (body and headers) for deploy based on execution environment.\n * Selects browser or Node.js multipart construction as needed.\n * @param files - Files to deploy.\n * @returns Promise resolving to request body and headers.\n * @private\n */\n async #prepareRequestPayload(files: StaticFile[]): Promise<{\n requestBody: FormData | Buffer;\n requestHeaders: Record<string, string>;\n }> {\n let requestBody: FormData | Buffer;\n let requestHeaders: Record<string, string> = {};\n \n if (getENV() === 'browser') {\n requestBody = this.#createBrowserBody(files);\n } else if (getENV() === 'node') {\n const { body, headers } = await this.#createNodeBody(files);\n requestBody = body;\n requestHeaders = headers;\n } else {\n throw ShipError.business('Unknown or unsupported execution environment');\n }\n \n return { requestBody, requestHeaders };\n }\n\n /**\n * Creates a FormData object for browser environments, populating it with files and checksums.\n * @param files - Array of StaticFile objects to include in the FormData.\n * @returns FormData object ready for transmission.\n * @throws {ShipFileError} If file content is of an unsupported type for browser FormData.\n * @private\n */\n #createBrowserBody(files: StaticFile[]): FormData {\n const formData = new FormData();\n const checksums: string[] = [];\n \n for (let i = 0; i < files.length; i++) {\n const file = files[i];\n let fileContent: File | Blob;\n if (file.content instanceof File || file.content instanceof Blob) {\n fileContent = file.content;\n } else {\n throw ShipError.file(`Unsupported file.content type for browser FormData: ${file.path}`, file.path);\n }\n const contentType = getBrowserContentType(fileContent instanceof File ? fileContent : file.path);\n const fileWithPath = new File([fileContent], file.path, { type: contentType });\n formData.append('files[]', fileWithPath);\n checksums.push(file.md5!);\n }\n \n // Add checksums as JSON array\n formData.append('checksums', JSON.stringify(checksums));\n return formData;\n }\n\n /**\n * Creates the multipart request body (Buffer) and headers for Node.js environments using formdata-node and form-data-encoder.\n * @param files - Array of StaticFile objects to include in the multipart body.\n * @returns Promise resolving to an object with the body Buffer and headers.\n * @throws {ShipFileError} If file content is of an unsupported type for Node.js FormData.\n * @private\n */\n async #createNodeBody(files: StaticFile[]): Promise<{body: Buffer, headers: Record<string, string>}> {\n const { FormData: FormDataNodeClass, File: FileNodeClass } = await import('formdata-node');\n const { FormDataEncoder: FormDataEncoderClass } = await import('form-data-encoder');\n const pathImport = await import('path');\n const formDataNodeInstance: FormDataNode = new FormDataNodeClass();\n const checksums: string[] = [];\n\n for (let i = 0; i < files.length; i++) {\n const file = files[i];\n const contentType = _mime.lookup(file.path) || 'application/octet-stream';\n let fileNodeInstance: FileNode;\n\n if (Buffer.isBuffer(file.content)) {\n fileNodeInstance = new FileNodeClass([file.content], file.path, { type: contentType });\n } else if (typeof Blob !== \"undefined\" && file.content instanceof Blob) {\n fileNodeInstance = new FileNodeClass([file.content], file.path, { type: contentType });\n } else {\n throw ShipError.file(`Unsupported file.content type for Node.js FormData: ${file.path}`, file.path);\n }\n const preservedPath = file.path.startsWith('/') ? file.path : '/' + file.path;\n formDataNodeInstance.append('files[]', fileNodeInstance, preservedPath);\n checksums.push(file.md5!);\n }\n\n // Add checksums as JSON array\n formDataNodeInstance.append('checksums', JSON.stringify(checksums));\n\n const encoder = new FormDataEncoderClass(formDataNodeInstance);\n const encodedChunks = await collectAsyncIterable(encoder.encode());\n const body = Buffer.concat(encodedChunks.map(chunk => Buffer.from(chunk as Uint8Array)));\n\n const headers = {\n 'Content-Type': encoder.contentType,\n 'Content-Length': Buffer.byteLength(body).toString()\n };\n return { body, headers };\n }\n\n /**\n * Handles fetch response errors and throws appropriate ShipError types.\n * @param response - Fetch Response object with error status\n * @param operationName - Name of the failed operation\n * @throws {ShipApiError} Always throws with API error details\n * @private\n */\n async #handleResponseError(response: Response, operationName: string): Promise<never> {\n let errorData: any = {};\n try {\n const contentType = response.headers.get('content-type');\n if (contentType && contentType.includes('application/json')) {\n errorData = await response.json();\n } else {\n errorData = { message: await response.text() };\n }\n } catch {\n errorData = { message: 'Failed to parse error response' };\n }\n\n // Handle structured error responses (with error field, code, or message)\n if (errorData.error || errorData.code || errorData.message) {\n // Use message if available, otherwise use the error field as the message, or fallback\n const message = errorData.message || errorData.error || `${operationName} failed due to API error`;\n \n // Handle authentication errors specifically\n if (response.status === 401) {\n throw ShipError.authentication(message);\n }\n \n throw ShipError.api(\n message,\n response.status,\n errorData.code,\n errorData\n );\n }\n\n // Fallback for completely unstructured errors\n // Handle authentication errors specifically for unstructured responses too\n if (response.status === 401) {\n throw ShipError.authentication(`Authentication failed for ${operationName}`);\n }\n \n throw ShipError.api(\n `${operationName} failed due to API error`,\n response.status,\n undefined,\n errorData\n );\n }\n\n /**\n * Translates fetch errors into appropriate ShipError types and always throws.\n * Intended for use in catch blocks of API requests; never returns.\n * @param error - The error object caught from fetch.\n * @param operationName - Name of the failed operation (e.g., 'Deploy', 'Ping').\n * @throws {ShipCancelledError} If the request was cancelled.\n * @throws {ShipNetworkError} If a network error occurred (DNS failure, connection refused).\n * @throws {ShipClientError} For other unexpected errors.\n * @private\n */\n #handleFetchError(error: any, operationName: string): never {\n if (error.name === 'AbortError') {\n throw ShipError.cancelled(`${operationName} operation was cancelled.`);\n }\n if (error instanceof TypeError && error.message.includes('fetch')) {\n throw ShipError.network(`${operationName} failed due to network error: ${error.message}`, error);\n }\n if (error instanceof ShipError) {\n throw error;\n }\n throw ShipError.business(`An unexpected error occurred during ${operationName}: ${error.message || 'Unknown error'}`);\n }\n\n /**\n * Lists all deployments for the authenticated account\n * @returns Promise resolving to deployment list response\n */\n public async listDeployments(): Promise<DeploymentListResponse> {\n return await this.#request<DeploymentListResponse>(`${this.apiUrl}${DEPLOY_ENDPOINT}`, { method: 'GET' }, 'List Deployments');\n }\n\n /**\n * Gets a specific deployment by ID\n * @param id - Deployment ID to retrieve\n * @returns Promise resolving to deployment details\n */\n public async getDeployment(id: string): Promise<Deployment> {\n return await this.#request<Deployment>(`${this.apiUrl}${DEPLOY_ENDPOINT}/${id}`, { method: 'GET' }, 'Get Deployment');\n }\n\n /**\n * Removes a deployment by ID\n * @param id - Deployment ID to remove\n * @returns Promise resolving when removal is complete\n */\n public async removeDeployment(id: string): Promise<void> {\n await this.#request<DeploymentRemoveResponse>(`${this.apiUrl}${DEPLOY_ENDPOINT}/${id}`, { method: 'DELETE' }, 'Remove Deployment');\n }\n\n /**\n * Sets an alias (create or update)\n * @param name - Alias name\n * @param deployment - Deployment name to point to\n * @returns Promise resolving to the created/updated alias with operation context\n */\n public async setAlias(name: string, deployment: string): Promise<import('@shipstatic/types').Alias> {\n try {\n const response = await this.#fetchWithAuth(`${this.apiUrl}${ALIASES_ENDPOINT}/${encodeURIComponent(name)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ deployment: deployment })\n }, 'Set Alias');\n \n if (!response.ok) {\n await this.#handleResponseError(response, 'Set Alias');\n }\n \n const alias = await response.json() as import('@shipstatic/types').Alias;\n \n // 201 = created, 200 = updated\n return {\n ...alias,\n isCreate: response.status === 201\n };\n } catch (error: any) {\n this.#handleFetchError(error, 'Set Alias');\n }\n }\n\n /**\n * Gets a specific alias by name\n * @param name - Alias name to retrieve\n * @returns Promise resolving to alias details\n */\n public async getAlias(name: string): Promise<Alias> {\n return await this.#request<Alias>(`${this.apiUrl}${ALIASES_ENDPOINT}/${encodeURIComponent(name)}`, { method: 'GET' }, 'Get Alias');\n }\n\n /**\n * Lists all aliases for the authenticated account\n * @returns Promise resolving to alias list response\n */\n public async listAliases(): Promise<AliasListResponse> {\n return await this.#request<AliasListResponse>(`${this.apiUrl}${ALIASES_ENDPOINT}`, { method: 'GET' }, 'List Aliases');\n }\n\n /**\n * Removes an alias by name\n * @param name - Alias name to remove\n * @returns Promise resolving to removal confirmation\n */\n public async removeAlias(name: string): Promise<void> {\n await this.#request<void>(`${this.apiUrl}${ALIASES_ENDPOINT}/${encodeURIComponent(name)}`, { method: 'DELETE' }, 'Remove Alias');\n }\n\n /**\n * Triggers a manual DNS check for an external alias\n * @param name - Alias name to check DNS for\n * @returns Promise resolving to confirmation message\n */\n public async checkAlias(name: string): Promise<{ message: string }> {\n return await this.#request<{ message: string }>(`${this.apiUrl}${ALIASES_ENDPOINT}/${encodeURIComponent(name)}/dns-check`, { method: 'POST' }, 'Check Alias');\n }\n\n /**\n * Gets account details for the authenticated user\n * @returns Promise resolving to account details\n */\n public async getAccount(): Promise<Account> {\n return await this.#request<Account>(`${this.apiUrl}${ACCOUNT_ENDPOINT}`, { method: 'GET' }, 'Get Account');\n }\n\n /**\n * Creates a new API key for the authenticated user\n * @returns Promise resolving to the new API key\n */\n public async createApiKey(): Promise<{ apiKey: string }> {\n return await this.#request<{ apiKey: string }>(`${this.apiUrl}/key`, { method: 'POST' }, 'Create API Key');\n }\n\n /**\n * Checks if file paths represent a SPA structure using AI analysis\n * @param filePaths - Array of file paths to analyze\n * @returns Promise resolving to boolean indicating if it's a SPA\n */\n public async checkSPA(filePaths: string[]): Promise<boolean> {\n const response = await this.#request<{ isSPA: boolean }>(\n `${this.apiUrl}${SPA_CHECK_ENDPOINT}`, \n { \n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ files: filePaths })\n }, \n 'SPA Check'\n );\n return response.isSPA;\n }\n}\n","/**\n * @file Main entry point for the Ship SDK.\n * This module provides a simplified class-based SDK interface similar to Vercel's approach.\n */\n\n// Core imports\nimport { getENV } from './lib/env.js';\nimport { loadConfig, resolveConfig } from './core/config.js';\nimport { ApiHttp } from './api/http.js';\nimport { ShipError } from '@shipstatic/types';\nimport { setConfig } from './core/platform-config.js';\nimport type { ShipClientOptions } from './types.js';\n\n// Resource imports\nimport { createDeploymentResource, createAliasResource, createAccountResource, createKeysResource } from './resources.js';\nimport type { DeploymentResource, AliasResource, AccountResource, KeysResource, DeployInput } from './resources.js';\nimport type { DeploymentOptions } from './types.js';\nimport type { Deployment } from '@shipstatic/types';\n\n\n\n// Re-export types from deploy types\nexport type { DeployInput, DeploymentOptions } from './types.js';\n\n\n/**\n * Ship SDK Client - Universal class-based interface for both Node.js and browser environments.\n * \n * ```\n * const ship = new Ship({ apiKey: \"your-api-key\" });\n * ```\n * \n * Automatically detects the environment and handles Node.js and browser deploys directly.\n * In Node.js environments, loads configuration from files and environment variables.\n * In browser environments, uses only the provided options.\n */\nexport class Ship {\n private http: ApiHttp;\n private environment: 'node' | 'browser';\n private readonly clientOptions: ShipClientOptions;\n private initPromise: Promise<void> | null = null;\n \n // Resource instances (initialized during creation)\n private _deployments: DeploymentResource;\n private _aliases: AliasResource;\n private _account: AccountResource;\n private _keys: KeysResource;\n\n constructor(options: ShipClientOptions = {}) {\n this.clientOptions = options;\n this.environment = getENV() as 'node' | 'browser';\n \n if (this.environment !== 'node' && this.environment !== 'browser') {\n throw ShipError.business('Unsupported execution environment.');\n }\n \n // Initialize HTTP client with constructor options for immediate use\n const config = resolveConfig(options, {});\n this.http = new ApiHttp({ ...options, ...config });\n \n // Initialize resources with lazy loading support\n const initCallback = this.getInitCallback();\n const getApi = () => this.http; // Dynamic getter for current HTTP client\n this._deployments = createDeploymentResource(getApi, this.clientOptions, initCallback);\n this._aliases = createAliasResource(getApi, initCallback);\n this._account = createAccountResource(getApi, initCallback);\n this._keys = createKeysResource(getApi, initCallback);\n }\n\n /**\n * Ensure full initialization is complete - called lazily by resources\n */\n private async ensureInitialized(): Promise<void> {\n if (!this.initPromise) {\n this.initPromise = this.initializeConfig();\n }\n return this.initPromise;\n }\n\n /**\n * Helper method to create initialization callback for resources\n */\n private getInitCallback() {\n return () => this.ensureInitialized();\n }\n\n /**\n * Initialize config from file/env and platform config from API\n */\n private async initializeConfig(): Promise<void> {\n try {\n // Load config from file/env\n const loadedConfig = await loadConfig(this.clientOptions.configFile);\n // Re-resolve and re-create the http client with the full config\n const finalConfig = resolveConfig(this.clientOptions, loadedConfig);\n this.http = new ApiHttp({ ...this.clientOptions, ...finalConfig });\n \n const platformConfig = await this.http.getConfig();\n setConfig(platformConfig);\n } catch (error) {\n // Reset initialization promise so it can be retried\n this.initPromise = null;\n throw error;\n }\n }\n\n /**\n * Ping the API server to check connectivity\n */\n async ping(): Promise<boolean> {\n // Ensure initialization before any HTTP operations\n await this.ensureInitialized();\n return this.http.ping();\n }\n\n /**\n * Deploy project (convenience shortcut to ship.deployments.create())\n */\n async deploy(input: DeployInput, options?: DeploymentOptions): Promise<Deployment> {\n return this.deployments.create(input, options);\n }\n\n /**\n * Get current account information (convenience shortcut to ship.account.get())\n */\n async whoami() {\n return this.account.get();\n }\n\n /**\n * Get deployments resource (environment-specific)\n */\n get deployments(): DeploymentResource {\n return this._deployments;\n }\n \n /**\n * Get aliases resource\n */\n get aliases(): AliasResource {\n return this._aliases;\n }\n \n /**\n * Get account resource\n */\n get account(): AccountResource {\n return this._account;\n }\n \n /**\n * Get keys resource\n */\n get keys(): KeysResource {\n return this._keys;\n }\n\n\n}\n\n// Default export (for import Ship from 'ship')\nexport default Ship;\n\n// Export all public types\nexport type { StaticFile, ShipClientOptions, ApiDeployOptions, ProgressStats } from './types.js';\nexport type { PingResponse } from '@shipstatic/types';\n\n// Export resource types\nexport type { DeploymentResource, AliasResource, AccountResource, KeysResource } from './resources.js';\n\n// Export main error class and error type enum\nexport { ShipError, ShipErrorType } from '@shipstatic/types';\n\n// Advanced/utility exports (for tests and power users; subject to change)\nexport { processFilesForNode } from './lib/node-files.js';\nexport { processFilesForBrowser } from './lib/browser-files.js';\n\n// Test utilities\n/**\n * @internal\n * Test utility to set the execution environment (e.g., 'node', 'browser').\n * This should not be used in production code.\n */\nexport { __setTestEnvironment } from './lib/env.js';\n","/**\n * @file Platform configuration management for the Ship SDK.\n * Implements fail-fast dynamic configuration with mandatory API fetch.\n */\n\nimport type { ConfigResponse } from '@shipstatic/types';\nimport { ShipError } from '@shipstatic/types';\n\n// Dynamic config - must be fetched from API before operations\nlet _config: ConfigResponse | null = null;\n\n/**\n * Set the current config (called after fetching from API)\n */\nexport function setConfig(config: ConfigResponse): void {\n _config = config;\n}\n\n/**\n * Get current config - throws if not initialized (fail-fast approach)\n * @throws {ShipError.config} If configuration hasn't been fetched from API\n */\nexport function getCurrentConfig(): ConfigResponse {\n if (_config === null) {\n throw ShipError.config(\n 'Platform configuration not initialized. The SDK must fetch configuration from the API before performing operations.'\n );\n }\n return _config;\n}\n\n/**\n * Check if config has been initialized from API\n */\nexport function isConfigInitialized(): boolean {\n return _config !== null;\n}\n\n/**\n * Reset config state (primarily for testing)\n * @internal\n */\nexport function resetConfig(): void {\n _config = null;\n}","/**\n * @file Input conversion utilities for deployment\n * Converts various input types to StaticFile[] for unified processing\n */\n\nimport type { StaticFile, DeploymentOptions, DeployInput } from '../types.js';\nimport { ShipError, DEPLOYMENT_CONFIG_FILENAME } from '@shipstatic/types';\nimport { getENV } from './env.js';\nimport { processFilesForNode } from './node-files.js';\nimport { processFilesForBrowser } from './browser-files.js';\nimport { getCurrentConfig } from '../core/platform-config.js';\nimport { calculateMD5 } from './md5.js';\n\n\n/**\n * Fail-fast file validation for both Node.js and browser environments\n * Validates immediately without collecting all files first for better performance\n * @param files - Array of files to validate (can be File[] or file metadata)\n * @param options - Validation options\n * @throws {ShipError} If validation fails\n * @internal\n */\nfunction validateFiles(files: Array<{ name: string; size: number }>, options: { skipEmptyCheck?: boolean } = {}): void {\n const config = getCurrentConfig();\n \n // Check for empty file array - fail fast\n if (!options.skipEmptyCheck && files.length === 0) {\n throw ShipError.business('No files to deploy.');\n }\n \n // Check file count limit - fail fast\n if (files.length > config.maxFilesCount) {\n throw ShipError.business(`Too many files to deploy. Maximum allowed is ${config.maxFilesCount}.`);\n }\n \n // Validate individual files and calculate total size - fail on first violation\n let totalSize = 0;\n for (const file of files) {\n // Individual file size validation - fail immediately\n if (file.size > config.maxFileSize) {\n throw ShipError.business(`File ${file.name} is too large. Maximum allowed size is ${config.maxFileSize / (1024 * 1024)}MB.`);\n }\n \n // Accumulate total size and check incrementally for early failure\n totalSize += file.size;\n if (totalSize > config.maxTotalSize) {\n throw ShipError.business(`Total deploy size is too large. Maximum allowed is ${config.maxTotalSize / (1024 * 1024)}MB.`);\n }\n }\n}\n\n/**\n * Early validation for file count and basic input\n * Used before file processing to fail fast on obvious issues\n * @param input - Input to validate\n * @param environment - Current environment (node/browser)\n * @throws {ShipError} If validation fails\n * @internal\n */\nfunction validateInputEarly(input: any, environment: string): void {\n if (environment === 'node') {\n if (!Array.isArray(input)) {\n throw ShipError.business('Invalid input type for Node.js environment. Expected string[] file paths.');\n }\n if (input.length === 0) {\n throw ShipError.business('No files to deploy.');\n }\n if (!input.every(item => typeof item === 'string')) {\n throw ShipError.business('Invalid input type for Node.js environment. Expected string[] file paths.');\n }\n } else if (environment === 'browser') {\n if (input instanceof HTMLInputElement && !input.files) {\n throw ShipError.business('No files selected in HTMLInputElement');\n }\n }\n}\n\n/**\n * Shared post-processing logic for StaticFile arrays\n * @param files - Array of StaticFile objects to process\n * @returns Processed StaticFile array\n * @internal\n */\nfunction postProcessFiles(files: StaticFile[]): StaticFile[] {\n // Validate processed files\n validateFiles(files, { skipEmptyCheck: true });\n \n // Normalize paths to forward slashes\n files.forEach(f => {\n if (f.path) f.path = f.path.replace(/\\\\/g, '/');\n });\n \n return files;\n}\n\n/**\n * Converts Node.js string[] paths to StaticFile[]\n */\nexport async function convertNodeInput(\n input: string[], \n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n // Early validation - fail fast before processing\n validateInputEarly(input, 'node');\n\n // Pass options directly to node processor - no conflicting logic here\n const staticFiles: StaticFile[] = await processFilesForNode(input, options);\n \n // Apply shared validation and post-processing\n return postProcessFiles(staticFiles);\n}\n\n/**\n * Converts browser FileList/File[]/HTMLInputElement to StaticFile[]\n */\nexport async function convertBrowserInput(\n input: FileList | File[] | HTMLInputElement,\n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n // Early validation - fail fast before processing\n validateInputEarly(input, 'browser');\n\n let fileArray: File[];\n \n if (input instanceof HTMLInputElement) {\n fileArray = Array.from(input.files!);\n } else if (\n typeof input === 'object' &&\n input !== null &&\n typeof (input as any).length === 'number' &&\n typeof (input as any).item === 'function'\n ) {\n fileArray = Array.from(input as FileList);\n } else if (Array.isArray(input)) {\n if (input.length > 0 && typeof input[0] === 'string') {\n throw ShipError.business('Invalid input type for browser environment. Expected File[], FileList, or HTMLInputElement.');\n }\n fileArray = input as File[];\n } else {\n throw ShipError.business('Invalid input type for browser environment. Expected File[], FileList, or HTMLInputElement.');\n }\n\n // Filter out empty files first\n fileArray = fileArray.filter(file => {\n if (file.size === 0) {\n console.warn(`Skipping empty file: ${file.name}`);\n return false;\n }\n return true;\n });\n\n // Early validation using shared logic - fail fast before heavy processing\n validateFiles(fileArray);\n\n // Pass options directly to browser processor - no conflicting logic here\n const staticFiles: StaticFile[] = await processFilesForBrowser(fileArray as File[], options);\n \n // Apply shared validation and post-processing\n return postProcessFiles(staticFiles);\n}\n\n/**\n * Unified input conversion function with automatic SPA detection\n * Converts any DeployInput to StaticFile[] and auto-generates ship.json for SPAs\n */\nexport async function convertDeployInput(\n input: DeployInput,\n options: DeploymentOptions = {},\n apiClient?: any\n): Promise<StaticFile[]> {\n const environment = getENV();\n \n // Early validation at the unified level - fail immediately on environment issues\n if (environment !== 'node' && environment !== 'browser') {\n throw ShipError.business('Unsupported execution environment.');\n }\n \n // Convert input to StaticFile[] based on environment\n let files: StaticFile[];\n if (environment === 'node') {\n files = await convertNodeInput(input as string[], options);\n } else {\n files = await convertBrowserInput(input as FileList | File[] | HTMLInputElement, options);\n }\n \n // Auto-detect and configure SPA projects\n if (apiClient) {\n files = await detectAndConfigureSPA(files, apiClient, options);\n }\n \n return files;\n}\n\n/**\n * Creates ship.json configuration for SPA projects\n * @private\n */\nasync function createSPAConfig(): Promise<StaticFile> {\n const config = {\n \"rewrites\": [{\n \"source\": \"/(.*)\",\n \"destination\": \"/index.html\"\n }]\n };\n \n const content = Buffer.from(JSON.stringify(config, null, 2), 'utf-8');\n const { md5 } = await calculateMD5(content);\n \n return {\n path: DEPLOYMENT_CONFIG_FILENAME,\n content,\n size: content.length,\n md5\n };\n}\n\n/**\n * Detects SPA projects and auto-generates configuration\n * @private\n */\nasync function detectAndConfigureSPA(files: StaticFile[], apiClient: any, options: DeploymentOptions): Promise<StaticFile[]> {\n // Skip if disabled or config already exists\n if (options.spaDetect === false || files.some(f => f.path === DEPLOYMENT_CONFIG_FILENAME)) {\n return files;\n }\n \n try {\n const filePaths = files.map(f => f.path);\n const isSPA = await apiClient.checkSPA(filePaths);\n \n if (isSPA) {\n const spaConfig = await createSPAConfig();\n console.log(`SPA detected - generated ${DEPLOYMENT_CONFIG_FILENAME}`);\n return [...files, spaConfig];\n }\n } catch (error) {\n console.warn('SPA detection failed, continuing without auto-config');\n }\n \n return files;\n}","/**\n * @file Simplified MD5 calculation utility with separate environment handlers.\n */\nimport { getENV } from './env';\nimport { ShipError } from '@shipstatic/types';\n\nexport interface MD5Result {\n md5: string;\n}\n\n/**\n * Browser-specific MD5 calculation for Blob/File objects\n */\nasync function calculateMD5Browser(blob: Blob): Promise<MD5Result> {\n const SparkMD5 = (await import('spark-md5')).default;\n \n return new Promise((resolve, reject) => {\n const chunkSize = 2097152; // 2MB chunks\n const chunks = Math.ceil(blob.size / chunkSize);\n let currentChunk = 0;\n const spark = new SparkMD5.ArrayBuffer();\n const fileReader = new FileReader();\n\n const loadNext = () => {\n const start = currentChunk * chunkSize;\n const end = Math.min(start + chunkSize, blob.size);\n fileReader.readAsArrayBuffer(blob.slice(start, end));\n };\n\n fileReader.onload = (e) => {\n const result = e.target?.result as ArrayBuffer;\n if (!result) {\n reject(ShipError.business('Failed to read file chunk'));\n return;\n }\n \n spark.append(result);\n currentChunk++;\n \n if (currentChunk < chunks) {\n loadNext();\n } else {\n resolve({ md5: spark.end() });\n }\n };\n\n fileReader.onerror = () => {\n reject(ShipError.business('Failed to calculate MD5: FileReader error'));\n };\n\n loadNext();\n });\n}\n\n/**\n * Node.js-specific MD5 calculation for Buffer or file path\n */\nasync function calculateMD5Node(input: Buffer | string): Promise<MD5Result> {\n const crypto = await import('crypto');\n \n if (Buffer.isBuffer(input)) {\n const hash = crypto.createHash('md5');\n hash.update(input);\n return { md5: hash.digest('hex') };\n }\n \n // Handle file path\n const fs = await import('fs');\n return new Promise((resolve, reject) => {\n const hash = crypto.createHash('md5');\n const stream = fs.createReadStream(input);\n \n stream.on('error', err => \n reject(ShipError.business(`Failed to read file for MD5: ${err.message}`))\n );\n stream.on('data', chunk => hash.update(chunk));\n stream.on('end', () => resolve({ md5: hash.digest('hex') }));\n });\n}\n\n/**\n * Unified MD5 calculation that delegates to environment-specific handlers\n */\nexport async function calculateMD5(input: Blob | Buffer | string): Promise<MD5Result> {\n const env = getENV();\n \n if (env === 'browser') {\n if (!(input instanceof Blob)) {\n throw ShipError.business('Invalid input for browser MD5 calculation: Expected Blob or File.');\n }\n return calculateMD5Browser(input);\n }\n \n if (env === 'node') {\n if (!(Buffer.isBuffer(input) || typeof input === 'string')) {\n throw ShipError.business('Invalid input for Node.js MD5 calculation: Expected Buffer or file path string.');\n }\n return calculateMD5Node(input);\n }\n \n throw ShipError.business('Unknown or unsupported execution environment for MD5 calculation.');\n}\n","/**\n * @file Utility for filtering out junk files and directories from file paths\n * \n * This module provides functionality to filter out common system junk files and directories\n * from a list of file paths. It uses the 'junk' package to identify junk filenames and\n * a custom list to filter out common junk directories.\n */\nimport { isJunk } from 'junk';\n\n/**\n * List of directory names considered as junk\n * \n * Files within these directories (at any level in the path hierarchy) will be excluded.\n * The comparison is case-insensitive for cross-platform compatibility.\n * \n * @internal\n */\nexport const JUNK_DIRECTORIES = [\n '__MACOSX',\n '.Trashes',\n '.fseventsd',\n '.Spotlight-V100',\n] as const;\n\n/**\n * Filters an array of file paths, removing those considered junk\n * \n * A path is filtered out if either:\n * 1. The basename is identified as junk by the 'junk' package (e.g., .DS_Store, Thumbs.db)\n * 2. Any directory segment in the path matches an entry in JUNK_DIRECTORIES (case-insensitive)\n *\n * All path separators are normalized to forward slashes for consistent cross-platform behavior.\n * \n * @param filePaths - An array of file path strings to filter\n * @returns A new array containing only non-junk file paths\n */\nexport function filterJunk(filePaths: string[]): string[] {\n if (!filePaths || filePaths.length === 0) {\n return [];\n }\n\n return filePaths.filter(filePath => {\n if (!filePath) {\n return false; // Exclude null or undefined paths\n }\n\n // Normalize path separators to forward slashes and split into segments\n const parts = filePath.replace(/\\\\/g, '/').split('/').filter(Boolean);\n if (parts.length === 0) return true;\n \n // Check if the basename is a junk file (using junk package)\n const basename = parts[parts.length - 1];\n if (isJunk(basename)) {\n return false;\n }\n\n // Check if any directory segment is in our junk directories list\n const directorySegments = parts.slice(0, -1);\n for (const segment of directorySegments) {\n if (JUNK_DIRECTORIES.some(junkDir => \n segment.toLowerCase() === junkDir.toLowerCase())) {\n return false;\n }\n }\n\n return true;\n });\n}\n","/**\n * @file Node.js-specific file utilities for the Ship SDK.\n * Provides helpers for recursively discovering, filtering, and preparing files for deploy in Node.js.\n */\nimport { getENV } from './env.js';\nimport { StaticFile, DeploymentOptions } from '../types.js';\nimport { calculateMD5 } from './md5.js';\nimport { filterJunk } from './junk.js';\nimport { ShipError } from '@shipstatic/types';\nimport { getCurrentConfig } from '../core/platform-config.js';\nimport { findCommonParent } from './path.js';\n\nimport * as fs from 'fs';\nimport * as path from 'path';\n\n\n/**\n * Simple recursive function to walk directory and return all file paths.\n * More declarative and focused than the previous implementation.\n * @param dirPath - Directory path to traverse\n * @returns Array of absolute file paths in the directory\n */\nfunction findAllFilePaths(dirPath: string): string[] {\n const results: string[] = [];\n \n try {\n const entries = fs.readdirSync(dirPath);\n \n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry);\n const stats = fs.statSync(fullPath);\n \n if (stats.isDirectory()) {\n const subFiles = findAllFilePaths(fullPath);\n results.push(...subFiles);\n } else if (stats.isFile()) {\n results.push(fullPath);\n }\n }\n } catch (error) {\n console.error(`Error reading directory ${dirPath}:`, error);\n }\n \n return results;\n}\n\n/**\n * Clean, declarative function to get files from a source path.\n * Follows the suggested architectural pattern from the feedback.\n * @param sourcePath - File or directory path to process\n * @param options - Options for processing (basePath, preserveDirs)\n * @returns Promise resolving to array of StaticFile objects\n */\nexport async function getFilesFromPath(\n sourcePath: string, \n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n const absolutePath = path.resolve(sourcePath);\n \n // 1. Discover all files\n const allPaths = (() => {\n const stats = fs.statSync(absolutePath);\n if (stats.isFile()) {\n return [absolutePath];\n } else if (stats.isDirectory()) {\n return findAllFilePaths(absolutePath);\n } else {\n return [];\n }\n })();\n \n // 2. Filter out junk\n const validPaths = allPaths.filter(p => {\n const basename = path.basename(p);\n return filterJunk([basename]).length > 0; // Keep files that pass junk filter\n });\n \n // 3. Determine base for relative paths with automatic path optimization\n const stats = fs.statSync(absolutePath);\n let commonParent: string;\n \n // Default to path detection (flattening) unless explicitly disabled\n if (options.pathDetect === false) {\n // Path detection disabled: preserve directory structure\n commonParent = stats.isDirectory() ? absolutePath : path.dirname(absolutePath);\n } else {\n // Path detection enabled: optimize by finding common parent\n const fileDirs = validPaths.map(filePath => path.dirname(filePath));\n commonParent = findCommonParent(fileDirs);\n }\n \n // 4. Process into StaticFile objects\n const results: StaticFile[] = [];\n let totalSize = 0;\n \n for (const filePath of validPaths) {\n try {\n // Validate file\n const stats = fs.statSync(filePath);\n \n if (stats.size === 0) {\n console.warn(`Skipping empty file: ${filePath}`);\n continue;\n }\n \n const platformLimits = getCurrentConfig();\n if (stats.size > platformLimits.maxFileSize) {\n throw ShipError.business(`File ${filePath} is too large. Maximum allowed size is ${platformLimits.maxFileSize / (1024 * 1024)}MB.`);\n }\n \n totalSize += stats.size;\n if (totalSize > platformLimits.maxTotalSize) {\n throw ShipError.business(`Total deploy size is too large. Maximum allowed is ${platformLimits.maxTotalSize / (1024 * 1024)}MB.`);\n }\n \n // Read content and calculate metadata\n const content = fs.readFileSync(filePath);\n const { md5 } = await calculateMD5(content);\n let relativePath = path.relative(commonParent, filePath).replace(/\\\\/g, '/');\n \n // Security validation: Ensure no dangerous characters in paths\n if (relativePath.includes('\\0') || relativePath.includes('/../') || relativePath.startsWith('../') || relativePath.endsWith('/..')) {\n // Only flatten if there are actual dangerous path traversals\n // Allow legitimate .. that might appear in the middle of a path component (like in filenames)\n relativePath = path.basename(filePath);\n }\n \n // Ensure path is not empty (this can happen if file equals commonParent)\n if (!relativePath) {\n relativePath = path.basename(filePath);\n }\n \n results.push({\n path: relativePath,\n content: content,\n size: content.length,\n md5,\n });\n } catch (error) {\n if (error instanceof ShipError && error.isClientError && error.isClientError()) {\n throw error;\n }\n console.error(`Could not process file ${filePath}:`, error);\n }\n }\n \n // Validate total file count\n const platformLimits = getCurrentConfig();\n if (results.length > platformLimits.maxFilesCount) {\n throw ShipError.business(`Too many files to deploy. Maximum allowed is ${platformLimits.maxFilesCount} files.`);\n }\n \n return results;\n}\n\n\n/**\n * Processes Node.js file and directory paths into an array of StaticFile objects ready for deploy.\n * Now uses the simplified, declarative approach suggested in the feedback.\n * \n * @param paths - File or directory paths to scan and process.\n * @param options - Processing options (basePath, preserveDirs).\n * @returns Promise resolving to an array of StaticFile objects.\n * @throws {ShipClientError} If called outside Node.js or if fs/path modules fail.\n */\nexport async function processFilesForNode(\n paths: string[],\n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n if (getENV() !== 'node') {\n throw ShipError.business('processFilesForNode can only be called in a Node.js environment.');\n }\n\n // Handle multiple paths\n if (paths.length > 1) {\n const allResults: StaticFile[] = [];\n for (const singlePath of paths) {\n const results = await getFilesFromPath(singlePath, options);\n allResults.push(...results);\n }\n return allResults;\n }\n\n // Single path - use the getFilesFromPath function\n return await getFilesFromPath(paths[0], options);\n}\n","/**\n * @file Path helper utilities that work in both browser and Node.js environments.\n * Provides environment-agnostic path manipulation functions.\n */\n\n/**\n * Finds the common parent directory from an array of directory paths.\n * Simple, unified implementation for flattenDirs functionality.\n * \n * @param dirPaths - Array of directory paths (not file paths - directories containing the files)\n * @returns The common parent directory path, or empty string if none found\n */\nexport function findCommonParent(dirPaths: string[]): string {\n if (!dirPaths || dirPaths.length === 0) return '';\n \n // Normalize all paths to use forward slashes\n const normalizedPaths = dirPaths\n .filter(p => p && typeof p === 'string')\n .map(p => p.replace(/\\\\/g, '/'));\n \n if (normalizedPaths.length === 0) return '';\n if (normalizedPaths.length === 1) return normalizedPaths[0];\n \n // Split into segments and find common prefix\n const pathSegments = normalizedPaths.map(p => p.split('/').filter(Boolean));\n const commonSegments = [];\n const minLength = Math.min(...pathSegments.map(p => p.length));\n \n for (let i = 0; i < minLength; i++) {\n const segment = pathSegments[0][i];\n if (pathSegments.every(segments => segments[i] === segment)) {\n commonSegments.push(segment);\n } else {\n break;\n }\n }\n \n return commonSegments.join('/');\n}\n\n\n\n/**\n * Converts backslashes to forward slashes for cross-platform compatibility.\n * Does not remove leading slashes (preserves absolute paths).\n * @param path - The path to normalize\n * @returns Path with forward slashes\n */\nexport function normalizeSlashes(path: string): string {\n return path.replace(/\\\\/g, '/');\n}\n\n/**\n * Normalizes a path for web usage by converting backslashes to forward slashes\n * and removing leading slashes.\n * @param path - The path to normalize\n * @returns Normalized path suitable for web deployment\n */\nexport function normalizeWebPath(path: string): string {\n return path.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n}\n\n","/**\n * @file Browser-specific file utilities for the Ship SDK.\n * Provides helpers for processing browser files into deploy-ready objects and extracting common directory info.\n */\nimport { getENV } from './env.js';\nimport { StaticFile, DeploymentOptions } from '../types.js';\nimport { calculateMD5 } from './md5.js';\nimport { ShipError } from '@shipstatic/types';\nimport { findCommonParent, normalizeWebPath } from './path.js';\nimport { filterJunk } from './junk.js';\n\n\n/**\n * Internal structure representing a browser file to be processed for deploy.\n * @internal\n */\ninterface BrowserFileProcessItem {\n file: File;\n relativePath: string;\n}\n\n/**\n * Processes browser files (FileList or File[]) into an array of StaticFile objects ready for deploy.\n * Calculates MD5, filters junk files, and applies automatic path optimization.\n *\n * @param browserFiles - FileList or File[] to process for deploy.\n * @param options - Processing options including pathDetect for automatic path optimization.\n * @returns Promise resolving to an array of StaticFile objects.\n * @throws {ShipClientError} If called outside a browser or with invalid input.\n */\nexport async function processFilesForBrowser(\n browserFiles: FileList | File[],\n options: DeploymentOptions = {}\n): Promise<StaticFile[]> {\n if (getENV() !== 'browser') {\n throw ShipError.business('processFilesForBrowser can only be called in a browser environment.');\n }\n\n const filesArray = Array.isArray(browserFiles) ? browserFiles : Array.from(browserFiles);\n \n // Determine common parent for path optimization - enabled by default\n let commonParent = '';\n if (options.pathDetect !== false) {\n // Path detection enabled: optimize by finding common parent\n const fileDirs = filesArray\n .map(file => (file as any).webkitRelativePath || file.name)\n .filter(path => path)\n .map(filePath => filePath.includes('/') ? filePath.substring(0, filePath.lastIndexOf('/')) : '');\n \n commonParent = findCommonParent(fileDirs);\n }\n\n // Prepare file information with automatic path optimization\n const initialFileInfos: BrowserFileProcessItem[] = [];\n for (const file of filesArray) {\n let relativePath = (file as any).webkitRelativePath || file.name;\n \n // Apply path optimization if enabled\n if (commonParent && options.pathDetect !== false) {\n relativePath = normalizeWebPath(relativePath);\n const basePathWithSlash = commonParent.endsWith('/') ? commonParent : `${commonParent}/`;\n if (relativePath.startsWith(basePathWithSlash)) {\n relativePath = relativePath.substring(basePathWithSlash.length);\n } else if (relativePath === commonParent) {\n relativePath = '';\n }\n }\n \n // Always normalize to web paths (forward slashes, no leading slash)\n relativePath = normalizeWebPath(relativePath);\n \n // Security validation: Ensure no dangerous characters in paths\n if (relativePath.includes('..') || relativePath.includes('\\0')) {\n throw ShipError.business(`Security error: Unsafe file path \"${relativePath}\" for file: ${file.name}`);\n }\n \n // Ensure path is not empty\n if (!relativePath) {\n relativePath = file.name;\n }\n \n initialFileInfos.push({ file, relativePath });\n }\n\n // Filter out junk files\n const allRelativePaths = initialFileInfos.map(info => info.relativePath);\n const nonJunkRelativePathsArray = filterJunk(allRelativePaths);\n const nonJunkRelativePathsSet = new Set(nonJunkRelativePathsArray);\n\n // Create StaticFile objects for each valid file\n const result: StaticFile[] = [];\n for (const fileInfo of initialFileInfos) {\n // Skip junk files and empty files\n if (!nonJunkRelativePathsSet.has(fileInfo.relativePath) || fileInfo.file.size === 0) {\n continue;\n }\n \n // Calculate MD5 hash\n const { md5 } = await calculateMD5(fileInfo.file);\n \n // Create and add the StaticFile\n result.push({\n content: fileInfo.file,\n path: fileInfo.relativePath,\n size: fileInfo.file.size,\n md5,\n });\n }\n \n return result;\n}\n\n","/**\n * @file All Ship SDK resources in one place - impossibly simple.\n */\nimport type { Deployment, DeploymentListResponse, Alias, AliasListResponse, Account } from '@shipstatic/types';\nimport type { ApiHttp } from './api/http.js';\nimport type { StaticFile, ShipClientOptions } from './types.js';\nimport type { DeploymentOptions, DeployInput } from './types.js';\nimport { convertDeployInput } from './lib/prepare-input.js';\nimport { mergeDeployOptions } from './core/config.js';\n\n// Re-export DeployInput for external use\nexport type { DeployInput };\n\n// =============================================================================\n// DEPLOYMENT RESOURCE\n// =============================================================================\n\nexport interface DeploymentResource {\n create: (input: DeployInput, options?: DeploymentOptions) => Promise<Deployment>;\n list: () => Promise<DeploymentListResponse>;\n remove: (id: string) => Promise<void>;\n get: (id: string) => Promise<Deployment>;\n}\n\nexport function createDeploymentResource(\n getApi: () => ApiHttp, \n clientDefaults?: ShipClientOptions,\n ensureInit?: () => Promise<void>\n): DeploymentResource {\n return {\n create: async (input: DeployInput, options: DeploymentOptions = {}) => {\n // Ensure full initialization before proceeding\n if (ensureInit) await ensureInit();\n \n // Merge user options with client defaults\n const mergedOptions = clientDefaults \n ? mergeDeployOptions(options, clientDefaults)\n : options;\n \n // Convert input to StaticFile[] with automatic SPA detection\n const staticFiles: StaticFile[] = await convertDeployInput(input, mergedOptions, getApi());\n \n // Deploy using the API - now returns the full Deployment object directly\n return await getApi().deploy(staticFiles, mergedOptions);\n },\n\n list: async () => {\n if (ensureInit) await ensureInit();\n return getApi().listDeployments();\n },\n\n remove: async (id: string) => {\n if (ensureInit) await ensureInit();\n await getApi().removeDeployment(id);\n // Return void for deletion operations\n },\n\n get: async (id: string) => {\n if (ensureInit) await ensureInit();\n return getApi().getDeployment(id);\n }\n };\n}\n\n// =============================================================================\n// ALIAS RESOURCE\n// =============================================================================\n\nexport interface AliasResource {\n set: (aliasName: string, deployment: string) => Promise<Alias>;\n get: (aliasName: string) => Promise<Alias>;\n list: () => Promise<AliasListResponse>;\n remove: (aliasName: string) => Promise<void>;\n check: (aliasName: string) => Promise<{ message: string }>;\n}\n\nexport function createAliasResource(getApi: () => ApiHttp, ensureInit?: () => Promise<void>): AliasResource {\n return {\n set: async (aliasName: string, deployment: string) => {\n if (ensureInit) await ensureInit();\n // Set alias and return the created/updated alias directly\n return getApi().setAlias(aliasName, deployment);\n },\n\n get: async (aliasName: string) => {\n if (ensureInit) await ensureInit();\n return getApi().getAlias(aliasName);\n },\n\n list: async () => {\n if (ensureInit) await ensureInit();\n return getApi().listAliases();\n },\n\n remove: async (aliasName: string) => {\n if (ensureInit) await ensureInit();\n await getApi().removeAlias(aliasName);\n // Return void for deletion operations\n },\n\n check: async (aliasName: string) => {\n if (ensureInit) await ensureInit();\n return getApi().checkAlias(aliasName);\n }\n };\n}\n\n// =============================================================================\n// ACCOUNT RESOURCE\n// =============================================================================\n\nexport interface AccountResource {\n get: () => Promise<Account>;\n}\n\nexport function createAccountResource(getApi: () => ApiHttp, ensureInit?: () => Promise<void>): AccountResource {\n return {\n get: async () => {\n if (ensureInit) await ensureInit();\n return getApi().getAccount();\n }\n };\n}\n\n// =============================================================================\n// KEYS RESOURCE\n// =============================================================================\n\nexport interface KeysResource {\n create: () => Promise<{ apiKey: string }>;\n}\n\nexport function createKeysResource(getApi: () => ApiHttp, ensureInit?: () => Promise<void>): KeysResource {\n return {\n create: async () => {\n if (ensureInit) await ensureInit();\n return getApi().createApiKey();\n }\n };\n}"],"mappings":"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","names":["_testEnvironment","__setTestEnvironment","env","detectEnvironment","getENV","z","types_exports","__reExport","types_star","DEFAULT_API","MODULE_NAME","ConfigSchema","z","validateConfig","config","error","firstError","path","loadConfigFromFile","configFile","getENV","cosmiconfigSync","os","explorer","result","loadConfig","envConfig","fileConfig","mergedConfig","resolveConfig","userOptions","loadedConfig","finalConfig","DEFAULT_API","mergeDeployOptions","clientDefaults","_mime","ShipError","DEPLOY_ENDPOINT","PING_ENDPOINT","ALIASES_ENDPOINT","CONFIG_ENDPOINT","ACCOUNT_ENDPOINT","SPA_CHECK_ENDPOINT","getBrowserContentType","file","collectAsyncIterable","iterable","result","x","ApiHttp","options","DEFAULT_API","#getAuthHeaders","customHeaders","headers","#fetchWithAuth","url","operationName","fetchOptions","getENV","error","#handleFetchError","#request","response","#handleResponseError","ShipError","files","#validateFiles","apiUrl","signal","requestBody","requestHeaders","#prepareRequestPayload","#createBrowserBody","body","#createNodeBody","formData","checksums","i","fileContent","contentType","fileWithPath","FormDataNodeClass","FileNodeClass","FormDataEncoderClass","pathImport","formDataNodeInstance","fileNodeInstance","preservedPath","encoder","encodedChunks","chunk","errorData","message","id","name","deployment","filePaths","ShipError","ShipError","_config","setConfig","config","getCurrentConfig","ShipError","DEPLOYMENT_CONFIG_FILENAME","ShipError","calculateMD5Browser","blob","SparkMD5","resolve","reject","chunks","currentChunk","spark","fileReader","loadNext","start","end","e","result","calculateMD5Node","input","crypto","hash","fs","stream","err","chunk","calculateMD5","env","getENV","isJunk","JUNK_DIRECTORIES","filterJunk","filePaths","filePath","parts","basename","directorySegments","segment","junkDir","ShipError","findCommonParent","dirPaths","normalizedPaths","p","pathSegments","commonSegments","minLength","i","segment","segments","normalizeWebPath","path","fs","path","findAllFilePaths","dirPath","results","entries","entry","fullPath","stats","subFiles","error","getFilesFromPath","sourcePath","options","absolutePath","validPaths","p","basename","filterJunk","commonParent","fileDirs","filePath","findCommonParent","totalSize","platformLimits","getCurrentConfig","ShipError","content","md5","calculateMD5","relativePath","processFilesForNode","paths","getENV","allResults","singlePath","ShipError","processFilesForBrowser","browserFiles","options","getENV","ShipError","filesArray","commonParent","fileDirs","file","path","filePath","findCommonParent","initialFileInfos","relativePath","normalizeWebPath","basePathWithSlash","allRelativePaths","info","nonJunkRelativePathsArray","filterJunk","nonJunkRelativePathsSet","result","fileInfo","md5","calculateMD5","validateFiles","files","options","config","getCurrentConfig","ShipError","totalSize","file","validateInputEarly","input","environment","item","postProcessFiles","f","convertNodeInput","staticFiles","processFilesForNode","convertBrowserInput","fileArray","processFilesForBrowser","convertDeployInput","apiClient","getENV","detectAndConfigureSPA","createSPAConfig","content","md5","calculateMD5","DEPLOYMENT_CONFIG_FILENAME","filePaths","spaConfig","createDeploymentResource","getApi","clientDefaults","ensureInit","input","options","mergedOptions","mergeDeployOptions","staticFiles","convertDeployInput","id","createAliasResource","aliasName","deployment","createAccountResource","createKeysResource","ShipError","ShipErrorType","Ship","options","getENV","config","resolveConfig","ApiHttp","initCallback","getApi","createDeploymentResource","createAliasResource","createAccountResource","createKeysResource","loadedConfig","loadConfig","finalConfig","platformConfig","setConfig","error","input","src_default"]}
|